mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-23 02:10:11 +01:00
add team member edit and remove confirmation
This commit is contained in:
parent
5ae04291af
commit
c7e9190c95
@ -0,0 +1,49 @@
|
|||||||
|
import { useMessages, useModified } from 'components/hooks';
|
||||||
|
import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
|
||||||
|
import TeamMemberEditForm from './TeamMemberEditForm';
|
||||||
|
|
||||||
|
export function TeamMemberEditButton({
|
||||||
|
teamId,
|
||||||
|
userId,
|
||||||
|
role,
|
||||||
|
onSave,
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
userId: string;
|
||||||
|
role: string;
|
||||||
|
onSave?: () => void;
|
||||||
|
}) {
|
||||||
|
const { formatMessage, labels, messages } = useMessages();
|
||||||
|
const { showToast } = useToasts();
|
||||||
|
const { touch } = useModified();
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
showToast({ message: formatMessage(messages.saved), variant: 'success' });
|
||||||
|
touch('teams:members');
|
||||||
|
onSave?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalTrigger>
|
||||||
|
<Button variant="quiet">
|
||||||
|
<Icon>
|
||||||
|
<Icons.Edit />
|
||||||
|
</Icon>
|
||||||
|
<Text>{formatMessage(labels.edit)}</Text>
|
||||||
|
</Button>
|
||||||
|
<Modal title={formatMessage(labels.editMember)}>
|
||||||
|
{(close: () => void) => (
|
||||||
|
<TeamMemberEditForm
|
||||||
|
teamId={teamId}
|
||||||
|
userId={userId}
|
||||||
|
role={role}
|
||||||
|
onSave={handleSave}
|
||||||
|
onClose={close}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
</ModalTrigger>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TeamMemberEditButton;
|
@ -0,0 +1,78 @@
|
|||||||
|
import { useApi, useMessages } from 'components/hooks';
|
||||||
|
import { ROLES } from 'lib/constants';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dropdown,
|
||||||
|
Form,
|
||||||
|
FormButtons,
|
||||||
|
FormInput,
|
||||||
|
FormRow,
|
||||||
|
Item,
|
||||||
|
SubmitButton,
|
||||||
|
} from 'react-basics';
|
||||||
|
|
||||||
|
export function UserAddForm({
|
||||||
|
teamId,
|
||||||
|
userId,
|
||||||
|
role,
|
||||||
|
onSave,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
userId: string;
|
||||||
|
role: string;
|
||||||
|
onSave?: () => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
}) {
|
||||||
|
const { post, useMutation } = useApi();
|
||||||
|
const { mutate, error, isPending } = useMutation({
|
||||||
|
mutationFn: (data: any) => post(`/teams/${teamId}/users/${userId}`, data),
|
||||||
|
});
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
|
||||||
|
const handleSubmit = async (data: any) => {
|
||||||
|
mutate(data, {
|
||||||
|
onSuccess: async () => {
|
||||||
|
onSave();
|
||||||
|
onClose();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderValue = (value: string) => {
|
||||||
|
if (value === ROLES.teamMember) {
|
||||||
|
return formatMessage(labels.teamMember);
|
||||||
|
}
|
||||||
|
if (value === ROLES.teamViewOnly) {
|
||||||
|
return formatMessage(labels.viewOnly);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form onSubmit={handleSubmit} error={error} values={{ role }}>
|
||||||
|
<FormRow label={formatMessage(labels.role)}>
|
||||||
|
<FormInput name="role" rules={{ required: formatMessage(labels.required) }}>
|
||||||
|
<Dropdown
|
||||||
|
renderValue={renderValue}
|
||||||
|
style={{
|
||||||
|
minWidth: '250px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Item key={ROLES.teamMember}>{formatMessage(labels.teamMember)}</Item>
|
||||||
|
<Item key={ROLES.teamViewOnly}>{formatMessage(labels.viewOnly)}</Item>
|
||||||
|
</Dropdown>
|
||||||
|
</FormInput>
|
||||||
|
</FormRow>
|
||||||
|
<FormButtons flex>
|
||||||
|
<SubmitButton variant="primary" disabled={false}>
|
||||||
|
{formatMessage(labels.save)}
|
||||||
|
</SubmitButton>
|
||||||
|
<Button disabled={isPending} onClick={onClose}>
|
||||||
|
{formatMessage(labels.cancel)}
|
||||||
|
</Button>
|
||||||
|
</FormButtons>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserAddForm;
|
@ -1,44 +1,64 @@
|
|||||||
|
import ConfirmationForm from 'components/common/ConfirmationForm';
|
||||||
import { useApi, useMessages, useModified } from 'components/hooks';
|
import { useApi, useMessages, useModified } from 'components/hooks';
|
||||||
import { Icon, Icons, LoadingButton, Text } from 'react-basics';
|
import { messages } from 'components/messages';
|
||||||
|
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
export function TeamMemberRemoveButton({
|
export function TeamMemberRemoveButton({
|
||||||
teamId,
|
teamId,
|
||||||
userId,
|
userId,
|
||||||
disabled,
|
userName,
|
||||||
onSave,
|
onSave,
|
||||||
}: {
|
}: {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
userName: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
const { del, useMutation } = useApi();
|
const { del, useMutation } = useApi();
|
||||||
const { mutate, isPending } = useMutation({
|
const { mutate, isPending, error } = useMutation({
|
||||||
mutationFn: () => del(`/teams/${teamId}/users/${userId}`),
|
mutationFn: () => del(`/teams/${teamId}/users/${userId}`),
|
||||||
});
|
});
|
||||||
const { touch } = useModified();
|
const { touch } = useModified();
|
||||||
|
|
||||||
const handleRemoveTeamMember = () => {
|
const handleConfirm = (close: () => void) => {
|
||||||
mutate(null, {
|
mutate(null, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
touch('teams:members');
|
touch('teams:members');
|
||||||
onSave?.();
|
onSave?.();
|
||||||
|
close();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingButton
|
<ModalTrigger>
|
||||||
onClick={() => handleRemoveTeamMember()}
|
<Button variant="quiet">
|
||||||
disabled={disabled}
|
<Icon>
|
||||||
isLoading={isPending}
|
<Icons.Close />
|
||||||
>
|
</Icon>
|
||||||
<Icon>
|
<Text>{formatMessage(labels.remove)}</Text>
|
||||||
<Icons.Close />
|
</Button>
|
||||||
</Icon>
|
<Modal title={formatMessage(labels.removeMember)}>
|
||||||
<Text>{formatMessage(labels.remove)}</Text>
|
{(close: () => void) => (
|
||||||
</LoadingButton>
|
<ConfirmationForm
|
||||||
|
message={
|
||||||
|
<FormattedMessage
|
||||||
|
{...messages.confirmRemove}
|
||||||
|
values={{ target: <b>{userName}</b> }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
isLoading={isPending}
|
||||||
|
error={error}
|
||||||
|
onConfirm={handleConfirm.bind(null, close)}
|
||||||
|
onClose={close}
|
||||||
|
buttonLabel={formatMessage(labels.remove)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
</ModalTrigger>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { GridColumn, GridTable, useBreakpoint } from 'react-basics';
|
|||||||
import { useMessages, useLogin } from 'components/hooks';
|
import { useMessages, useLogin } from 'components/hooks';
|
||||||
import { ROLES } from 'lib/constants';
|
import { ROLES } from 'lib/constants';
|
||||||
import TeamMemberRemoveButton from './TeamMemberRemoveButton';
|
import TeamMemberRemoveButton from './TeamMemberRemoveButton';
|
||||||
|
import TeamMemberEditButton from './TeamMemberEditButton';
|
||||||
|
|
||||||
export function TeamMembersTable({
|
export function TeamMembersTable({
|
||||||
data = [],
|
data = [],
|
||||||
@ -19,6 +20,7 @@ export function TeamMembersTable({
|
|||||||
const roles = {
|
const roles = {
|
||||||
[ROLES.teamOwner]: formatMessage(labels.teamOwner),
|
[ROLES.teamOwner]: formatMessage(labels.teamOwner),
|
||||||
[ROLES.teamMember]: formatMessage(labels.teamMember),
|
[ROLES.teamMember]: formatMessage(labels.teamMember),
|
||||||
|
[ROLES.teamViewOnly]: formatMessage(labels.viewOnly),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -35,7 +37,14 @@ export function TeamMembersTable({
|
|||||||
allowEdit &&
|
allowEdit &&
|
||||||
row?.role !== ROLES.teamOwner &&
|
row?.role !== ROLES.teamOwner &&
|
||||||
user?.id !== row?.id && (
|
user?.id !== row?.id && (
|
||||||
<TeamMemberRemoveButton teamId={teamId} userId={row?.user?.id} />
|
<>
|
||||||
|
<TeamMemberEditButton teamId={teamId} userId={row?.user?.id} role={row?.role} />
|
||||||
|
<TeamMemberRemoveButton
|
||||||
|
teamId={teamId}
|
||||||
|
userId={row?.user?.id}
|
||||||
|
userName={row?.user?.username}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -42,7 +42,7 @@ export const labels = defineMessages({
|
|||||||
owner: { id: 'label.owner', defaultMessage: 'Owner' },
|
owner: { id: 'label.owner', defaultMessage: 'Owner' },
|
||||||
teamOwner: { id: 'label.team-owner', defaultMessage: 'Team owner' },
|
teamOwner: { id: 'label.team-owner', defaultMessage: 'Team owner' },
|
||||||
teamMember: { id: 'label.team-member', defaultMessage: 'Team member' },
|
teamMember: { id: 'label.team-member', defaultMessage: 'Team member' },
|
||||||
teamGuest: { id: 'label.team-guest', defaultMessage: 'Team guest' },
|
teamViewOnly: { id: 'label.team-view-only', defaultMessage: 'Team view only' },
|
||||||
enableShareUrl: { id: 'label.enable-share-url', defaultMessage: 'Enable share URL' },
|
enableShareUrl: { id: 'label.enable-share-url', defaultMessage: 'Enable share URL' },
|
||||||
data: { id: 'label.data', defaultMessage: 'Data' },
|
data: { id: 'label.data', defaultMessage: 'Data' },
|
||||||
trackingCode: { id: 'label.tracking-code', defaultMessage: 'Tracking code' },
|
trackingCode: { id: 'label.tracking-code', defaultMessage: 'Tracking code' },
|
||||||
@ -56,6 +56,8 @@ export const labels = defineMessages({
|
|||||||
reset: { id: 'label.reset', defaultMessage: 'Reset' },
|
reset: { id: 'label.reset', defaultMessage: 'Reset' },
|
||||||
addWebsite: { id: 'label.add-website', defaultMessage: 'Add website' },
|
addWebsite: { id: 'label.add-website', defaultMessage: 'Add website' },
|
||||||
addMember: { id: 'label.add-member', defaultMessage: 'Add member' },
|
addMember: { id: 'label.add-member', defaultMessage: 'Add member' },
|
||||||
|
editMember: { id: 'label.edit-member', defaultMessage: 'Edit member' },
|
||||||
|
removeMember: { id: 'label.remove-member', defaultMessage: 'Remove member' },
|
||||||
addDescription: { id: 'label.add-description', defaultMessage: 'Add description' },
|
addDescription: { id: 'label.add-description', defaultMessage: 'Add description' },
|
||||||
changePassword: { id: 'label.change-password', defaultMessage: 'Change password' },
|
changePassword: { id: 'label.change-password', defaultMessage: 'Change password' },
|
||||||
currentPassword: { id: 'label.current-password', defaultMessage: 'Current password' },
|
currentPassword: { id: 'label.current-password', defaultMessage: 'Current password' },
|
||||||
@ -109,6 +111,7 @@ export const labels = defineMessages({
|
|||||||
allTime: { id: 'label.all-time', defaultMessage: 'All time' },
|
allTime: { id: 'label.all-time', defaultMessage: 'All time' },
|
||||||
customRange: { id: 'label.custom-range', defaultMessage: 'Custom range' },
|
customRange: { id: 'label.custom-range', defaultMessage: 'Custom range' },
|
||||||
selectWebsite: { id: 'label.select-website', defaultMessage: 'Select website' },
|
selectWebsite: { id: 'label.select-website', defaultMessage: 'Select website' },
|
||||||
|
selectRole: { id: 'label.select-role', defaultMessage: 'Select role' },
|
||||||
selectDate: { id: 'label.select-date', defaultMessage: 'Select date' },
|
selectDate: { id: 'label.select-date', defaultMessage: 'Select date' },
|
||||||
all: { id: 'label.all', defaultMessage: 'All' },
|
all: { id: 'label.all', defaultMessage: 'All' },
|
||||||
sessions: { id: 'label.sessions', defaultMessage: 'Sessions' },
|
sessions: { id: 'label.sessions', defaultMessage: 'Sessions' },
|
||||||
@ -220,6 +223,10 @@ export const messages = defineMessages({
|
|||||||
id: 'message.confirm-delete',
|
id: 'message.confirm-delete',
|
||||||
defaultMessage: 'Are you sure you want to delete {target}?',
|
defaultMessage: 'Are you sure you want to delete {target}?',
|
||||||
},
|
},
|
||||||
|
confirmRemove: {
|
||||||
|
id: 'message.confirm-remove',
|
||||||
|
defaultMessage: 'Are you sure you want to remove {target}?',
|
||||||
|
},
|
||||||
confirmLeave: {
|
confirmLeave: {
|
||||||
id: 'message.confirm-leave',
|
id: 'message.confirm-leave',
|
||||||
defaultMessage: 'Are you sure you want to leave {target}?',
|
defaultMessage: 'Are you sure you want to leave {target}?',
|
||||||
|
@ -125,7 +125,7 @@ export const ROLES = {
|
|||||||
viewOnly: 'view-only',
|
viewOnly: 'view-only',
|
||||||
teamOwner: 'team-owner',
|
teamOwner: 'team-owner',
|
||||||
teamMember: 'team-member',
|
teamMember: 'team-member',
|
||||||
teamGuest: 'team-guest',
|
teamViewOnly: 'team-view-only',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const PERMISSIONS = {
|
export const PERMISSIONS = {
|
||||||
@ -159,7 +159,7 @@ export const ROLE_PERMISSIONS = {
|
|||||||
PERMISSIONS.websiteUpdate,
|
PERMISSIONS.websiteUpdate,
|
||||||
PERMISSIONS.websiteDelete,
|
PERMISSIONS.websiteDelete,
|
||||||
],
|
],
|
||||||
[ROLES.teamGuest]: [],
|
[ROLES.teamViewOnly]: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const THEME_COLORS = {
|
export const THEME_COLORS = {
|
||||||
|
@ -23,7 +23,7 @@ const schema = {
|
|||||||
POST: yup.object().shape({
|
POST: yup.object().shape({
|
||||||
role: yup
|
role: yup
|
||||||
.string()
|
.string()
|
||||||
.matches(/team-member|team-guest/i)
|
.matches(/team-member|team-view-only/i)
|
||||||
.required(),
|
.required(),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user