diff --git a/002_source/cms/src/app/dashboard/lesson_types/page.tsx b/002_source/cms/src/app/dashboard/lesson_types/page.tsx index cb2352d..dd475e1 100644 --- a/002_source/cms/src/app/dashboard/lesson_types/page.tsx +++ b/002_source/cms/src/app/dashboard/lesson_types/page.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { useRouter } from 'next/navigation'; +import { COL_LESSON_TYPES } from '@/constants'; import { LoadingButton } from '@mui/lab'; import Box from '@mui/material/Box'; import Card from '@mui/material/Card'; @@ -9,7 +10,7 @@ import Divider from '@mui/material/Divider'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus'; -import type { RecordModel } from 'pocketbase'; +import type { ListResult, RecordModel } from 'pocketbase'; import PocketBase from 'pocketbase'; import { useTranslation } from 'react-i18next'; @@ -17,7 +18,7 @@ import { paths } from '@/paths'; import { logger } from '@/lib/default-logger'; import { toast } from '@/components/core/toaster'; import type { LessonType } from '@/components/dashboard/lesson_type/ILessonType'; -import { safeAssignment } from '@/components/dashboard/lesson_type/interfaces'; +import { emptyLessonType, safeAssignment } from '@/components/dashboard/lesson_type/interfaces'; import { LessonTypesFilters } from '@/components/dashboard/lesson_type/lesson-types-filters'; import type { Filters } from '@/components/dashboard/lesson_type/lesson-types-filters'; import { LessonTypesPagination } from '@/components/dashboard/lesson_type/lesson-types-pagination'; @@ -25,8 +26,6 @@ import { LessonTypesSelectionProvider } from '@/components/dashboard/lesson_type import { LessonTypesTable } from '@/components/dashboard/lesson_type/lesson-types-table'; import FormLoading from '@/components/loading'; -import { Helloworld } from './db'; - const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL); interface PageProps { @@ -47,11 +46,16 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { const { email, phone, sortDir, spStatus, spName, spVisible, spType } = searchParams; const router = useRouter(); + const [recordCount, setRecordCount] = React.useState(0); + const [rowsPerPage, setRowsPerPage] = React.useState(5); + const [currentPage, setCurrentPage] = React.useState(1); + + // const [isLoadingAddPage, setIsLoadingAddPage] = React.useState(false); const [lessonTypesData, setLessonTypesData] = React.useState([]); // const [recordModel, setRecordModel] = React.useState([]); const sortedLessonTypes = applySort(lessonTypesData, sortDir); - const filteredLessonTypes = applyFilters(sortedLessonTypes, { + const filteredLessonTypesOld = applyFilters(sortedLessonTypes, { email, phone, spStatus, @@ -61,19 +65,43 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { // }); + const needToFill = 5 - filteredLessonTypesOld.length; + + const filteredLessonTypes: LessonType[] = filteredLessonTypesOld; + for (let i = 0; i < needToFill; i++) { + filteredLessonTypes.push(emptyLessonType); + } + const reloadRows = () => { - pb.collection('LessonsTypes') - .getFullList() - .then((lessonTypes: RecordModel[]) => { - const tempLessonTypes: LessonType[] = lessonTypes.map((lt) => { + pb.collection(COL_LESSON_TYPES) + .getList(currentPage, rowsPerPage, {}) + .then((lessonTypes: ListResult) => { + // console.log(lessonTypes); + const { items, page, perPage, totalItems, totalPages } = lessonTypes; + const tempLessonTypes: LessonType[] = items.map((lt) => { return safeAssignment(lt); }); + setLessonTypesData(tempLessonTypes); + setRecordCount(totalItems); }) .catch((err) => { logger.error(err); toast(t('dashboard.lessonTypes.list.error')); }); + + // pb.collection(COL_LESSON_TYPES) + // .getFullList() + // .then((lessonTypes: RecordModel[]) => { + // const tempLessonTypes: LessonType[] = lessonTypes.map((lt) => { + // return safeAssignment(lt); + // }); + // setLessonTypesData(tempLessonTypes); + // }) + // .catch((err) => { + // logger.error(err); + // toast(t('dashboard.lessonTypes.list.error')); + // }); }; React.useEffect(() => { @@ -123,7 +151,13 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element { - + diff --git a/002_source/cms/src/components/dashboard/lesson_type/ILessonType.tsx b/002_source/cms/src/components/dashboard/lesson_type/ILessonType.tsx index 65a4930..226e1c9 100644 --- a/002_source/cms/src/components/dashboard/lesson_type/ILessonType.tsx +++ b/002_source/cms/src/components/dashboard/lesson_type/ILessonType.tsx @@ -4,6 +4,7 @@ import { dayjs } from '@/lib/dayjs'; export interface LessonType { id: string; + isEmpty?: boolean; name: string; type: string; pos: number; diff --git a/002_source/cms/src/components/dashboard/lesson_type/confirm-delete-modal.tsx b/002_source/cms/src/components/dashboard/lesson_type/confirm-delete-modal.tsx index a5c2075..7f0ab5f 100644 --- a/002_source/cms/src/components/dashboard/lesson_type/confirm-delete-modal.tsx +++ b/002_source/cms/src/components/dashboard/lesson_type/confirm-delete-modal.tsx @@ -1,6 +1,8 @@ 'use client'; import * as React from 'react'; +import { useRouter } from 'next/navigation'; +import { COL_LESSON_TYPES } from '@/constants'; import { LoadingButton } from '@mui/lab'; import { Button, Container, Modal, Paper } from '@mui/material'; import Avatar from '@mui/material/Avatar'; @@ -8,9 +10,13 @@ import Box from '@mui/material/Box'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import { Note as NoteIcon } from '@phosphor-icons/react/dist/ssr/Note'; +import PocketBase from 'pocketbase'; import { useTranslation } from 'react-i18next'; -// import { deleteLessonType } from './http-actions'; +import { logger } from '@/lib/default-logger'; +import { toast } from '@/components/core/toaster'; + +const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL); export default function ConfirmDeleteModal({ open, @@ -38,23 +44,36 @@ export default function ConfirmDeleteModal({ transform: 'translate(-50%, -50%)', }; - const handleUserConfirmDelete = (): void => { + function performDelete(id: string): Promise { + return pb + .collection(COL_LESSON_TYPES) + .delete(id) + .then(() => { + toast(t('dashboard.lessonTypes.delete.success')); + reloadRows(); + }) + .catch((err) => { + logger.error(err); + toast(t('dashboard.lessonTypes.delete.error')); + }) + .finally(() => {}); + } + + function handleUserConfirmDelete(): void { if (idToDelete) { setIsDeleteing(true); - // deleteLessonType(idToDelete) - // .then(() => { - // reloadRows(); - // handleClose(); - // }) - // .catch((err) => { - // // console.error(err); - // setIsDeleteing(false); - // }) - // .finally(() => { - // setIsDeleteing(false); - // }); + performDelete(idToDelete) + .then(() => { + handleClose(); + setIsDeleteing(false); + }) + .catch((err) => { + // console.error(err) + logger.error(err); + toast(t('dashboard.lessonTypes.delete.error')); + }); } - }; + } return (
diff --git a/002_source/cms/src/components/dashboard/lesson_type/interfaces.ts b/002_source/cms/src/components/dashboard/lesson_type/interfaces.ts index e6b18cc..e18a1c6 100644 --- a/002_source/cms/src/components/dashboard/lesson_type/interfaces.ts +++ b/002_source/cms/src/components/dashboard/lesson_type/interfaces.ts @@ -1,3 +1,4 @@ +import { NO_NUM, NO_VALUE } from '@/constants'; import type { RecordModel } from 'pocketbase'; import { dayjs } from '@/lib/dayjs'; @@ -50,6 +51,11 @@ export const defaultLessonType: LessonType = { // createdAt: Date; }; +export const emptyLessonType: LessonType = { + ...defaultLessonType, + isEmpty: true, +}; + export function safeAssignment(inTemp: LessonType | RecordModel): LessonType { const { id, name, type, pos, visible, createdAt, email, quota, status } = { ...defaultLessonType, ...inTemp }; const oCreatedAt = dayjs(createdAt).toDate(); diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form.tsx index 1d0a7cd..fd9a18d 100644 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form.tsx +++ b/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import RouterLink from 'next/link'; import { useRouter } from 'next/navigation'; +import { COL_LESSON_TYPES } from '@/constants'; import { zodResolver } from '@hookform/resolvers/zod'; import { LoadingButton } from '@mui/lab'; import { MenuItem } from '@mui/material'; @@ -23,6 +24,8 @@ 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 type { RecordModel } from 'pocketbase'; +import PocketBase from 'pocketbase'; // import { Camera as CameraIcon } from '@phosphor-icons/react/dist/ssr/Camera'; // import axios from 'axios'; import { Controller, useForm } from 'react-hook-form'; @@ -33,6 +36,10 @@ import { paths } from '@/paths'; import { logger } from '@/lib/default-logger'; // import { Option } from '@/components/core/option'; import { toast } from '@/components/core/toaster'; +import { defaultLessonType, LessonTypeCreateForm, LessonTypeCreateFormDefault } from './interfaces'; +import type { LessonType } from './ILessonType'; + +const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL); // import { createLessonType } from './http-actions'; // import { LessonTypeCreateForm, LessonTypeCreateFormDefault } from './interfaces'; @@ -55,7 +62,7 @@ import { toast } from '@/components/core/toaster'; const schema = zod.object({ name: zod.string().min(1, 'Name is required').max(255), type: zod.string().min(1, 'Name is required').max(255), - pos: zod.string().min(1, 'Phone is required').max(15), + pos: zod.number().min(1, 'Phone is required').max(15), visible_to_user: zod.string().max(255), }); @@ -64,7 +71,7 @@ type Values = zod.infer; const defaultValues = { name: '', type: '', - pos: '1', + pos: 1, visible_to_user: 'visible', } satisfies Values; @@ -84,6 +91,21 @@ export function LessonTypeCreateForm(): React.JSX.Element { const onSubmit = React.useCallback( async (values: Values): Promise => { setIsCreating(true); + + const data: LessonTypeCreateForm = {...LessonTypeCreateFormDefault, ...values} + + pb.collection(COL_LESSON_TYPES).create(data).then((res)=>{ + console.log(res) + logger.debug(res); + toast.success(t('dashboard.lessonTypes.update.success')); + setIsCreating(false); + router.push(paths.dashboard.lesson_types.list); + + }).catch((err) => { + logger.error(err); + toast.error('Something went wrong!'); + setIsCreating(false);}) + // const tempCreate: LessonTypeCreateForm = LessonTypeCreateFormDefault; // tempCreate.name = values.name; // tempCreate.type = values.type; @@ -136,7 +158,7 @@ export function LessonTypeCreateForm(): React.JSX.Element { display: 'inline-flex', p: '4px', }} - > + /> {t('dashboard.lessonTypes.create.avatar')} {t('dashboard.lessonTypes.create.avatarRequirements')} @@ -186,7 +208,7 @@ export function LessonTypeCreateForm(): React.JSX.Element { render={({ field }) => ( {t('dashboard.lessonTypes.create.position')} - + {field.onChange(parseInt(e.target.value))} } type="number"/> {errors.pos ? {errors.pos.message} : null} )} diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-edit-form.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-type-edit-form.tsx index 0bf807f..6d63225 100644 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-edit-form.tsx +++ b/002_source/cms/src/components/dashboard/lesson_type/lesson-type-edit-form.tsx @@ -26,8 +26,6 @@ import Typography from '@mui/material/Typography'; import Grid from '@mui/material/Unstable_Grid2'; import type { RecordModel } from 'pocketbase'; import PocketBase from 'pocketbase'; -// import { Camera as CameraIcon } from '@phosphor-icons/react/dist/ssr/Camera'; -// import axios from 'axios'; import { Controller, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { z as zod } from 'zod'; diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-pagination.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-pagination.tsx index a124e14..1f0caee 100644 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-pagination.tsx +++ b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-pagination.tsx @@ -10,21 +10,48 @@ function noop(): void { interface LessonTypesPaginationProps { count: number; page: number; + rowsPerPage: number; + setRowsPerPage: React.Dispatch>; + setCurrentPage: React.Dispatch>; } -export function LessonTypesPagination({ count, page }: LessonTypesPaginationProps): React.JSX.Element { +export function LessonTypesPagination({ + count, + page, + rowsPerPage, + setRowsPerPage, + setCurrentPage, +}: LessonTypesPaginationProps): React.JSX.Element { // You should implement the pagination using a similar logic as the filters. // Note that when page change, you should keep the filter search params. + function handleRowsPerPageChange(e: React.ChangeEvent): void { + console.log(e.target.value); + console.log(parseInt(e.target.value)); + setRowsPerPage(parseInt(e.target.value)); + } + + function handlePageChange(e: React.MouseEvent | null): void { + // console.log(e.target.value); + // console.log(parseInt(e.target.value)); + // setCurrentPage(parseInt(e.target.value)); + } + return ( { + handlePageChange(e); + }} + onRowsPerPageChange={(e) => { + handleRowsPerPageChange(e); + }} /> ); } diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-table.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-table.tsx index a725ff0..7269e9d 100644 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-table.tsx +++ b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-table.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import RouterLink from 'next/link'; +import { NO_NUM, NO_VALUE } from '@/constants'; import { Button } from '@mui/material'; import Box from '@mui/material/Box'; import Chip from '@mui/material/Chip'; @@ -34,22 +35,58 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef
- {row.name} + {row.isEmpty ? '--' : row.name}
), name: 'Name', - width: '250px', + width: '150px', + }, + { + formatter: (row): React.JSX.Element => ( + +
+ + {row.isEmpty ? '--' : row.type} + +
+
+ ), + name: 'Lesson type', + width: '150px', + }, + { + formatter: (row): React.JSX.Element => ( + +
+ + {row.isEmpty ? '--' : row.pos} + +
+
+ ), + name: 'Lesson position', + width: '150px', }, - { field: 'type', name: 'Lesson type', width: '150px' }, - { field: 'pos', name: 'Lesson position', width: '150px' }, { formatter: (row): React.JSX.Element => { // eslint-disable-next-line react-hooks/rules-of-hooks @@ -67,9 +104,13 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef, }, + no_value: { + label: t('no_value'), + icon: , + }, } as const; - const { label, icon } = mapping[row.visible] ?? { label: 'Unknown', icon: null }; + const { label, icon } = row.isEmpty ? { label: '--', icon: null } : mapping[row.visible]; return ( ); }, @@ -87,7 +129,7 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef void): ColumnDef ( - + { handleDeleteClick(row.id); }} diff --git a/002_source/cms/src/constants.ts b/002_source/cms/src/constants.ts index ad482d0..23cd528 100644 --- a/002_source/cms/src/constants.ts +++ b/002_source/cms/src/constants.ts @@ -1,3 +1,5 @@ const COL_LESSON_TYPES = 'LessonsTypes'; +const NO_VALUE = 'NO_VALUE'; +const NO_NUM = -Infinity; -export { COL_LESSON_TYPES }; +export { COL_LESSON_TYPES, NO_VALUE, NO_NUM };