diff --git a/002_source/cms/src/app/dashboard/students/[customerId]/BasicDetailCard.tsx b/002_source/cms/src/app/dashboard/students/detail/[customerId]/BasicDetailCard.tsx similarity index 100% rename from 002_source/cms/src/app/dashboard/students/[customerId]/BasicDetailCard.tsx rename to 002_source/cms/src/app/dashboard/students/detail/[customerId]/BasicDetailCard.tsx diff --git a/002_source/cms/src/app/dashboard/students/[customerId]/TitleCard.tsx b/002_source/cms/src/app/dashboard/students/detail/[customerId]/TitleCard.tsx similarity index 100% rename from 002_source/cms/src/app/dashboard/students/[customerId]/TitleCard.tsx rename to 002_source/cms/src/app/dashboard/students/detail/[customerId]/TitleCard.tsx diff --git a/002_source/cms/src/app/dashboard/students/[customerId]/page.tsx b/002_source/cms/src/app/dashboard/students/detail/[customerId]/page.tsx similarity index 100% rename from 002_source/cms/src/app/dashboard/students/[customerId]/page.tsx rename to 002_source/cms/src/app/dashboard/students/detail/[customerId]/page.tsx diff --git a/002_source/cms/src/app/dashboard/students/mail/[labelId]/[threadId]/page.tsx b/002_source/cms/src/app/dashboard/students/mail/[labelId]/[threadId]/page.tsx new file mode 100644 index 0000000..cf93176 --- /dev/null +++ b/002_source/cms/src/app/dashboard/students/mail/[labelId]/[threadId]/page.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import type { Metadata } from 'next'; + +import { config } from '@/config'; +import { ThreadView } from '@/components/dashboard/mail/thread-view'; + +export const metadata = { title: `Thread | Mail | Dashboard | ${config.site.name}` } satisfies Metadata; + +interface PageProps { + params: { threadId: string }; +} + +export default function Page({ params }: PageProps): React.JSX.Element { + const { threadId } = params; + + return ; +} diff --git a/002_source/cms/src/app/dashboard/students/mail/[labelId]/layout.tsx b/002_source/cms/src/app/dashboard/students/mail/[labelId]/layout.tsx new file mode 100644 index 0000000..cdde2e8 --- /dev/null +++ b/002_source/cms/src/app/dashboard/students/mail/[labelId]/layout.tsx @@ -0,0 +1,142 @@ +import * as React from 'react'; + +import { dayjs } from '@/lib/dayjs'; +import { MailProvider } from '@/components/dashboard/mail/mail-context'; +import { MailView } from '@/components/dashboard/mail/mail-view'; +import type { Label, Thread } from '@/components/dashboard/mail/types'; + +function filterThreads(threads: Thread[], labelId: string): Thread[] { + return threads.filter((thread) => { + if (['inbox', 'sent', 'drafts', 'spam', 'trash'].includes(labelId)) { + return thread.folder === labelId; + } + + if (labelId === 'important') { + return thread.isImportant; + } + + if (labelId === 'starred') { + return thread.isStarred; + } + + if (thread.labels.includes(labelId)) { + return true; + } + + return false; + }); +} + +const labels = [ + { id: 'inbox', type: 'system', name: 'Inbox', unreadCount: 1, totalCount: 0 }, + { id: 'sent', type: 'system', name: 'Sent', unreadCount: 0, totalCount: 0 }, + { id: 'drafts', type: 'system', name: 'Drafts', unreadCount: 0, totalCount: 0 }, + { id: 'spam', type: 'system', name: 'Spam', unreadCount: 0, totalCount: 0 }, + { id: 'trash', type: 'system', name: 'Trash', unreadCount: 0, totalCount: 1 }, + { id: 'important', type: 'system', name: 'Important', unreadCount: 0, totalCount: 1 }, + { id: 'starred', type: 'system', name: 'Starred', unreadCount: 1, totalCount: 1 }, + { id: 'work', type: 'custom', name: 'Work', color: '#43A048', unreadCount: 0, totalCount: 1 }, + { id: 'business', type: 'custom', name: 'Business', color: '#1E88E5', unreadCount: 1, totalCount: 2 }, + { id: 'personal', type: 'custom', name: 'Personal', color: '#FB8A00', unreadCount: 0, totalCount: 1 }, +] satisfies Label[]; + +const threads = [ + { + id: 'TRD-004', + from: { avatar: '/assets/avatar-9.png', email: 'marcus.finn@domain.com', name: 'Marcus Finn' }, + to: [{ avatar: '/assets/avatar.png', email: 'sofia@devias.io', name: 'Sofia Rivers' }], + subject: 'Website redesign. Interested in collaboration', + message: `Hey there, + +I hope this email finds you well. I'm glad you liked my projects, and I would be happy to provide you with a quote for a similar project. + +Please let me know your requirements and any specific details you have in mind, so I can give you an accurate quote. + +Looking forward to hearing from you soon. + +Best regards, + +Marcus Finn`, + attachments: [ + { + id: 'ATT-001', + name: 'working-sketch.png', + size: '128.5 KB', + type: 'image', + url: '/assets/image-abstract-1.png', + }, + { id: 'ATT-002', name: 'summer-customers.pdf', size: '782.3 KB', type: 'file', url: '#' }, + { + id: 'ATT-003', + name: 'desktop-coffee.png', + size: '568.2 KB', + type: 'image', + url: '/assets/image-minimal-1.png', + }, + ], + folder: 'inbox', + labels: ['work', 'business'], + isImportant: true, + isStarred: false, + isUnread: true, + createdAt: dayjs().subtract(3, 'hour').toDate(), + }, + { + id: 'TRD-003', + to: [{ name: 'Sofia Rivers', avatar: '/assets/avatar.png', email: 'sofia@devias.io' }], + from: { name: 'Miron Vitold', avatar: '/assets/avatar-1.png', email: 'miron.vitold@domain.com' }, + subject: 'Amazing work', + message: `Hey, nice projects! I really liked the one in react. What's your quote on kinda similar project?`, + folder: 'spam', + labels: [], + isImportant: false, + isStarred: true, + isUnread: false, + createdAt: dayjs().subtract(1, 'day').toDate(), + }, + { + id: 'TRD-002', + from: { name: 'Penjani Inyene', avatar: '/assets/avatar-4.png', email: 'penjani.inyene@domain.com' }, + to: [{ name: 'Sofia Rivers', avatar: '/assets/avatar.png', email: 'sofia@devias.io' }], + subject: 'Flight reminder', + message: `Dear Sofia, + +Your flight is coming up soon. Please don't forget to check in for your scheduled flight.`, + folder: 'inbox', + labels: ['business'], + isImportant: false, + isStarred: false, + isUnread: false, + createdAt: dayjs().subtract(2, 'day').toDate(), + }, + { + id: 'TRD-001', + from: { name: 'Carson Darrin', avatar: '/assets/avatar-3.png', email: 'carson.darrin@domain.com' }, + to: [{ name: 'Sofia Rivers', avatar: '/assets/avatar.png', email: 'sofia@devias.io' }], + subject: 'Possible candidates for the position', + message: `My market leading client has another fantastic opportunity for an experienced Software Developer to join them on a heavily remote basis`, + folder: 'trash', + labels: ['personal'], + isImportant: false, + isStarred: false, + isUnread: true, + createdAt: dayjs().subtract(2, 'day').toDate(), + }, +] satisfies Thread[]; + +interface LayoutProps { + children: React.ReactNode; + params: { labelId: string }; +} + +export default function Layout({ children, params }: LayoutProps): React.JSX.Element { + const { labelId } = params; + + const filteredThreads = filterThreads(threads, labelId); + + return ( + + {children} + + ); +} diff --git a/002_source/cms/src/app/dashboard/students/mail/[labelId]/page.tsx b/002_source/cms/src/app/dashboard/students/mail/[labelId]/page.tsx new file mode 100644 index 0000000..9989b26 --- /dev/null +++ b/002_source/cms/src/app/dashboard/students/mail/[labelId]/page.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; +import type { Metadata } from 'next'; + +import { config } from '@/config'; +import { ThreadsView } from '@/components/dashboard/mail/threads-view'; + +export const metadata = { title: `Mail | Dashboard | ${config.site.name}` } satisfies Metadata; + +export default function Page(): React.JSX.Element { + return ; +} diff --git a/002_source/cms/src/app/dashboard/students/page.tsx b/002_source/cms/src/app/dashboard/students/page.tsx index d6ec7ba..5dcb6d0 100644 --- a/002_source/cms/src/app/dashboard/students/page.tsx +++ b/002_source/cms/src/app/dashboard/students/page.tsx @@ -10,7 +10,6 @@ import { useRouter } from 'next/navigation'; import { COL_CUSTOMERS } from '@/constants'; import { LoadingButton } from '@mui/lab'; import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; import Card from '@mui/material/Card'; import Divider from '@mui/material/Divider'; import Stack from '@mui/material/Stack'; @@ -18,21 +17,17 @@ import Typography from '@mui/material/Typography'; import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus'; import type { ListResult, RecordModel } from 'pocketbase'; -import { config } from '@/config'; import { StudentsFilters } from '@/components/dashboard/student/students-filters'; -// import type { Filters } from '@/components/dashboard/student/customers-filters'; import { StudentsPagination } from '@/components/dashboard/student/students-pagination'; import { StudentsSelectionProvider } from '@/components/dashboard/student/students-selection-context'; import { StudentsTable } from '@/components/dashboard/student/students-table'; -import type { Student, Filters } from '@/components/dashboard/student/type.d'; -import { SampleCustomers } from './SampleCustomers'; +import type { Student } from '@/components/dashboard/student/type.d'; import { useTranslation } from 'react-i18next'; import { paths } from '@/paths'; import isDevelopment from '@/lib/check-is-development'; import { logger } from '@/lib/default-logger'; import { pb } from '@/lib/pb'; -import { toast } from '@/components/core/toaster'; import ErrorDisplay from '@/components/dashboard/error'; import { defaultStudent } from '@/components/dashboard/student/_constants'; import FormLoading from '@/components/loading'; @@ -57,11 +52,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { // const [recordCount, setRecordCount] = React.useState(0); const [listOption, setListOption] = React.useState({}); - const [listSort, setListSort] = React.useState({}); - - // - // const sortedCustomers = applySort(SampleCustomers, sortDir); - // const filteredCustomers = applyFilters(sortedCustomers, { email, phone, status }); // const reloadRows = async (): Promise => { @@ -77,7 +67,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { setLessonCategoriesData(tempLessonTypes); setRecordCount(totalItems); setF(tempLessonTypes); - // console.log({ currentPage, f }); } catch (error) { // logger.error(error); @@ -107,8 +96,8 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { }, [currentPage, rowsPerPage, listOption]); React.useEffect(() => { - let tempFilter = [], - tempSortDir = ''; + const tempFilter = []; + let tempSortDir = ''; if (status) { tempFilter.push(`status = "${status}"`); @@ -133,11 +122,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { preFinalListOption = { ...preFinalListOption, sort: tempSortDir }; } setListOption(preFinalListOption); - // setListOption({ - // filter: tempFilter.join(' && '), - // sort: tempSortDir, - // // - // }); }, [sortDir, email, phone, status]); if (showLoading) return ; @@ -215,42 +199,12 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { ); } -// Sorting and filtering has to be done on the server. - -function applySort(row: Student[], sortDir: 'asc' | 'desc' | undefined): Student[] { - return row.sort((a, b) => { - if (sortDir === 'asc') { - return a.createdAt.getTime() - b.createdAt.getTime(); - } - - return b.createdAt.getTime() - a.createdAt.getTime(); - }); -} - -function applyFilters(row: Student[], { email, phone, status }: Filters): Student[] { - return row.filter((item) => { - if (email) { - if (!item.email?.toLowerCase().includes(email.toLowerCase())) { - return false; - } - } - - if (phone) { - if (!item.phone?.toLowerCase().includes(phone.toLowerCase())) { - return false; - } - } - - if (status) { - if (item.status !== status) { - return false; - } - } - - return true; - }); -} - interface PageProps { - searchParams: { email?: string; phone?: string; sortDir?: 'asc' | 'desc'; status?: string }; + searchParams: { + email?: string; + phone?: string; + sortDir?: 'asc' | 'desc'; + status?: string; + // + }; } diff --git a/002_source/cms/src/app/dashboard/students/xxx/BasicDetailCard.tsx b/002_source/cms/src/app/dashboard/students/xxx/BasicDetailCard.tsx new file mode 100644 index 0000000..e64427e --- /dev/null +++ b/002_source/cms/src/app/dashboard/students/xxx/BasicDetailCard.tsx @@ -0,0 +1,80 @@ +'use client'; + +import * as React from 'react'; +import Avatar from '@mui/material/Avatar'; +import Card from '@mui/material/Card'; +import CardHeader from '@mui/material/CardHeader'; +import Chip from '@mui/material/Chip'; +import Divider from '@mui/material/Divider'; +import IconButton from '@mui/material/IconButton'; +import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; +import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User'; +import { useTranslation } from 'react-i18next'; + +import { PropertyItem } from '@/components/core/property-item'; +import { PropertyList } from '@/components/core/property-list'; +// import { CrCategory } from '@/components/dashboard/cr/categories/type'; +import type { Student } from '@/components/dashboard/student/type.d'; + +export default function BasicDetailCard({ + lpModel: model, + handleEditClick, +}: { + lpModel: Student; + handleEditClick: () => void; +}): React.JSX.Element { + const { t } = useTranslation(); + + return ( + + { + handleEditClick(); + }} + > + + + } + avatar={ + + + + } + title={t('list.basic-details')} + /> + } + orientation="vertical" + sx={{ '--PropertyItem-padding': '12px 24px' }} + > + {( + [ + { + key: 'Customer ID', + value: ( + + ), + }, + { key: 'Email', value: model.email }, + { key: 'Quota', value: model.quota }, + { key: 'Status', value: model.status }, + ] satisfies { key: string; value: React.ReactNode }[] + ).map( + (item): React.JSX.Element => ( + + ) + )} + + + ); +} diff --git a/002_source/cms/src/app/dashboard/students/xxx/TitleCard.tsx b/002_source/cms/src/app/dashboard/students/xxx/TitleCard.tsx new file mode 100644 index 0000000..9be2807 --- /dev/null +++ b/002_source/cms/src/app/dashboard/students/xxx/TitleCard.tsx @@ -0,0 +1,76 @@ +'use client'; + +import * as React from 'react'; +import { Button } from '@mui/material'; +import Avatar from '@mui/material/Avatar'; +import Chip from '@mui/material/Chip'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown'; +import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle'; +import { useTranslation } from 'react-i18next'; +import type { Student } from '@/components/dashboard/student/type.d'; + +// import type { CrCategory } from '@/components/dashboard/cr/categories/type'; + +function getImageUrlFrRecord(record: Student): string { + // TODO: fix this + // `http://127.0.0.1:8090/api/files/${'record.collectionId'}/${'record.id'}/${'record.cat_image'}`; + return 'getImageUrlFrRecord(helloworld)'; +} + +export default function SampleTitleCard({ lpModel }: { lpModel: Student }): React.JSX.Element { + const { t } = useTranslation(); + + return ( + <> + + + {t('empty')} + +
+ + {lpModel.email} + + } + label={lpModel.quota} + size="small" + variant="outlined" + /> + + + {lpModel.status} + +
+
+
+ +
+ + ); +} diff --git a/002_source/cms/src/app/dashboard/students/xxx/page.tsx b/002_source/cms/src/app/dashboard/students/xxx/page.tsx new file mode 100644 index 0000000..5362910 --- /dev/null +++ b/002_source/cms/src/app/dashboard/students/xxx/page.tsx @@ -0,0 +1,142 @@ +'use client'; + +import * as React from 'react'; +import RouterLink from 'next/link'; +import { useParams, useRouter } from 'next/navigation'; +import SampleAddressCard from '@/app/dashboard/Sample/AddressCard'; +import { SampleNotifications } from '@/app/dashboard/Sample/Notifications'; +import SamplePaymentCard from '@/app/dashboard/Sample/PaymentCard'; +import SampleSecurityCard from '@/app/dashboard/Sample/SecurityCard'; + +import Box from '@mui/material/Box'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import Grid from '@mui/material/Unstable_Grid2'; +import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft'; +import type { RecordModel } from 'pocketbase'; +import { useTranslation } from 'react-i18next'; + +import { config } from '@/config'; +import { paths } from '@/paths'; +import { logger } from '@/lib/default-logger'; +import { pb } from '@/lib/pb'; +import { toast } from '@/components/core/toaster'; + +import ErrorDisplay from '@/components/dashboard/error'; + +import { Notifications } from '@/components/dashboard/student/notifications'; +import FormLoading from '@/components/loading'; +import BasicDetailCard from './BasicDetailCard'; +import TitleCard from './TitleCard'; +import { defaultStudent } from '@/components/dashboard/student/_constants'; +import type { Student } from '@/components/dashboard/student/type.d'; +import { COL_STUDENTS } from '@/constants'; + +export default function Page(): React.JSX.Element { + const { t } = useTranslation(); + const router = useRouter(); + // + const { customerId } = useParams<{ customerId: string }>(); + // + const [showLoading, setShowLoading] = React.useState(true); + const [showError, setShowError] = React.useState({ show: false, detail: '' }); + // + const [showLessonCategory, setShowLessonCategory] = React.useState(defaultStudent); + + function handleEditClick(): void { + router.push(paths.dashboard.students.edit(showLessonCategory.id)); + } + + React.useEffect(() => { + if (customerId) { + pb.collection(COL_STUDENTS) + .getOne(customerId) + .then((model: RecordModel) => { + setShowLessonCategory({ ...defaultStudent, ...model }); + }) + .catch((err) => { + logger.error(err); + toast(t('list.error')); + + setShowError({ show: true, detail: JSON.stringify(err) }); + }) + .finally(() => { + setShowLoading(false); + }); + } + }, [customerId]); + + // return <>{JSON.stringify({ showError, showLessonCategory }, null, 2)}; + + if (showLoading) return ; + if (showError.show) + return ( + + ); + + return ( + + + +
+ + + Students + +
+ + + +
+ + + + + + + + + + + + + + + +
+
+ ); +} diff --git a/002_source/cms/src/components/dashboard/layout/config.ts b/002_source/cms/src/components/dashboard/layout/config.ts index d4abda1..4944931 100644 --- a/002_source/cms/src/components/dashboard/layout/config.ts +++ b/002_source/cms/src/components/dashboard/layout/config.ts @@ -136,7 +136,7 @@ export const layoutConfig = { items: [ { key: 'students', title: 'List students', href: paths.dashboard.students.list }, { key: 'students:create', title: 'Create student', href: paths.dashboard.students.create }, - { key: 'students:details', title: 'Student details', href: paths.dashboard.students.details('1') }, + { key: 'students:details', title: 'Student details', href: paths.dashboard.students.view('1') }, ], }, // { diff --git a/002_source/cms/src/components/dashboard/layout/horizontal/horizontal-layout.tsx b/002_source/cms/src/components/dashboard/layout/horizontal/horizontal-layout.tsx index abf559d..21fd50a 100644 --- a/002_source/cms/src/components/dashboard/layout/horizontal/horizontal-layout.tsx +++ b/002_source/cms/src/components/dashboard/layout/horizontal/horizontal-layout.tsx @@ -30,7 +30,10 @@ export function HorizontalLayout({ children }: HorizontalLayoutProps): React.JSX minHeight: '100%', }} > - + - + void): ColumnDef @@ -143,7 +143,7 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef diff --git a/002_source/cms/src/db/Students/Create.tsx b/002_source/cms/src/db/Students/Create.tsx new file mode 100644 index 0000000..75c6f4e --- /dev/null +++ b/002_source/cms/src/db/Students/Create.tsx @@ -0,0 +1,11 @@ +// api method for crate student record +// RULES: +// TBA +import { pb } from '@/lib/pb'; +import { COL_STUDENTS } from '@/constants'; +import type { CreateFormProps } from '@/components/dashboard/student/type.d'; +import type { RecordModel } from 'pocketbase'; + +export async function createStudent(data: CreateFormProps): Promise { + return pb.collection(COL_STUDENTS).create(data); +} diff --git a/002_source/cms/src/db/Students/Delete.tsx b/002_source/cms/src/db/Students/Delete.tsx new file mode 100644 index 0000000..a9406ae --- /dev/null +++ b/002_source/cms/src/db/Students/Delete.tsx @@ -0,0 +1,6 @@ +import { pb } from '@/lib/pb'; +import { COL_STUDENTS } from '@/constants'; + +export async function deleteStudent(id: string): Promise { + return pb.collection(COL_STUDENTS).delete(id); +} diff --git a/002_source/cms/src/db/Students/GetActiveCount.tsx b/002_source/cms/src/db/Students/GetActiveCount.tsx new file mode 100644 index 0000000..c36fbca --- /dev/null +++ b/002_source/cms/src/db/Students/GetActiveCount.tsx @@ -0,0 +1,9 @@ +import { COL_STUDENTS } from '@/constants'; +import { pb } from '@/lib/pb'; + +export default async function GetActiveCount(): Promise { + const { totalItems: count } = await pb.collection(COL_STUDENTS).getList(1, 1, { + filter: 'status = "active"', + }); + return count; +} diff --git a/002_source/cms/src/db/Students/GetAll.tsx b/002_source/cms/src/db/Students/GetAll.tsx new file mode 100644 index 0000000..1e7c2c1 --- /dev/null +++ b/002_source/cms/src/db/Students/GetAll.tsx @@ -0,0 +1,7 @@ +import { pb } from '@/lib/pb'; +import { COL_STUDENTS } from '@/constants'; +import { RecordModel } from 'pocketbase'; + +export async function getAllStudents(options = {}): Promise { + return pb.collection(COL_STUDENTS).getFullList(options); +} diff --git a/002_source/cms/src/db/Students/GetAllCount.tsx b/002_source/cms/src/db/Students/GetAllCount.tsx new file mode 100644 index 0000000..91f84cc --- /dev/null +++ b/002_source/cms/src/db/Students/GetAllCount.tsx @@ -0,0 +1,7 @@ +import { pb } from '@/lib/pb'; +import { COL_STUDENTS } from '@/constants'; + +export async function getAllStudentsCount(): Promise { + const result = await pb.collection(COL_STUDENTS).getList(1, 1); + return result.totalItems; +} diff --git a/002_source/cms/src/db/Students/GetBlockedCount.tsx b/002_source/cms/src/db/Students/GetBlockedCount.tsx new file mode 100644 index 0000000..eca70b6 --- /dev/null +++ b/002_source/cms/src/db/Students/GetBlockedCount.tsx @@ -0,0 +1,9 @@ +import { COL_STUDENTS } from '@/constants'; +import { pb } from '@/lib/pb'; + +export default async function GetBlockedCount(): Promise { + const { totalItems: count } = await pb.collection(COL_STUDENTS).getList(1, 1, { + filter: 'status = "blocked"', + }); + return count; +} diff --git a/002_source/cms/src/db/Students/GetById.tsx b/002_source/cms/src/db/Students/GetById.tsx new file mode 100644 index 0000000..39a12d8 --- /dev/null +++ b/002_source/cms/src/db/Students/GetById.tsx @@ -0,0 +1,7 @@ +import { pb } from '@/lib/pb'; +import { COL_STUDENTS } from '@/constants'; +import { RecordModel } from 'pocketbase'; + +export async function getStudentById(id: string): Promise { + return pb.collection(COL_STUDENTS).getOne(id); +} diff --git a/002_source/cms/src/db/Students/GetPendingCount.tsx b/002_source/cms/src/db/Students/GetPendingCount.tsx new file mode 100644 index 0000000..b7f3d7d --- /dev/null +++ b/002_source/cms/src/db/Students/GetPendingCount.tsx @@ -0,0 +1,9 @@ +import { COL_STUDENTS } from '@/constants'; +import { pb } from '@/lib/pb'; + +export default async function GetPendingCount(): Promise { + const { totalItems: count } = await pb.collection(COL_STUDENTS).getList(1, 1, { + filter: 'status = "pending"', + }); + return count; +} diff --git a/002_source/cms/src/db/Students/Helloworld.tsx b/002_source/cms/src/db/Students/Helloworld.tsx new file mode 100644 index 0000000..2487997 --- /dev/null +++ b/002_source/cms/src/db/Students/Helloworld.tsx @@ -0,0 +1,3 @@ +export function helloCustomer() { + return 'Hello from Customers module!'; +} diff --git a/002_source/cms/src/db/Students/Update.tsx b/002_source/cms/src/db/Students/Update.tsx new file mode 100644 index 0000000..7beeff7 --- /dev/null +++ b/002_source/cms/src/db/Students/Update.tsx @@ -0,0 +1,8 @@ +import { pb } from '@/lib/pb'; +import { COL_CUSTOMERS } from '@/constants'; +import type { RecordModel } from 'pocketbase'; +import type { EditFormProps } from '@/components/dashboard/customer/type.d'; + +export async function updateCustomer(id: string, data: Partial): Promise { + return pb.collection(COL_CUSTOMERS).update(id, data); +} diff --git a/002_source/cms/src/db/Students/_GUIDELINES.md b/002_source/cms/src/db/Students/_GUIDELINES.md new file mode 100644 index 0000000..6515d08 --- /dev/null +++ b/002_source/cms/src/db/Students/_GUIDELINES.md @@ -0,0 +1,31 @@ +# GUIDELINES + +This folder contains drivers for `Customer`/`Customers` records using PocketBase: + +- create (Create.tsx) +- read (GetById.tsx) +- write (Update.tsx) +- count (GetAllCount.tsx, GetActiveCount.tsx, GetBlockedCount.tsx, GetPendingCount.tsx) +- misc (Helloworld.tsx) +- delete (Delete.tsx) +- list (GetAll.tsx) + +the `@` sign refer to `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src` + +## Assumption and Requirements + +- assume `pb` is located in `@/lib/pb` +- no need to handle error in this function, i'll handle it in the caller +- type information defined in `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/db/Customers/type.d.tsx` + +simple template: + +```typescript +import { pb } from '@/lib/pb'; +import { COL_CUSTOMERS } from '@/constants'; + +export async function createCustomer(data: CreateFormProps) { + // ...content + // use direct return of pb.collection (e.g. return pb.collection(xxx)) +} +``` diff --git a/002_source/cms/src/paths.ts b/002_source/cms/src/paths.ts index 7e51347..d77ca2b 100644 --- a/002_source/cms/src/paths.ts +++ b/002_source/cms/src/paths.ts @@ -133,7 +133,7 @@ export const paths = { students: { list: '/dashboard/students', create: '/dashboard/students/create', - details: (id: string) => `/dashboard/students/${id}`, + view: (id: string) => `/dashboard/students/view/${id}`, edit: (id: string) => `/dashboard/students/edit/${id}`, }, customers: {