updat build ok,
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
need to fix local storage error
|
need to fix local storage error
|
||||||
|
|
||||||
page right (next page) button is not working
|
page right (next page) button is not working
|
||||||
|
in the middle of clone lesson type to lesson category
|
||||||
|
- [ ] listing page
|
||||||
|
- [ ] delete button on each row
|
||||||
|
- [ ] create page
|
||||||
|
- [ ] edit
|
||||||
|
- [ ] delete
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { COL_LESSON_CATEGORIES } from '@/constants';
|
||||||
|
import { LoadingButton } from '@mui/lab';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import Card from '@mui/material/Card';
|
import Card from '@mui/material/Card';
|
||||||
@@ -8,13 +11,24 @@ import Divider from '@mui/material/Divider';
|
|||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
|
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
|
||||||
|
import type { ListResult, RecordModel } from 'pocketbase';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import type { LessonCategory } from '@/components/dashboard/lesson_category/interfaces';
|
import { paths } from '@/paths';
|
||||||
|
import { logger } from '@/lib/default-logger';
|
||||||
|
import { pb } from '@/lib/pb';
|
||||||
|
import { toast } from '@/components/core/toaster';
|
||||||
|
import {
|
||||||
|
defaultLessonCategory,
|
||||||
|
type emptyLessonCategory,
|
||||||
|
type LessonCategory,
|
||||||
|
} from '@/components/dashboard/lesson_category/interfaces';
|
||||||
import { LessonCategoriesFilters } from '@/components/dashboard/lesson_category/lesson-categories-filters';
|
import { LessonCategoriesFilters } from '@/components/dashboard/lesson_category/lesson-categories-filters';
|
||||||
import type { Filters } from '@/components/dashboard/lesson_category/lesson-categories-filters';
|
import type { Filters } from '@/components/dashboard/lesson_category/lesson-categories-filters';
|
||||||
import { LessonCategoriesPagination } from '@/components/dashboard/lesson_category/lesson-categories-pagination';
|
import { LessonCategoriesPagination } from '@/components/dashboard/lesson_category/lesson-categories-pagination';
|
||||||
import { LessonCategoriesSelectionProvider } from '@/components/dashboard/lesson_category/lesson-categories-selection-context';
|
import { LessonCategoriesSelectionProvider } from '@/components/dashboard/lesson_category/lesson-categories-selection-context';
|
||||||
import { LessonCategoriesTable } from '@/components/dashboard/lesson_category/lesson-categories-table';
|
import { LessonCategoriesTable } from '@/components/dashboard/lesson_category/lesson-categories-table';
|
||||||
|
import FormLoading from '@/components/loading';
|
||||||
|
|
||||||
import { lessonCategoriesSampleData } from './lesson-categories-sample-data';
|
import { lessonCategoriesSampleData } from './lesson-categories-sample-data';
|
||||||
|
|
||||||
@@ -23,19 +37,51 @@ interface PageProps {
|
|||||||
email?: string;
|
email?: string;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
sortDir?: 'asc' | 'desc';
|
sortDir?: 'asc' | 'desc';
|
||||||
status?: string;
|
spStatus?: string;
|
||||||
name?: string;
|
spName?: string;
|
||||||
visible?: string;
|
spVisible?: string;
|
||||||
type?: string;
|
spType?: string;
|
||||||
//
|
//
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
||||||
const { email, phone, sortDir, status } = searchParams;
|
const { t } = useTranslation();
|
||||||
|
const { email, phone, sortDir, spStatus } = 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 sortedLessonCategories = applySort(lessonCategoriesSampleData, sortDir);
|
const sortedLessonCategories = applySort(lessonCategoriesSampleData, sortDir);
|
||||||
const filteredLessonCategories = applyFilters(sortedLessonCategories, { email, phone, status });
|
const filteredLessonCategories = applyFilters(sortedLessonCategories, { email, phone, status: spStatus });
|
||||||
|
//
|
||||||
|
const [isLoadingAddPage, setIsLoadingAddPage] = React.useState<boolean>(false);
|
||||||
|
const [lessonCategoriesData, setLessonCategoriesData] = React.useState<LessonCategory[]>([]);
|
||||||
|
const reloadRows = () => {
|
||||||
|
pb.collection(COL_LESSON_CATEGORIES)
|
||||||
|
.getList(currentPage, rowsPerPage, {})
|
||||||
|
.then((lessonCategories: ListResult<RecordModel>) => {
|
||||||
|
// console.log(lessonTypes);
|
||||||
|
const { items, page, perPage, totalItems, totalPages } = lessonCategories;
|
||||||
|
const tempLessonCategories: LessonCategory[] = items.map((item) => {
|
||||||
|
return { ...defaultLessonCategory, ...item };
|
||||||
|
});
|
||||||
|
|
||||||
|
setLessonCategoriesData(tempLessonCategories);
|
||||||
|
setRecordCount(totalItems);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
logger.error(err);
|
||||||
|
toast(t('dashboard.lessonTypes.list.error'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
reloadRows();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (lessonCategoriesData.length < 1) return <FormLoading />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -49,17 +95,25 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
|||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={3} sx={{ alignItems: 'flex-start' }}>
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={3} sx={{ alignItems: 'flex-start' }}>
|
||||||
<Box sx={{ flex: '1 1 auto' }}>
|
<Box sx={{ flex: '1 1 auto' }}>
|
||||||
<Typography variant="h4">Lesson Categories</Typography>
|
<Typography variant="h4">{t('Lesson Categories')}</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||||
<Button startIcon={<PlusIcon />} variant="contained">
|
<LoadingButton
|
||||||
Add
|
loading={isLoadingAddPage}
|
||||||
</Button>
|
onClick={(): void => {
|
||||||
|
setIsLoadingAddPage(true);
|
||||||
|
router.push(paths.dashboard.lesson_categories.create);
|
||||||
|
}}
|
||||||
|
startIcon={<PlusIcon />}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
{t('dashboard.lessonCategories.add')}
|
||||||
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
<LessonCategoriesSelectionProvider lessonCategories={filteredLessonCategories}>
|
<LessonCategoriesSelectionProvider lessonCategories={filteredLessonCategories}>
|
||||||
<Card>
|
<Card>
|
||||||
<LessonCategoriesFilters filters={{ email, phone, status }} sortDir={sortDir} />
|
<LessonCategoriesFilters filters={{ email, phone, status: spStatus }} sortDir={sortDir} />
|
||||||
<Divider />
|
<Divider />
|
||||||
<Box sx={{ overflowX: 'auto' }}>
|
<Box sx={{ overflowX: 'auto' }}>
|
||||||
<LessonCategoriesTable rows={filteredLessonCategories} />
|
<LessonCategoriesTable rows={filteredLessonCategories} />
|
||||||
|
@@ -11,7 +11,6 @@ import Stack from '@mui/material/Stack';
|
|||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
|
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
|
||||||
import type { ListResult, RecordModel } from 'pocketbase';
|
import type { ListResult, RecordModel } from 'pocketbase';
|
||||||
import PocketBase from 'pocketbase';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { paths } from '@/paths';
|
import { paths } from '@/paths';
|
||||||
@@ -88,19 +87,6 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
|||||||
logger.error(err);
|
logger.error(err);
|
||||||
toast(t('dashboard.lessonTypes.list.error'));
|
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(() => {
|
React.useEffect(() => {
|
||||||
@@ -178,7 +164,7 @@ function applySort(row: LessonType[], sortDir: 'asc' | 'desc' | undefined): Less
|
|||||||
|
|
||||||
function applyFilters(
|
function applyFilters(
|
||||||
row: LessonType[],
|
row: LessonType[],
|
||||||
{ email, phone, spStatus: status, spName: name, spVisible: visible }: Filters
|
{ email, phone, spStatus: status, spName: name, spVisible }: Filters
|
||||||
): LessonType[] {
|
): LessonType[] {
|
||||||
return row.filter((item) => {
|
return row.filter((item) => {
|
||||||
if (email) {
|
if (email) {
|
||||||
@@ -205,8 +191,8 @@ function applyFilters(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visible) {
|
if (spVisible) {
|
||||||
if (!item.visible?.toLowerCase().includes(visible.toLowerCase())) {
|
if (!item.visible?.toLowerCase().includes(spVisible.toLowerCase())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,18 @@
|
|||||||
|
import { dayjs } from '@/lib/dayjs';
|
||||||
|
|
||||||
export interface LessonCategory {
|
export interface LessonCategory {
|
||||||
|
id: string;
|
||||||
|
isEmpty?: boolean;
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
email: string;
|
||||||
|
phone?: string;
|
||||||
|
quota: number;
|
||||||
|
status: 'pending' | 'active' | 'blocked' | 'NA';
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DBLessonCategory {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
@@ -8,3 +22,18 @@ export interface LessonCategory {
|
|||||||
status: 'pending' | 'active' | 'blocked';
|
status: 'pending' | 'active' | 'blocked';
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultLessonCategory: LessonCategory = {
|
||||||
|
isEmpty: true,
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
quota: -Infinity,
|
||||||
|
status: 'NA',
|
||||||
|
createdAt: dayjs('2099-01-01').toDate(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const emptyLessonCategory: LessonCategory = {
|
||||||
|
...defaultLessonCategory,
|
||||||
|
isEmpty: true,
|
||||||
|
};
|
||||||
|
@@ -20,7 +20,7 @@ import { dayjs } from '@/lib/dayjs';
|
|||||||
import { DataTable } from '@/components/core/data-table';
|
import { DataTable } from '@/components/core/data-table';
|
||||||
import type { ColumnDef } from '@/components/core/data-table';
|
import type { ColumnDef } from '@/components/core/data-table';
|
||||||
|
|
||||||
import { LessonCategory } from './interfaces';
|
import type { LessonCategory } from './interfaces';
|
||||||
import { useLessonCategoriesSelection } from './lesson-categories-selection-context';
|
import { useLessonCategoriesSelection } from './lesson-categories-selection-context';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@@ -73,6 +73,7 @@ const columns = [
|
|||||||
active: { label: 'Active', icon: <CheckCircleIcon color="var(--mui-palette-success-main)" weight="fill" /> },
|
active: { label: 'Active', icon: <CheckCircleIcon color="var(--mui-palette-success-main)" weight="fill" /> },
|
||||||
blocked: { label: 'Blocked', icon: <MinusIcon color="var(--mui-palette-error-main)" /> },
|
blocked: { label: 'Blocked', icon: <MinusIcon color="var(--mui-palette-error-main)" /> },
|
||||||
pending: { label: 'Pending', icon: <ClockIcon color="var(--mui-palette-warning-main)" weight="fill" /> },
|
pending: { label: 'Pending', icon: <ClockIcon color="var(--mui-palette-warning-main)" weight="fill" /> },
|
||||||
|
NA: { label: 'NA', icon: <ClockIcon color="var(--mui-palette-warning-main)" weight="fill" /> },
|
||||||
} as const;
|
} as const;
|
||||||
const { label, icon } = mapping[row.status] ?? { label: 'Unknown', icon: null };
|
const { label, icon } = mapping[row.status] ?? { label: 'Unknown', icon: null };
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
const COL_LESSON_TYPES = 'LessonsTypes';
|
const COL_LESSON_TYPES = 'LessonsTypes';
|
||||||
|
const COL_LESSON_CATEGORIES = 'LessonsCategories';
|
||||||
const NO_VALUE = 'NO_VALUE';
|
const NO_VALUE = 'NO_VALUE';
|
||||||
const NO_NUM = -Infinity;
|
const NO_NUM = -Infinity;
|
||||||
|
|
||||||
export { COL_LESSON_TYPES, NO_VALUE, NO_NUM };
|
export { COL_LESSON_TYPES, NO_VALUE, NO_NUM, COL_LESSON_CATEGORIES };
|
||||||
|
Reference in New Issue
Block a user