update build ok,
This commit is contained in:
@@ -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<number>(0);
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState<number>(5);
|
||||
const [currentPage, setCurrentPage] = React.useState<number>(1);
|
||||
|
||||
//
|
||||
const [isLoadingAddPage, setIsLoadingAddPage] = React.useState<boolean>(false);
|
||||
const [lessonTypesData, setLessonTypesData] = React.useState<LessonType[]>([]);
|
||||
// const [recordModel, setRecordModel] = React.useState<RecordModel[]>([]);
|
||||
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<RecordModel>) => {
|
||||
// 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 {
|
||||
<LessonTypesTable reloadRows={reloadRows} rows={filteredLessonTypes} />
|
||||
</Box>
|
||||
<Divider />
|
||||
<LessonTypesPagination count={filteredLessonTypes.length + 100} page={0} />
|
||||
<LessonTypesPagination
|
||||
count={recordCount}
|
||||
page={0}
|
||||
rowsPerPage={rowsPerPage}
|
||||
setRowsPerPage={setRowsPerPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
/>
|
||||
</Card>
|
||||
</LessonTypesSelectionProvider>
|
||||
</Stack>
|
||||
|
@@ -4,6 +4,7 @@ import { dayjs } from '@/lib/dayjs';
|
||||
|
||||
export interface LessonType {
|
||||
id: string;
|
||||
isEmpty?: boolean;
|
||||
name: string;
|
||||
type: string;
|
||||
pos: number;
|
||||
|
@@ -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<void> {
|
||||
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 (
|
||||
<div>
|
||||
|
@@ -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();
|
||||
|
@@ -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<typeof schema>;
|
||||
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<void> => {
|
||||
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',
|
||||
}}
|
||||
></Box>
|
||||
/>
|
||||
<Stack spacing={1} sx={{ alignItems: 'flex-start' }}>
|
||||
<Typography variant="subtitle1">{t('dashboard.lessonTypes.create.avatar')}</Typography>
|
||||
<Typography variant="caption">{t('dashboard.lessonTypes.create.avatarRequirements')}</Typography>
|
||||
@@ -186,7 +208,7 @@ export function LessonTypeCreateForm(): React.JSX.Element {
|
||||
render={({ field }) => (
|
||||
<FormControl error={Boolean(errors.pos)} fullWidth>
|
||||
<InputLabel required>{t('dashboard.lessonTypes.create.position')}</InputLabel>
|
||||
<OutlinedInput {...field} />
|
||||
<OutlinedInput {...field} onChange={e => {field.onChange(parseInt(e.target.value))} } type="number"/>
|
||||
{errors.pos ? <FormHelperText>{errors.pos.message}</FormHelperText> : null}
|
||||
</FormControl>
|
||||
)}
|
||||
|
@@ -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';
|
||||
|
@@ -10,21 +10,48 @@ function noop(): void {
|
||||
interface LessonTypesPaginationProps {
|
||||
count: number;
|
||||
page: number;
|
||||
rowsPerPage: number;
|
||||
setRowsPerPage: React.Dispatch<React.SetStateAction<number>>;
|
||||
setCurrentPage: React.Dispatch<React.SetStateAction<number>>;
|
||||
}
|
||||
|
||||
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<HTMLInputElement | HTMLTextAreaElement>): void {
|
||||
console.log(e.target.value);
|
||||
console.log(parseInt(e.target.value));
|
||||
setRowsPerPage(parseInt(e.target.value));
|
||||
}
|
||||
|
||||
function handlePageChange(e: React.MouseEvent<HTMLButtonElement> | null): void {
|
||||
// console.log(e.target.value);
|
||||
// console.log(parseInt(e.target.value));
|
||||
// setCurrentPage(parseInt(e.target.value));
|
||||
}
|
||||
|
||||
return (
|
||||
<TablePagination
|
||||
component="div"
|
||||
count={count}
|
||||
onPageChange={noop}
|
||||
onRowsPerPageChange={noop}
|
||||
page={page}
|
||||
rowsPerPage={5}
|
||||
//
|
||||
rowsPerPage={rowsPerPage}
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
//
|
||||
onPageChange={(e) => {
|
||||
handlePageChange(e);
|
||||
}}
|
||||
onRowsPerPageChange={(e) => {
|
||||
handleRowsPerPageChange(e);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -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<LessonT
|
||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
|
||||
<div>
|
||||
<Link
|
||||
color="inherit"
|
||||
color={row.isEmpty ? 'disabled' : 'inherit'}
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.lesson_types.details(row.id)}
|
||||
sx={{ whiteSpace: 'nowrap' }}
|
||||
variant="subtitle2"
|
||||
>
|
||||
{row.name}
|
||||
{row.isEmpty ? '--' : row.name}
|
||||
</Link>
|
||||
</div>
|
||||
</Stack>
|
||||
),
|
||||
name: 'Name',
|
||||
width: '250px',
|
||||
width: '150px',
|
||||
},
|
||||
{
|
||||
formatter: (row): React.JSX.Element => (
|
||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
|
||||
<div>
|
||||
<Link
|
||||
color={row.isEmpty ? 'disabled' : 'inherit'}
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.lesson_types.details(row.id)}
|
||||
sx={{ whiteSpace: 'nowrap' }}
|
||||
variant="subtitle2"
|
||||
>
|
||||
{row.isEmpty ? '--' : row.type}
|
||||
</Link>
|
||||
</div>
|
||||
</Stack>
|
||||
),
|
||||
name: 'Lesson type',
|
||||
width: '150px',
|
||||
},
|
||||
{
|
||||
formatter: (row): React.JSX.Element => (
|
||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
|
||||
<div>
|
||||
<Link
|
||||
color={row.isEmpty ? 'disabled' : 'inherit'}
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.lesson_types.details(row.id)}
|
||||
sx={{ whiteSpace: 'nowrap' }}
|
||||
variant="subtitle2"
|
||||
>
|
||||
{row.isEmpty ? '--' : row.pos}
|
||||
</Link>
|
||||
</div>
|
||||
</Stack>
|
||||
),
|
||||
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<LessonT
|
||||
label: t('hidden'),
|
||||
icon: <ClockIcon color="var(--mui-palette-warning-main)" weight="fill" />,
|
||||
},
|
||||
no_value: {
|
||||
label: t('no_value'),
|
||||
icon: <ClockIcon color="var(--mui-palette-warning-main)" weight="fill" />,
|
||||
},
|
||||
} as const;
|
||||
|
||||
const { label, icon } = mapping[row.visible] ?? { label: 'Unknown', icon: null };
|
||||
const { label, icon } = row.isEmpty ? { label: '--', icon: null } : mapping[row.visible];
|
||||
|
||||
return (
|
||||
<Button
|
||||
@@ -78,7 +119,8 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonT
|
||||
}}
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
>
|
||||
<Chip icon={icon} label={label} size="small" variant="outlined" />
|
||||
{/* <Chip icon={icon} label={label} size="small" variant="outlined" /> */}
|
||||
{label}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
@@ -87,7 +129,7 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonT
|
||||
},
|
||||
{
|
||||
formatter(row) {
|
||||
return dayjs(row.createdAt).format('MMM D, YYYY h:mm A');
|
||||
return row.isEmpty ? '--' : dayjs(row.createdAt).format('MMM D, YYYY h:mm A');
|
||||
},
|
||||
name: 'Created at',
|
||||
width: '200px',
|
||||
@@ -96,11 +138,12 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonT
|
||||
{
|
||||
formatter: (row): React.JSX.Element => (
|
||||
<Stack direction="row" spacing={1}>
|
||||
<IconButton component={RouterLink} href={paths.dashboard.lesson_types.details(row.id)}>
|
||||
<IconButton disabled={row.isEmpty} component={RouterLink} href={paths.dashboard.lesson_types.details(row.id)}>
|
||||
<PencilSimpleIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color="error"
|
||||
disabled={row.isEmpty}
|
||||
onClick={() => {
|
||||
handleDeleteClick(row.id);
|
||||
}}
|
||||
|
@@ -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 };
|
||||
|
Reference in New Issue
Block a user