update customers,

This commit is contained in:
louiscklaw
2025-04-23 02:02:47 +08:00
parent c8d184465a
commit 41cc82d54d
6 changed files with 215 additions and 62 deletions

View File

@@ -96,8 +96,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
React.useEffect(() => { React.useEffect(() => {
if (!isFirstRun.current) { if (!isFirstRun.current) {
isFirstRun.current = true; isFirstRun.current = true;
} else { } else if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
if (JSON.stringify(listOption) !== JSON.stringify(lastListOption)) {
// reset page number as tab changes // reset page number as tab changes
setLastListOption(listOption); setLastListOption(listOption);
setCurrentPage(0); setCurrentPage(0);
@@ -105,7 +104,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
} else { } else {
void reloadRows(); void reloadRows();
} }
}
}, [currentPage, rowsPerPage, listOption]); }, [currentPage, rowsPerPage, listOption]);
React.useEffect(() => { React.useEffect(() => {

View File

@@ -16,6 +16,7 @@ import { useTranslation } from 'react-i18next';
import { logger } from '@/lib/default-logger'; import { logger } from '@/lib/default-logger';
import { toast } from '@/components/core/toaster'; import { toast } from '@/components/core/toaster';
import { deleteCustomer } from '@/db/Customers/Delete';
export default function ConfirmDeleteModal({ export default function ConfirmDeleteModal({
open, open,
@@ -46,7 +47,9 @@ export default function ConfirmDeleteModal({
function handleUserConfirmDelete(): void { function handleUserConfirmDelete(): void {
if (idToDelete) { if (idToDelete) {
setIsDeleteing(true); setIsDeleteing(true);
deleteQuizLPCategories(idToDelete)
// RULES: delete<CollectionName>
deleteCustomer(idToDelete)
.then(() => { .then(() => {
reloadRows(); reloadRows();
handleClose(); handleClose();

View File

@@ -29,6 +29,8 @@ import { paths } from '@/paths';
import { logger } from '@/lib/default-logger'; import { logger } from '@/lib/default-logger';
import { Option } from '@/components/core/option'; import { Option } from '@/components/core/option';
import { toast } from '@/components/core/toaster'; import { toast } from '@/components/core/toaster';
import { createCustomer } from '@/db/Customers/Create';
import isDevelopment from '@/lib/check-is-development';
function fileToBase64(file: Blob): Promise<string> { function fileToBase64(file: Blob): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@@ -67,12 +69,19 @@ type Values = zod.infer<typeof schema>;
const defaultValues = { const defaultValues = {
avatar: '', avatar: '',
name: '', name: 'new name',
email: '', email: '123@123.com',
phone: '', phone: '91234567',
company: '', company: '',
billingAddress: { country: '', state: '', city: '', zipCode: '', line1: '', line2: '' }, billingAddress: {
taxId: '', country: 'US',
state: '00000',
city: 'NY',
zipCode: '00000',
line1: 'test line 1',
line2: 'test line 2',
},
taxId: '12345',
timezone: 'new_york', timezone: 'new_york',
language: 'en', language: 'en',
currency: 'USD', currency: 'USD',
@@ -90,14 +99,15 @@ export function CustomerCreateForm(): React.JSX.Element {
} = useForm<Values>({ defaultValues, resolver: zodResolver(schema) }); } = useForm<Values>({ defaultValues, resolver: zodResolver(schema) });
const onSubmit = React.useCallback( const onSubmit = React.useCallback(
async (_: Values): Promise<void> => { async (values: Values): Promise<void> => {
try { try {
// Make API request // Use standard create method from db/Customers/Create
toast.success('Customer updated'); const record = await createCustomer(values);
router.push(paths.dashboard.customers.details('1')); toast.success('Customer created');
router.push(paths.dashboard.customers.details(record.id));
} catch (err) { } catch (err) {
logger.error(err); logger.error(err);
toast.error('Something went wrong!'); toast.error('Failed to create customer');
} }
}, },
[router] [router]
@@ -122,12 +132,22 @@ export function CustomerCreateForm(): React.JSX.Element {
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<Card> <Card>
<CardContent> <CardContent>
<Stack divider={<Divider />} spacing={4}> <Stack
divider={<Divider />}
spacing={4}
>
<Stack spacing={3}> <Stack spacing={3}>
<Typography variant="h6">Account information</Typography> <Typography variant="h6">Account information</Typography>
<Grid container spacing={3}> <Grid
container
spacing={3}
>
<Grid xs={12}> <Grid xs={12}>
<Stack direction="row" spacing={3} sx={{ alignItems: 'center' }}> <Stack
direction="row"
spacing={3}
sx={{ alignItems: 'center' }}
>
<Box <Box
sx={{ sx={{
border: '1px dashed var(--mui-palette-divider)', border: '1px dashed var(--mui-palette-divider)',
@@ -151,7 +171,10 @@ export function CustomerCreateForm(): React.JSX.Element {
<CameraIcon fontSize="var(--Icon-fontSize)" /> <CameraIcon fontSize="var(--Icon-fontSize)" />
</Avatar> </Avatar>
</Box> </Box>
<Stack spacing={1} sx={{ alignItems: 'flex-start' }}> <Stack
spacing={1}
sx={{ alignItems: 'flex-start' }}
>
<Typography variant="subtitle1">Avatar</Typography> <Typography variant="subtitle1">Avatar</Typography>
<Typography variant="caption">Min 400x400px, PNG or JPEG</Typography> <Typography variant="caption">Min 400x400px, PNG or JPEG</Typography>
<Button <Button
@@ -163,16 +186,27 @@ export function CustomerCreateForm(): React.JSX.Element {
> >
Select Select
</Button> </Button>
<input hidden onChange={handleAvatarChange} ref={avatarInputRef} type="file" /> <input
hidden
onChange={handleAvatarChange}
ref={avatarInputRef}
type="file"
/>
</Stack> </Stack>
</Stack> </Stack>
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.name)} fullWidth> <FormControl
error={Boolean(errors.name)}
fullWidth
>
<InputLabel required>Name</InputLabel> <InputLabel required>Name</InputLabel>
<OutlinedInput {...field} /> <OutlinedInput {...field} />
{errors.name ? <FormHelperText>{errors.name.message}</FormHelperText> : null} {errors.name ? <FormHelperText>{errors.name.message}</FormHelperText> : null}
@@ -180,25 +214,40 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="email" name="email"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.email)} fullWidth> <FormControl
error={Boolean(errors.email)}
fullWidth
>
<InputLabel required>Email address</InputLabel> <InputLabel required>Email address</InputLabel>
<OutlinedInput {...field} type="email" /> <OutlinedInput
{...field}
type="email"
/>
{errors.email ? <FormHelperText>{errors.email.message}</FormHelperText> : null} {errors.email ? <FormHelperText>{errors.email.message}</FormHelperText> : null}
</FormControl> </FormControl>
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="phone" name="phone"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.phone)} fullWidth> <FormControl
error={Boolean(errors.phone)}
fullWidth
>
<InputLabel required>Phone number</InputLabel> <InputLabel required>Phone number</InputLabel>
<OutlinedInput {...field} /> <OutlinedInput {...field} />
{errors.phone ? <FormHelperText>{errors.phone.message}</FormHelperText> : null} {errors.phone ? <FormHelperText>{errors.phone.message}</FormHelperText> : null}
@@ -206,12 +255,18 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="company" name="company"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.company)} fullWidth> <FormControl
error={Boolean(errors.company)}
fullWidth
>
<InputLabel>Company</InputLabel> <InputLabel>Company</InputLabel>
<OutlinedInput {...field} /> <OutlinedInput {...field} />
{errors.company ? <FormHelperText>{errors.company.message}</FormHelperText> : null} {errors.company ? <FormHelperText>{errors.company.message}</FormHelperText> : null}
@@ -223,13 +278,22 @@ export function CustomerCreateForm(): React.JSX.Element {
</Stack> </Stack>
<Stack spacing={3}> <Stack spacing={3}>
<Typography variant="h6">Billing information</Typography> <Typography variant="h6">Billing information</Typography>
<Grid container spacing={3}> <Grid
<Grid md={6} xs={12}> container
spacing={3}
>
<Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="billingAddress.country" name="billingAddress.country"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.billingAddress?.country)} fullWidth> <FormControl
error={Boolean(errors.billingAddress?.country)}
fullWidth
>
<InputLabel required>Country</InputLabel> <InputLabel required>Country</InputLabel>
<Select {...field}> <Select {...field}>
<Option value="">Choose a country</Option> <Option value="">Choose a country</Option>
@@ -244,12 +308,18 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="billingAddress.state" name="billingAddress.state"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.billingAddress?.state)} fullWidth> <FormControl
error={Boolean(errors.billingAddress?.state)}
fullWidth
>
<InputLabel required>State</InputLabel> <InputLabel required>State</InputLabel>
<OutlinedInput {...field} /> <OutlinedInput {...field} />
{errors.billingAddress?.state ? ( {errors.billingAddress?.state ? (
@@ -259,12 +329,18 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="billingAddress.city" name="billingAddress.city"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.billingAddress?.city)} fullWidth> <FormControl
error={Boolean(errors.billingAddress?.city)}
fullWidth
>
<InputLabel required>City</InputLabel> <InputLabel required>City</InputLabel>
<OutlinedInput {...field} /> <OutlinedInput {...field} />
{errors.billingAddress?.city ? ( {errors.billingAddress?.city ? (
@@ -274,12 +350,18 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="billingAddress.zipCode" name="billingAddress.zipCode"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.billingAddress?.zipCode)} fullWidth> <FormControl
error={Boolean(errors.billingAddress?.zipCode)}
fullWidth
>
<InputLabel required>Zip code</InputLabel> <InputLabel required>Zip code</InputLabel>
<OutlinedInput {...field} /> <OutlinedInput {...field} />
{errors.billingAddress?.zipCode ? ( {errors.billingAddress?.zipCode ? (
@@ -289,12 +371,18 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="billingAddress.line1" name="billingAddress.line1"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.billingAddress?.line1)} fullWidth> <FormControl
error={Boolean(errors.billingAddress?.line1)}
fullWidth
>
<InputLabel required>Address</InputLabel> <InputLabel required>Address</InputLabel>
<OutlinedInput {...field} /> <OutlinedInput {...field} />
{errors.billingAddress?.line1 ? ( {errors.billingAddress?.line1 ? (
@@ -304,14 +392,23 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="taxId" name="taxId"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.taxId)} fullWidth> <FormControl
error={Boolean(errors.taxId)}
fullWidth
>
<InputLabel>Tax ID</InputLabel> <InputLabel>Tax ID</InputLabel>
<OutlinedInput {...field} placeholder="e.g EU372054390" /> <OutlinedInput
{...field}
placeholder="e.g EU372054390"
/>
{errors.taxId ? <FormHelperText>{errors.taxId.message}</FormHelperText> : null} {errors.taxId ? <FormHelperText>{errors.taxId.message}</FormHelperText> : null}
</FormControl> </FormControl>
)} )}
@@ -321,17 +418,29 @@ export function CustomerCreateForm(): React.JSX.Element {
</Stack> </Stack>
<Stack spacing={3}> <Stack spacing={3}>
<Typography variant="h6">Shipping information</Typography> <Typography variant="h6">Shipping information</Typography>
<FormControlLabel control={<Checkbox defaultChecked />} label="Same as billing address" /> <FormControlLabel
control={<Checkbox defaultChecked />}
label="Same as billing address"
/>
</Stack> </Stack>
<Stack spacing={3}> <Stack spacing={3}>
<Typography variant="h6">Additional information</Typography> <Typography variant="h6">Additional information</Typography>
<Grid container spacing={3}> <Grid
<Grid md={6} xs={12}> container
spacing={3}
>
<Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="timezone" name="timezone"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.timezone)} fullWidth> <FormControl
error={Boolean(errors.timezone)}
fullWidth
>
<InputLabel required>Timezone</InputLabel> <InputLabel required>Timezone</InputLabel>
<Select {...field}> <Select {...field}>
<Option value="">Select a timezone</Option> <Option value="">Select a timezone</Option>
@@ -344,12 +453,18 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="language" name="language"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.language)} fullWidth> <FormControl
error={Boolean(errors.language)}
fullWidth
>
<InputLabel required>Language</InputLabel> <InputLabel required>Language</InputLabel>
<Select {...field}> <Select {...field}>
<Option value="">Select a language</Option> <Option value="">Select a language</Option>
@@ -362,12 +477,18 @@ export function CustomerCreateForm(): React.JSX.Element {
)} )}
/> />
</Grid> </Grid>
<Grid md={6} xs={12}> <Grid
md={6}
xs={12}
>
<Controller <Controller
control={control} control={control}
name="currency" name="currency"
render={({ field }) => ( render={({ field }) => (
<FormControl error={Boolean(errors.currency)} fullWidth> <FormControl
error={Boolean(errors.currency)}
fullWidth
>
<InputLabel>Currency</InputLabel> <InputLabel>Currency</InputLabel>
<Select {...field}> <Select {...field}>
<Option value="">Select a currency</Option> <Option value="">Select a currency</Option>
@@ -385,14 +506,24 @@ export function CustomerCreateForm(): React.JSX.Element {
</Stack> </Stack>
</CardContent> </CardContent>
<CardActions sx={{ justifyContent: 'flex-end' }}> <CardActions sx={{ justifyContent: 'flex-end' }}>
<Button color="secondary" component={RouterLink} href={paths.dashboard.customers.list}> <Button
color="secondary"
component={RouterLink}
href={paths.dashboard.customers.list}
>
Cancel Cancel
</Button> </Button>
<Button type="submit" variant="contained"> <Button
type="submit"
variant="contained"
>
Create customer Create customer
</Button> </Button>
</CardActions> </CardActions>
</Card> </Card>
<Box sx={{ display: isDevelopment ? 'block' : 'none' }}>
<pre>{JSON.stringify({ errors }, null, 2)}</pre>
</Box>
</form> </form>
); );
} }

View File

@@ -46,7 +46,7 @@ import isDevelopment from '@/lib/check-is-development';
const schema = zod.object({ const schema = zod.object({
name: zod.string().min(1, 'Name is required').max(255), name: zod.string().min(1, 'Name is required').max(255),
email: zod.string().email('Must be a valid email').min(1, 'Email is required').max(255), email: zod.string().email('Must be a valid email').min(1, 'Email is required').max(255),
phone: zod.string().min(1, 'Phone is required').max(15), phone: zod.string().min(1, 'Phone is required').max(25),
company: zod.string().max(255).optional(), company: zod.string().max(255).optional(),
billingAddress: zod.object({ billingAddress: zod.object({
country: zod.string().min(1, 'Country is required').max(255), country: zod.string().min(1, 'Country is required').max(255),
@@ -596,6 +596,9 @@ export function CustomerEditForm(): React.JSX.Element {
</LoadingButton> </LoadingButton>
</CardActions> </CardActions>
</Card> </Card>
<Box sx={{ display: isDevelopment ? 'block' : 'none' }}>
<pre>{JSON.stringify({ errors }, null, 2)}</pre>
</Box>
</form> </form>
); );
} }

View File

@@ -16,11 +16,24 @@ export interface Customer {
export interface CreateFormProps { export interface CreateFormProps {
name: string; name: string;
avatar?: string;
email: string; email: string;
phone?: string; phone?: string;
quota: number; company?: string;
status: 'pending' | 'active' | 'blocked'; billingAddress?: {
country: string;
state: string;
city: string;
zipCode: string;
line1: string;
line2?: string;
};
taxId?: string;
timezone: string;
language: string;
currency: string;
avatar?: string;
// quota?: number;
// status?: 'pending' | 'active' | 'blocked';
} }
export interface EditFormProps { export interface EditFormProps {

View File

@@ -1,6 +1,11 @@
// api method for crate customer record
// RULES:
// TBA
import { pb } from '@/lib/pb'; import { pb } from '@/lib/pb';
import { COL_CUSTOMERS } from '@/constants'; import { COL_CUSTOMERS } from '@/constants';
import type { CreateFormProps } from '@/components/dashboard/customer/type.d';
import type { RecordModel } from 'pocketbase';
export async function createCustomer(data) { export async function createCustomer(data: CreateFormProps): Promise<RecordModel> {
return pb.collection(COL_CUSTOMERS).create(data); return pb.collection(COL_CUSTOMERS).create(data);
} }