This commit is contained in:
louiscklaw
2025-05-30 01:14:10 +08:00
parent 98bc3fe3ce
commit 834f58bde1
52 changed files with 624 additions and 604 deletions

View File

@@ -7,15 +7,18 @@ import Grid from '@mui/material/Grid';
import Stack from '@mui/material/Stack';
import Switch from '@mui/material/Switch';
import Typography from '@mui/material/Typography';
import { useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { isValidPhoneNumber } from 'react-phone-number-input/input';
import { createUser, deleteUser, saveUser } from 'src/actions/user';
import { Field, Form, schemaHelper } from 'src/components/hook-form';
import { Label } from 'src/components/label';
import { toast } from 'src/components/snackbar';
import { useRouter } from 'src/routes/hooks';
import { paths } from 'src/routes/paths';
import type { IUserItem } from 'src/types/user';
import { fileToBase64 } from 'src/utils/file-to-base64';
import { fData } from 'src/utils/format-number';
import { z as zod } from 'zod';
@@ -24,26 +27,27 @@ import { z as zod } from 'zod';
export type NewUserSchemaType = zod.infer<typeof NewUserSchema>;
export const NewUserSchema = zod.object({
avatarUrl: schemaHelper.file({ message: 'Avatar is required!' }),
name: zod.string().min(1, { message: 'Name is required!' }),
city: zod.string().min(1, { message: 'City is required!' }),
role: zod.string().min(1, { message: 'Role is required!' }),
email: zod
.string()
.min(1, { message: 'Email is required!' })
.email({ message: 'Email must be a valid email address!' }),
phoneNumber: schemaHelper.phoneNumber({ isValid: isValidPhoneNumber }),
state: zod.string().min(1, { message: 'State is required!' }),
status: zod.string(),
address: zod.string().min(1, { message: 'Address is required!' }),
country: schemaHelper.nullableInput(zod.string().min(1, { message: 'Country is required!' }), {
// message for null value
message: 'Country is required!',
}),
address: zod.string().min(1, { message: 'Address is required!' }),
company: zod.string().min(1, { message: 'Company is required!' }),
state: zod.string().min(1, { message: 'State is required!' }),
city: zod.string().min(1, { message: 'City is required!' }),
role: zod.string().min(1, { message: 'Role is required!' }),
zipCode: zod.string().min(1, { message: 'Zip code is required!' }),
// Not required
status: zod.string(),
company: zod.string().min(1, { message: 'Company is required!' }),
avatarUrl: schemaHelper.file({ message: 'Avatar is required!' }),
phoneNumber: schemaHelper.phoneNumber({ isValid: isValidPhoneNumber }),
isVerified: zod.boolean(),
username: zod.string(),
password: zod.string(),
});
// ----------------------------------------------------------------------
@@ -61,16 +65,19 @@ export function UserNewEditForm({ currentUser }: Props) {
status: '',
avatarUrl: null,
isVerified: true,
name: '',
email: '',
phoneNumber: '',
country: '',
state: '',
city: '',
address: '',
zipCode: '',
company: '',
role: '',
name: '新用戶名字',
email: 'user@123.com',
phoneNumber: '+85291234567',
country: 'Hong Kong',
state: 'HK',
city: 'hong kong',
address: 'Kwun Tong, Sau Mau Ping',
zipCode: '00000',
company: 'test company',
role: 'user',
//
username: '',
password: '',
};
const methods = useForm<NewUserSchemaType>({
@@ -85,18 +92,50 @@ export function UserNewEditForm({ currentUser }: Props) {
watch,
control,
handleSubmit,
formState: { isSubmitting },
formState: { errors, isSubmitting },
} = methods;
const values = watch();
const onSubmit = handleSubmit(async (data) => {
const [disableDeleteUserButton, setDisableDeleteUserButton] = useState<boolean>(false);
const handleDeleteUserClick = async () => {
setDisableDeleteUserButton(true);
try {
if (currentUser) {
await deleteUser(currentUser.id);
toast.success(t('user deleted'));
router.push(paths.dashboard.user.list);
}
} catch (error) {
console.error(error);
}
setDisableDeleteUserButton(false);
};
const onSubmit = handleSubmit(async (data: any) => {
try {
await new Promise((resolve) => setTimeout(resolve, 500));
reset();
toast.success(currentUser ? 'Update success!' : 'Create success!');
const temp: any = data.avatarUrl;
if (temp instanceof File) {
data.avatarUrl = await fileToBase64(temp);
}
if (currentUser) {
// perform save
await saveUser(currentUser.id, data);
} else {
// perform create
await createUser(data);
}
toast.success(currentUser ? t('Update success!') : t('Create success!'));
router.push(paths.dashboard.user.list);
console.info('DATA', data);
// console.info('DATA', data);
} catch (error) {
console.error(error);
}
@@ -135,8 +174,8 @@ export function UserNewEditForm({ currentUser }: Props) {
color: 'text.disabled',
}}
>
Allowed *.jpeg, *.jpg, *.png, *.gif
<br /> max size of {fData(3145728)}
{t('Allowed')} *.jpeg, *.jpg, *.png, *.gif
<br /> {t('max size of')} {fData(3145728)}
</Typography>
}
/>
@@ -163,10 +202,10 @@ export function UserNewEditForm({ currentUser }: Props) {
label={
<>
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
Banned
{t('Banned')}
</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
Apply disable account
{t('Apply disable account')}
</Typography>
</>
}
@@ -185,10 +224,10 @@ export function UserNewEditForm({ currentUser }: Props) {
label={
<>
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
Email verified
{t('Email verified')}
</Typography>
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
Disabling this will automatically send the user a verification email
{t('Disabling this will automatically send the user a verification email')}
</Typography>
</>
}
@@ -197,8 +236,14 @@ export function UserNewEditForm({ currentUser }: Props) {
{currentUser && (
<Stack sx={{ mt: 3, alignItems: 'center', justifyContent: 'center' }}>
<Button variant="soft" color="error">
Delete user
<Button
disabled={disableDeleteUserButton}
loading={disableDeleteUserButton}
variant="soft"
color="error"
onClick={handleDeleteUserClick}
>
{t('Delete user')}
</Button>
</Stack>
)}
@@ -215,32 +260,33 @@ export function UserNewEditForm({ currentUser }: Props) {
gridTemplateColumns: { xs: 'repeat(1, 1fr)', sm: 'repeat(2, 1fr)' },
}}
>
<Field.Text name="name" label="Full name" />
<Field.Text name="email" label="Email address" />
<Field.Phone
name="phoneNumber"
label="Phone number"
country={!currentUser ? 'DE' : undefined}
/>
<Field.Text name="name" label={t('Full name')} />
<Field.Text name="email" label={t('Email address')} />
<Field.Phone name="phoneNumber" label={t('Phone number')} country="HK" />
<Field.CountrySelect
fullWidth
name="country"
label="Country"
placeholder="Choose a country"
label={t('Country')}
placeholder={t('Choose a country')}
/>
<Field.Text name="state" label="State/region" />
<Field.Text name="city" label="City" />
<Field.Text name="state" label={t('State/region')} />
<Field.Text name="city" label={t('City')} />
<Field.Text name="address" label={t('Address')} />
<Field.Text name="zipCode" label="Zip/code" />
<Field.Text name="company" label="Company" />
<Field.Text name="role" label="Role" />
<Field.Text name="zipCode" label={t('Zip/code')} />
<Field.Text name="company" label={t('Company')} />
<Field.Text name="role" label={t('Role')} />
</Box>
<Stack sx={{ mt: 3, alignItems: 'flex-end' }}>
<Button type="submit" variant="contained" loading={isSubmitting}>
{!currentUser ? 'Create user' : 'Save changes'}
<Button
disabled={isSubmitting}
loading={isSubmitting}
type="submit"
variant="contained"
>
{!currentUser ? t('Create user') : t('Save changes')}
</Button>
</Stack>
</Card>