193 lines
5.2 KiB
TypeScript
193 lines
5.2 KiB
TypeScript
// src/sections/invoice/invoice-new-edit-form.tsx
|
|
|
|
import { today, fIsAfter } from 'src/utils/format-time';
|
|
|
|
import { _addressBooks } from 'src/_mock';
|
|
|
|
import { Form, schemaHelper } from 'src/components/hook-form';
|
|
|
|
import { InvoiceNewEditAddress } from './invoice-new-edit-address';
|
|
import { InvoiceNewEditStatusDate } from './invoice-new-edit-status-date';
|
|
import { defaultItem, InvoiceNewEditDetails } from './invoice-new-edit-details';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import { useForm } from 'react-hook-form';
|
|
import { useBoolean } from 'minimal-shared/hooks';
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
|
import Box from '@mui/material/Box';
|
|
import Button from '@mui/material/Button';
|
|
import Card from '@mui/material/Card';
|
|
|
|
import { toast } from 'src/components/snackbar';
|
|
import { useRouter } from 'src/routes/hooks';
|
|
import { paths } from 'src/routes/paths';
|
|
import type { IInvoiceItem } from 'src/types/invoice';
|
|
import { fileToBase64 } from 'src/utils/file-to-base64';
|
|
import { z as zod } from 'zod';
|
|
import { saveInvoice } from 'src/actions/invoice';
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
export type NewInvoiceSchemaType = zod.infer<typeof NewInvoiceSchema>;
|
|
export const NewInvoiceSchema = zod
|
|
.object({
|
|
id: zod.string(),
|
|
taxes: zod.number(),
|
|
status: zod.string(),
|
|
discount: zod.number(),
|
|
shipping: zod.number(),
|
|
subtotal: zod.number(),
|
|
totalAmount: zod.number(),
|
|
|
|
items: zod.array(
|
|
zod.object({
|
|
title: zod.string().min(1, { message: 'Title is required!' }),
|
|
service: zod.string().min(1, { message: 'Service is required!' }),
|
|
quantity: zod.number().int().positive().min(1, { message: 'Quantity must be more than 0' }),
|
|
// Not required
|
|
price: zod.number(),
|
|
total: zod.number(),
|
|
description: zod.string(),
|
|
})
|
|
),
|
|
invoiceNumber: zod.string(),
|
|
invoiceFrom: zod.custom<IInvoiceItem['invoiceFrom']>().nullable(),
|
|
|
|
invoiceTo: schemaHelper.nullableInput(zod.custom<IInvoiceItem['invoiceTo']>(), {
|
|
message: 'Invoice to is required!',
|
|
}),
|
|
sent: zod.number().default(0),
|
|
createDate: schemaHelper.date({ message: { required: 'Create date is required!' } }),
|
|
dueDate: schemaHelper.date({ message: { required: 'Due date is required!' } }),
|
|
// Not required
|
|
})
|
|
.refine((data) => !fIsAfter(data.createDate, data.dueDate), {
|
|
message: 'Due date cannot be earlier than create date!',
|
|
path: ['dueDate'],
|
|
});
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
type Props = {
|
|
currentInvoice?: IInvoiceItem;
|
|
};
|
|
|
|
export function InvoiceNewEditForm({ currentInvoice }: Props) {
|
|
const router = useRouter();
|
|
const { t } = useTranslation();
|
|
|
|
const loadingSave = useBoolean();
|
|
const loadingSend = useBoolean();
|
|
|
|
const defaultValues: NewInvoiceSchemaType = {
|
|
id: '',
|
|
sent: 0,
|
|
invoiceNumber: 'INV-1990',
|
|
createDate: today(),
|
|
dueDate: null,
|
|
taxes: 0,
|
|
shipping: 0,
|
|
status: 'draft',
|
|
discount: 0,
|
|
invoiceFrom: _addressBooks[0],
|
|
invoiceTo: null,
|
|
subtotal: 0,
|
|
totalAmount: 0,
|
|
items: [defaultItem],
|
|
};
|
|
|
|
const methods = useForm<NewInvoiceSchemaType>({
|
|
mode: 'all',
|
|
resolver: zodResolver(NewInvoiceSchema),
|
|
defaultValues,
|
|
values: currentInvoice,
|
|
});
|
|
|
|
const {
|
|
reset,
|
|
handleSubmit,
|
|
formState: { isSubmitting },
|
|
} = methods;
|
|
|
|
const handleSaveAsDraft = handleSubmit(async (data) => {
|
|
loadingSave.onTrue();
|
|
|
|
try {
|
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
reset();
|
|
|
|
loadingSave.onFalse();
|
|
router.push(paths.dashboard.invoice.root);
|
|
console.info('DATA', JSON.stringify(data, null, 2));
|
|
} catch (error) {
|
|
console.error(error);
|
|
loadingSave.onFalse();
|
|
}
|
|
});
|
|
|
|
const handleCreateAndSend = handleSubmit(async (data) => {
|
|
loadingSend.onTrue();
|
|
|
|
try {
|
|
if (currentInvoice) {
|
|
data.dueDate = '2029-01-01';
|
|
|
|
await saveInvoice(currentInvoice.id, data);
|
|
}
|
|
|
|
loadingSend.onFalse();
|
|
toast.success(currentInvoice ? 'Update success!' : 'Create success!');
|
|
|
|
router.push(paths.dashboard.invoice.root);
|
|
|
|
console.info('DATA', JSON.stringify(data, null, 2));
|
|
} catch (error) {
|
|
console.error(error);
|
|
loadingSend.onFalse();
|
|
}
|
|
});
|
|
|
|
return (
|
|
<Form methods={methods}>
|
|
<Card>
|
|
<InvoiceNewEditAddress />
|
|
|
|
<InvoiceNewEditStatusDate />
|
|
|
|
<InvoiceNewEditDetails />
|
|
</Card>
|
|
|
|
<Box
|
|
sx={{
|
|
mt: 3,
|
|
gap: 2,
|
|
display: 'flex',
|
|
justifyContent: 'flex-end',
|
|
}}
|
|
>
|
|
<Button
|
|
color="inherit"
|
|
size="large"
|
|
variant="outlined"
|
|
disabled={loadingSave.value && isSubmitting}
|
|
loading={loadingSave.value && isSubmitting}
|
|
onClick={handleSaveAsDraft}
|
|
>
|
|
Save as draft
|
|
</Button>
|
|
|
|
<Button
|
|
size="large"
|
|
variant="contained"
|
|
disabled={loadingSend.value && isSubmitting}
|
|
loading={loadingSend.value && isSubmitting}
|
|
onClick={handleCreateAndSend}
|
|
>
|
|
{currentInvoice ? 'Update' : 'Create'} & send
|
|
</Button>
|
|
</Box>
|
|
</Form>
|
|
);
|
|
}
|