diff --git a/002_source/cms/src/components/dashboard/student/student-create-form.tsx b/002_source/cms/src/components/dashboard/student/student-create-form.tsx index f845be9..7b471be 100644 --- a/002_source/cms/src/components/dashboard/student/student-create-form.tsx +++ b/002_source/cms/src/components/dashboard/student/student-create-form.tsx @@ -1,9 +1,17 @@ 'use client'; +// src/components/dashboard/student/student-create-form.tsx +// import * as React from 'react'; import RouterLink from 'next/link'; import { useRouter } from 'next/navigation'; +import { UpdateBillingAddressById } from '@/db/billingAddress/UpdateById'; +import { createStudent } from '@/db/Students/Create'; +import { getStudentById } from '@/db/Students/GetById'; +import { UpdateStudentById } from '@/db/Students/UpdateById'; import { zodResolver } from '@hookform/resolvers/zod'; +import { LoadingButton } from '@mui/lab'; +// import Avatar from '@mui/material/Avatar'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -16,41 +24,38 @@ import FormControl from '@mui/material/FormControl'; import FormControlLabel from '@mui/material/FormControlLabel'; import FormHelperText from '@mui/material/FormHelperText'; import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; import OutlinedInput from '@mui/material/OutlinedInput'; import Select from '@mui/material/Select'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import Grid from '@mui/material/Unstable_Grid2'; +// import { Camera as CameraIcon } from '@phosphor-icons/react/dist/ssr/Camera'; +// import { Controller, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; import { z as zod } from 'zod'; import { paths } from '@/paths'; +import isDevelopment from '@/lib/check-is-development'; import { logger } from '@/lib/default-logger'; +import { base64ToFile, fileToBase64 } from '@/lib/file-to-base64'; +import { pb } from '@/lib/pb'; import { Option } from '@/components/core/option'; import { toast } from '@/components/core/toaster'; -import { createCustomer } from '@/db/Customers/Create'; -import isDevelopment from '@/lib/check-is-development'; +import FormLoading from '@/components/loading'; -function fileToBase64(file: Blob): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = () => { - resolve(reader.result as string); - }; - reader.onerror = () => { - reject(new Error('Error converting file to base64')); - }; - }); -} +// import ErrorDisplay from '../../error'; +import ErrorDisplay from '../error'; +import { CreateFormProps, Student } from './type.d'; +// TODO: review schema const schema = zod.object({ - avatar: zod.string().optional(), 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), - phone: zod.string().min(1, 'Phone is required').max(15), - company: zod.string().max(255), + phone: zod.string().min(1, 'Phone is required').max(25), + company: zod.string().max(255).optional(), billingAddress: zod.object({ country: zod.string().min(1, 'Country is required').max(255), state: zod.string().min(1, 'State is required').max(255), @@ -63,12 +68,12 @@ const schema = zod.object({ timezone: zod.string().min(1, 'Timezone is required').max(255), language: zod.string().min(1, 'Language is required').max(255), currency: zod.string().min(1, 'Currency is required').max(255), + avatar: zod.string().optional(), }); type Values = zod.infer; const defaultValues = { - avatar: '', name: 'new name', email: '123@123.com', phone: '91234567', @@ -85,10 +90,18 @@ const defaultValues = { timezone: 'new_york', language: 'en', currency: 'USD', + avatar: '', } satisfies Values; -export function CustomerCreateForm(): React.JSX.Element { +export function StudentCreateForm(): React.JSX.Element { const router = useRouter(); + const { t } = useTranslation(['students']); + + // + const [isUpdating, setIsUpdating] = React.useState(false); + const [showLoading, setShowLoading] = React.useState(false); + // + const [showError, setShowError] = React.useState({ show: false, detail: '' }); const { control, @@ -100,14 +113,35 @@ export function CustomerCreateForm(): React.JSX.Element { const onSubmit = React.useCallback( async (values: Values): Promise => { + // Use standard create method from db/Customers/Create + const tempCreate: CreateFormProps = { + avatar: values.avatar ? await base64ToFile(values.avatar) : null, + // + name: values.name, + email: values.email, + phone: values.phone, + company: values.company, + timezone: values.timezone, + language: values.language, + currency: values.currency, + taxId: values.taxId, + state: 'pending', + meta: {}, + }; + try { - // Use standard create method from db/Customers/Create - const record = await createCustomer(values); - toast.success('Customer created'); - router.push(paths.dashboard.students.view(record.id)); + // if (billingAddressId) { + // await UpdateBillingAddressById(billingAddressId, values.billingAddress); + // } + + const record = await createStudent(tempCreate); + toast.success('Student created'); + // router.push(paths.dashboard.students.view(record.id)); } catch (err) { logger.error(err); - toast.error('Failed to create customer'); + toast.error('Failed to create Student'); + } finally { + setIsUpdating(false); } }, [router] @@ -137,7 +171,7 @@ export function CustomerCreateForm(): React.JSX.Element { spacing={4} > - Account information + {t('create.basic-info')} - Avatar - Min 400x400px, PNG or JPEG + {t('create.avatar')} + {t('create.avatarRequirements')} - Email address + {t('create.email-address')} - Phone number + {t('create.phone-number')} {errors.phone ? {errors.phone.message} : null} @@ -268,7 +303,10 @@ export function CustomerCreateForm(): React.JSX.Element { fullWidth > Company - + {errors.company ? {errors.company.message} : null} )} @@ -276,8 +314,9 @@ export function CustomerCreateForm(): React.JSX.Element { + {/* */} - Billing information + {t('create.billing-information')} Country {errors.billingAddress?.country ? ( {errors.billingAddress?.country?.message} @@ -362,7 +403,7 @@ export function CustomerCreateForm(): React.JSX.Element { error={Boolean(errors.billingAddress?.zipCode)} fullWidth > - Zip code + {t('create.zip-code')} {errors.billingAddress?.zipCode ? ( {errors.billingAddress?.zipCode?.message} @@ -383,7 +424,7 @@ export function CustomerCreateForm(): React.JSX.Element { error={Boolean(errors.billingAddress?.line1)} fullWidth > - Address + {t('create.address-line-1')} {errors.billingAddress?.line1 ? ( {errors.billingAddress?.line1?.message} @@ -424,7 +465,7 @@ export function CustomerCreateForm(): React.JSX.Element { /> - Additional information + {t('create.additional-information')} Timezone {errors.timezone ? {errors.timezone.message} : null} @@ -467,10 +512,11 @@ export function CustomerCreateForm(): React.JSX.Element { > Language {errors.language ? {errors.language.message} : null} @@ -489,12 +535,12 @@ export function CustomerCreateForm(): React.JSX.Element { error={Boolean(errors.currency)} fullWidth > - Currency + {t('create.currency')} {errors.currency ? {errors.currency.message} : null} @@ -511,14 +557,17 @@ export function CustomerCreateForm(): React.JSX.Element { component={RouterLink} href={paths.dashboard.students.list} > - Cancel + {t('create.cancelButton')} - + {t('create.updateButton')} + diff --git a/002_source/cms/src/components/dashboard/student/student-edit-form.tsx b/002_source/cms/src/components/dashboard/student/student-edit-form.tsx index df2bb73..1810e24 100644 --- a/002_source/cms/src/components/dashboard/student/student-edit-form.tsx +++ b/002_source/cms/src/components/dashboard/student/student-edit-form.tsx @@ -1,10 +1,15 @@ 'use client'; +// src/components/dashboard/student/student-edit-form.tsx +// import * as React from 'react'; import RouterLink from 'next/link'; import { useParams, useRouter } from 'next/navigation'; // -import { COL_CUSTOMERS } from '@/constants'; +import { COL_CUSTOMERS, COL_USER_METAS } from '@/constants'; +import { UpdateBillingAddressById } from '@/db/billingAddress/UpdateById'; +import { getStudentById } from '@/db/Students/GetById'; +import { UpdateStudentById } from '@/db/Students/UpdateById'; import { zodResolver } from '@hookform/resolvers/zod'; import { LoadingButton } from '@mui/lab'; // @@ -87,14 +92,15 @@ const defaultValues = { export function StudentEditForm(): React.JSX.Element { const router = useRouter(); - const { t } = useTranslation(['lp_categories']); + const { t } = useTranslation(['students']); - const { customerId } = useParams<{ customerId: string }>(); + const { id: studentId } = useParams<{ id: string }>(); // const [isUpdating, setIsUpdating] = React.useState(false); const [showLoading, setShowLoading] = React.useState(false); // const [showError, setShowError] = React.useState({ show: false, detail: '' }); + const [billingAddressId, setBillingAddressId] = React.useState(null); const { control, @@ -110,30 +116,38 @@ export function StudentEditForm(): React.JSX.Element { setIsUpdating(true); const updateData = { + avatar: values.avatar ? await base64ToFile(values.avatar) : null, + // name: values.name, email: values.email, phone: values.phone, company: values.company, - billingAddress: values.billingAddress, - taxId: values.taxId, + // + // billingAddress: values.billingAddress, + // timezone: values.timezone, language: values.language, currency: values.currency, - avatar: values.avatar ? await base64ToFile(values.avatar) : null, + taxId: values.taxId, }; try { - await pb.collection(COL_CUSTOMERS).update(customerId, updateData); - toast.success('Customer updated successfully'); + // await pb.collection(COL_USER_METAS).update(studentId, updateData); + await UpdateStudentById(studentId, updateData); + toast.success('Student updated successfully'); router.push(paths.dashboard.students.list); + + if (billingAddressId) { + await UpdateBillingAddressById(billingAddressId, values.billingAddress); + } } catch (error) { logger.error(error); - toast.error('Failed to update customer'); + toast.error('Failed to update student'); } finally { setIsUpdating(false); } }, - [customerId, router] + [studentId, router] ); const avatarInputRef = React.useRef(null); @@ -162,13 +176,14 @@ export function StudentEditForm(): React.JSX.Element { setShowLoading(true); try { - const result = await pb.collection(COL_CUSTOMERS).getOne(id); + const result = await getStudentById(id); reset({ ...defaultValues, ...result }); - console.log({ result }); - if (result.avatar_file) { + setBillingAddressId(result.billingAddress.id); + + if (result.avatar) { const fetchResult = await fetch( - `http://127.0.0.1:8090/api/files/${result.collectionId}/${result.id}/${result.avatar_file}` + `http://127.0.0.1:8090/api/files/${result.collectionId}/${result.id}/${result.avatar}` ); const blob = await fetchResult.blob(); const url = await fileToBase64(blob); @@ -176,7 +191,7 @@ export function StudentEditForm(): React.JSX.Element { } } catch (error) { logger.error(error); - toast.error('Failed to load customer data'); + toast.error('Failed to load student data'); setShowError({ show: true, detail: JSON.stringify(error, null, 2) }); } finally { setShowLoading(false); @@ -186,9 +201,9 @@ export function StudentEditForm(): React.JSX.Element { ); React.useEffect(() => { - void loadExistingData(customerId); + void loadExistingData(studentId); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [customerId]); + }, [studentId]); if (showLoading) return ; if (showError.show) @@ -299,7 +314,7 @@ export function StudentEditForm(): React.JSX.Element { error={Boolean(errors.email)} fullWidth > - Email + {t('edit.email-address')} - Phone + {t('edit.phone-number')} {errors.phone ? {errors.phone.message} : null} @@ -354,7 +369,7 @@ export function StudentEditForm(): React.JSX.Element { {/* */} - Billing Information + {t('edit.billing-information')} Country {errors.billingAddress?.country ? ( {errors.billingAddress.country.message} @@ -438,7 +456,7 @@ export function StudentEditForm(): React.JSX.Element { error={Boolean(errors.billingAddress?.zipCode)} fullWidth > - Zip Code + {t('edit.zip-code')} {errors.billingAddress?.zipCode ? ( {errors.billingAddress.zipCode.message} @@ -459,7 +477,7 @@ export function StudentEditForm(): React.JSX.Element { error={Boolean(errors.billingAddress?.line1)} fullWidth > - Address Line 1 + {t('edit.address-line-1')} {errors.billingAddress?.line1 ? ( {errors.billingAddress.line1.message} @@ -494,7 +512,7 @@ export function StudentEditForm(): React.JSX.Element { - Additional Information + {t('edit.additional-information')} Language {errors.language ? {errors.language.message} : null} @@ -562,8 +582,9 @@ export function StudentEditForm(): React.JSX.Element { error={Boolean(errors.currency)} fullWidth > - Currency + {t('edit.currency')}