81
src/components/Manage/User/Edit/GeneralForm.tsx
Normal file
81
src/components/Manage/User/Edit/GeneralForm.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Box, Button, Group, TextInput, Title } from '@mantine/core';
|
||||
import { useForm, zodResolver } from '@mantine/form';
|
||||
import { IconAt, IconCheck, IconLetterCase } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { z } from 'zod';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
export const ManageUserGeneralForm = ({
|
||||
userId,
|
||||
defaultUsername,
|
||||
defaultEmail,
|
||||
}: {
|
||||
userId: string;
|
||||
defaultUsername: string;
|
||||
defaultEmail: string;
|
||||
}) => {
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
username: defaultUsername,
|
||||
eMail: defaultEmail,
|
||||
},
|
||||
validate: zodResolver(
|
||||
z.object({
|
||||
username: z.string(),
|
||||
eMail: z.string().email().or(z.literal('')),
|
||||
})
|
||||
),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
const { t } = useTranslation(['manage/users/edit', 'common']);
|
||||
|
||||
const utils = api.useUtils();
|
||||
|
||||
const { mutate, isLoading } = api.user.updateDetails.useMutation({
|
||||
onSettled: async () => {
|
||||
await utils.user.invalidate();
|
||||
form.resetDirty();
|
||||
},
|
||||
});
|
||||
|
||||
function handleSubmit() {
|
||||
mutate({
|
||||
userId: userId,
|
||||
username: form.values.username,
|
||||
eMail: form.values.eMail,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Box maw={500}>
|
||||
<Title order={3}>{t('sections.general.title')}</Title>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<TextInput
|
||||
icon={<IconLetterCase size="1rem" />}
|
||||
label={t('sections.general.inputs.username.label')}
|
||||
mb="md"
|
||||
withAsterisk
|
||||
{...form.getInputProps('username')}
|
||||
/>
|
||||
<TextInput
|
||||
icon={<IconAt size="1rem" />}
|
||||
label={t('sections.general.inputs.eMail.label')}
|
||||
{...form.getInputProps('eMail')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button
|
||||
disabled={!form.isDirty() || !form.isValid() || isLoading}
|
||||
loading={isLoading}
|
||||
leftIcon={<IconCheck size="1rem" />}
|
||||
color="green"
|
||||
variant="light"
|
||||
type="submit"
|
||||
>
|
||||
{t('common:save')}
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
83
src/components/Manage/User/Edit/ManageUserDanger.tsx
Normal file
83
src/components/Manage/User/Edit/ManageUserDanger.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Box, Button, Checkbox, Group, LoadingOverlay, PasswordInput, Title } from '@mantine/core';
|
||||
import { useForm, zodResolver } from '@mantine/form';
|
||||
import { IconTextSize, IconTrash } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { z } from 'zod';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
export const ManageUserDanger = ({
|
||||
userId,
|
||||
username,
|
||||
}: {
|
||||
userId: string;
|
||||
username: string | null;
|
||||
}) => {
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
username: '',
|
||||
confirm: false,
|
||||
},
|
||||
validate: zodResolver(
|
||||
z.object({
|
||||
username: z.literal(username),
|
||||
confirm: z.literal(true),
|
||||
})
|
||||
),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
|
||||
const apiUtils = api.useUtils();
|
||||
|
||||
const { mutate, isLoading } = api.user.deleteUser.useMutation({
|
||||
onSuccess: () => {
|
||||
window.location.href = '/manage/users';
|
||||
},
|
||||
onSettled: () => {
|
||||
void apiUtils.user.details.invalidate();
|
||||
form.reset();
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useTranslation(['manage/users/edit', 'common']);
|
||||
|
||||
const handleSubmit = () => {
|
||||
mutate({
|
||||
id: userId,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box maw={500}>
|
||||
<LoadingOverlay visible={isLoading} />
|
||||
<Title order={3}>{t('sections.deletion.title')}</Title>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<PasswordInput
|
||||
icon={<IconTextSize size="1rem" />}
|
||||
label={t('sections.deletion.inputs.confirmUsername.label')}
|
||||
description={t('sections.deletion.inputs.confirmUsername.description')}
|
||||
mb="md"
|
||||
withAsterisk
|
||||
{...form.getInputProps('username')}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t('sections.deletion.inputs.confirm.label')}
|
||||
description={t('sections.deletion.inputs.confirm.description')}
|
||||
{...form.getInputProps('confirm')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button
|
||||
disabled={!form.isDirty() || !form.isValid()}
|
||||
leftIcon={<IconTrash size="1rem" />}
|
||||
loading={isLoading}
|
||||
color="red"
|
||||
variant="light"
|
||||
type="submit"
|
||||
>
|
||||
{t('common:delete')}
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
65
src/components/Manage/User/Edit/ManageUserRoles.tsx
Normal file
65
src/components/Manage/User/Edit/ManageUserRoles.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { ActionIcon, Badge, Box, Group, Title, Text, Tooltip, Button } from '@mantine/core';
|
||||
import { openRoleChangeModal } from '~/components/Manage/User/change-user-role.modal';
|
||||
import { IconUserDown, IconUserUp } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSession } from 'next-auth/react';
|
||||
|
||||
export const ManageUserRoles = ({ user }: {
|
||||
user: {
|
||||
image: string | null;
|
||||
id: string;
|
||||
name: string | null;
|
||||
password: string | null;
|
||||
email: string | null;
|
||||
emailVerified: Date | null;
|
||||
salt: string | null;
|
||||
isAdmin: boolean;
|
||||
isOwner: boolean;
|
||||
}
|
||||
}) => {
|
||||
const { t } = useTranslation(['manage/users/edit', 'manage/users']);
|
||||
const { data: sessionData } = useSession();
|
||||
return (
|
||||
<Box maw={500}>
|
||||
<Title order={3}>
|
||||
{t('sections.roles.title')}
|
||||
</Title>
|
||||
|
||||
<Group mb={'md'}>
|
||||
<Text>{t('sections.roles.currentRole')}</Text>
|
||||
{user.isOwner ? (<Badge>{t('sections.roles.badges.owner')}</Badge>) : user.isAdmin ? (
|
||||
<Badge>{t('sections.roles.badges.admin')}</Badge>) : (<Badge>{t('sections.roles.badges.normal')}</Badge>)}
|
||||
</Group>
|
||||
|
||||
{user.isAdmin ? (
|
||||
<Button
|
||||
leftIcon={<IconUserDown size='1rem' />}
|
||||
disabled={user.id === sessionData?.user?.id || user.isOwner}
|
||||
onClick={() => {
|
||||
openRoleChangeModal({
|
||||
name: user.name as string,
|
||||
id: user.id,
|
||||
type: 'demote',
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('manage/users:tooltips.demoteAdmin')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
leftIcon={<IconUserUp size='1rem' />}
|
||||
onClick={() => {
|
||||
openRoleChangeModal({
|
||||
name: user.name as string,
|
||||
id: user.id,
|
||||
type: 'promote',
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
{t('manage/users:tooltips.promoteToAdmin')}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
91
src/components/Manage/User/Edit/ManageUserSecurityForm.tsx
Normal file
91
src/components/Manage/User/Edit/ManageUserSecurityForm.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { Box, Button, Checkbox, Group, LoadingOverlay, PasswordInput, Title } from '@mantine/core';
|
||||
import { useForm, zodResolver } from '@mantine/form';
|
||||
import { useInputState } from '@mantine/hooks';
|
||||
import { IconAlertTriangle, IconPassword } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { z } from 'zod';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
export const ManageUserSecurityForm = ({ userId }: { userId: string }) => {
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
password: '',
|
||||
terminateExistingSessions: false,
|
||||
confirm: false,
|
||||
},
|
||||
validate: zodResolver(
|
||||
z.object({
|
||||
password: z.string().min(3),
|
||||
terminateExistingSessions: z.boolean(),
|
||||
confirm: z.literal(true),
|
||||
})
|
||||
),
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
|
||||
const [checked, setChecked] = useInputState(false);
|
||||
|
||||
const { t } = useTranslation(['manage/users/edit', 'common']);
|
||||
|
||||
const apiUtils = api.useUtils();
|
||||
|
||||
const { mutate, isLoading } = api.user.updatePassword.useMutation({
|
||||
onSettled: () => {
|
||||
void apiUtils.user.details.invalidate();
|
||||
form.reset();
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (values: { password: string; terminateExistingSessions: boolean }) => {
|
||||
mutate({
|
||||
newPassword: values.password,
|
||||
terminateExistingSessions: values.terminateExistingSessions,
|
||||
userId: userId,
|
||||
});
|
||||
setChecked(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box maw={500}>
|
||||
<LoadingOverlay visible={isLoading} />
|
||||
<Title order={3}>{t('sections.security.title')}</Title>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<PasswordInput
|
||||
icon={<IconPassword size="1rem" />}
|
||||
label={t('sections.security.inputs.password.label')}
|
||||
mb="md"
|
||||
withAsterisk
|
||||
{...form.getInputProps('password')}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t('sections.security.inputs.terminateExistingSessions.label')}
|
||||
description={t('sections.security.inputs.terminateExistingSessions.description')}
|
||||
mb="md"
|
||||
{...form.getInputProps('terminateExistingSessions')}
|
||||
/>
|
||||
<Checkbox
|
||||
label={t('sections.security.inputs.confirm.label')}
|
||||
description={t('sections.security.inputs.confirm.description')}
|
||||
checked={checked}
|
||||
onClick={(event) => {
|
||||
setChecked(event.currentTarget.checked);
|
||||
}}
|
||||
{...form.getInputProps('confirm')}
|
||||
/>
|
||||
<Group position="right" mt="md">
|
||||
<Button
|
||||
disabled={!form.isDirty() || !form.isValid()}
|
||||
leftIcon={<IconAlertTriangle size="1rem" />}
|
||||
loading={isLoading}
|
||||
color="red"
|
||||
variant="light"
|
||||
type="submit"
|
||||
>
|
||||
{t('common:save')}
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -11,6 +11,7 @@ export const ChangeUserRoleModal = ({ id, innerProps }: ContextModalProps<InnerP
|
||||
const { isLoading, mutateAsync } = api.user.changeRole.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.user.all.invalidate();
|
||||
await utils.user.details.invalidate();
|
||||
modals.close(id);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user