update,
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
import Box from '@mui/material/Box';
|
||||
import Link from '@mui/material/Link';
|
||||
import Stack from '@mui/material/Stack';
|
||||
@@ -47,7 +47,7 @@ export default function Page(): React.JSX.Element {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (catId) {
|
||||
pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES)
|
||||
pb.collection(COL_QUIZ_LP_CATEGORIES)
|
||||
.getOne(catId)
|
||||
.then((model: RecordModel) => {
|
||||
setShowLessonCategory({ ...defaultLpCategory, ...model });
|
||||
|
@@ -6,7 +6,7 @@
|
||||
//
|
||||
import * as React from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import Box from '@mui/material/Box';
|
||||
import Card from '@mui/material/Card';
|
||||
@@ -56,7 +56,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
||||
const reloadRows = async (): Promise<void> => {
|
||||
try {
|
||||
const models: ListResult<RecordModel> = await pb
|
||||
.collection(COL_LISTENINGS_PRACTICE_CATEGORIES)
|
||||
.collection(COL_QUIZ_LP_CATEGORIES)
|
||||
.getList(currentPage + 1, rowsPerPage, {});
|
||||
const { items, totalItems } = models;
|
||||
const tempLessonTypes: LpCategory[] = items.map((lt) => {
|
||||
|
@@ -3,7 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
import Box from '@mui/material/Box';
|
||||
import Link from '@mui/material/Link';
|
||||
import Stack from '@mui/material/Stack';
|
||||
@@ -47,7 +47,7 @@ export default function Page(): React.JSX.Element {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (catId) {
|
||||
pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES)
|
||||
pb.collection(COL_QUIZ_MF_CATEGORIES)
|
||||
.getOne(catId)
|
||||
.then((model: RecordModel) => {
|
||||
setShowLessonCategory({ ...defaultLpCategory, ...model });
|
||||
|
@@ -6,7 +6,7 @@
|
||||
//
|
||||
import * as React from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES, COL_MF_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES, COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import Box from '@mui/material/Box';
|
||||
import Card from '@mui/material/Card';
|
||||
@@ -56,7 +56,7 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
||||
const reloadRows = async (): Promise<void> => {
|
||||
try {
|
||||
const models: ListResult<RecordModel> = await pb
|
||||
.collection(COL_MF_CATEGORIES)
|
||||
.collection(COL_QUIZ_MF_CATEGORIES)
|
||||
.getList(currentPage + 1, rowsPerPage, {});
|
||||
const { items, totalItems } = models;
|
||||
const tempLessonTypes: LpCategory[] = items.map((lt) => {
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
@@ -27,13 +28,17 @@ import type { ColumnDef } from '@/components/core/data-table';
|
||||
|
||||
import ConfirmDeleteModal from './confirm-delete-modal';
|
||||
import { useLessonCategoriesSelection } from './lesson-categories-selection-context';
|
||||
import { LessonCategory } from './type';
|
||||
import type { LessonCategory } from './type';
|
||||
|
||||
function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonCategory>[] {
|
||||
return [
|
||||
{
|
||||
formatter: (row): React.JSX.Element => (
|
||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
sx={{ alignItems: 'center' }}
|
||||
>
|
||||
<Link
|
||||
color="inherit"
|
||||
component={RouterLink}
|
||||
@@ -41,7 +46,11 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonC
|
||||
sx={{ whiteSpace: 'nowrap' }}
|
||||
variant="subtitle2"
|
||||
>
|
||||
<Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
sx={{ alignItems: 'center' }}
|
||||
>
|
||||
<Avatar
|
||||
src={`http://127.0.0.1:8090/api/files/${row.collectionId}/${row.id}/${row.cat_image}`}
|
||||
variant="rounded"
|
||||
@@ -50,7 +59,10 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonC
|
||||
</Avatar>{' '}
|
||||
<div>
|
||||
<Box sx={{ whiteSpace: 'nowrap' }}>{row.cat_name}</Box>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
<Typography
|
||||
color="text.secondary"
|
||||
variant="body2"
|
||||
>
|
||||
slug: {row.cat_name}
|
||||
</Typography>
|
||||
</div>
|
||||
@@ -63,9 +75,20 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonC
|
||||
},
|
||||
{
|
||||
formatter: (row): React.JSX.Element => (
|
||||
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
|
||||
<LinearProgress sx={{ flex: '1 1 auto' }} value={row.quota} variant="determinate" />
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ alignItems: 'center' }}
|
||||
>
|
||||
<LinearProgress
|
||||
sx={{ flex: '1 1 auto' }}
|
||||
value={row.quota}
|
||||
variant="determinate"
|
||||
/>
|
||||
<Typography
|
||||
color="text.secondary"
|
||||
variant="body2"
|
||||
>
|
||||
{new Intl.NumberFormat('en-US', { style: 'percent', maximumFractionDigits: 2 }).format(row.quota / 100)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
@@ -77,13 +100,36 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonC
|
||||
{
|
||||
formatter: (row): React.JSX.Element => {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { t } = useTranslation();
|
||||
|
||||
const mapping = {
|
||||
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)" /> },
|
||||
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" /> },
|
||||
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;
|
||||
const { label, icon } = mapping[row.status] ?? { label: 'Unknown', icon: null };
|
||||
|
||||
@@ -111,23 +157,27 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LessonC
|
||||
},
|
||||
{
|
||||
formatter: (row): React.JSX.Element => (
|
||||
<Stack direction="row" spacing={1}>
|
||||
<IconButton
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
>
|
||||
<LoadingButton
|
||||
//
|
||||
color="secondary"
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.lesson_categories.details(row.id)}
|
||||
>
|
||||
<PencilSimpleIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
<PencilSimpleIcon size={24} />
|
||||
</LoadingButton>
|
||||
<LoadingButton
|
||||
color="error"
|
||||
disabled={row.isEmpty}
|
||||
onClick={() => {
|
||||
handleDeleteClick(row.id);
|
||||
}}
|
||||
>
|
||||
<TrashSimpleIcon />
|
||||
</IconButton>
|
||||
<TrashSimpleIcon size={24} />
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
),
|
||||
name: 'Actions',
|
||||
@@ -157,7 +207,12 @@ export function LessonCategoriesTable({ rows, reloadRows }: LessonCategoriesTabl
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ConfirmDeleteModal idToDelete={idToDelete} open={open} reloadRows={reloadRows} setOpen={setOpen} />
|
||||
<ConfirmDeleteModal
|
||||
idToDelete={idToDelete}
|
||||
open={open}
|
||||
reloadRows={reloadRows}
|
||||
setOpen={setOpen}
|
||||
/>
|
||||
<DataTable<LessonCategory>
|
||||
columns={columns(handleDeleteClick)}
|
||||
onDeselectAll={deselectAll}
|
||||
@@ -174,7 +229,11 @@ export function LessonCategoriesTable({ rows, reloadRows }: LessonCategoriesTabl
|
||||
/>
|
||||
{!rows.length ? (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Typography color="text.secondary" sx={{ textAlign: 'center' }} variant="body2">
|
||||
<Typography
|
||||
color="text.secondary"
|
||||
sx={{ textAlign: 'center' }}
|
||||
variant="body2"
|
||||
>
|
||||
{/* TODO: use hyphen here */}
|
||||
{t('no-lesson-categories-found')}
|
||||
</Typography>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { COL_LESSON_TYPES } from '@/constants';
|
||||
import deleteQuizListening from '@/db/QuizListenings/Delete';
|
||||
import deleteQuizLPCategories from '@/db/QuizListenings/Delete';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { Button, Container, Modal, Paper } from '@mui/material';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
@@ -17,8 +17,6 @@ import { useTranslation } from 'react-i18next';
|
||||
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,
|
||||
setOpen,
|
||||
@@ -45,30 +43,19 @@ export default function ConfirmDeleteModal({
|
||||
transform: 'translate(-50%, -50%)',
|
||||
};
|
||||
|
||||
function performDelete(id: string): Promise<void> {
|
||||
return deleteQuizListening(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);
|
||||
performDelete(idToDelete)
|
||||
deleteQuizLPCategories(idToDelete)
|
||||
.then(() => {
|
||||
reloadRows();
|
||||
handleClose();
|
||||
toast(t('delete.success'));
|
||||
})
|
||||
.catch((err) => {
|
||||
// console.error(err)
|
||||
logger.error(err);
|
||||
toast(t('dashboard.lessonTypes.delete.error'));
|
||||
toast(t('delete.error'));
|
||||
})
|
||||
.finally(() => {
|
||||
setIsDeleteing(false);
|
||||
@@ -87,19 +74,33 @@ export default function ConfirmDeleteModal({
|
||||
<Box sx={style}>
|
||||
<Container maxWidth="sm">
|
||||
<Paper sx={{ border: '1px solid var(--mui-palette-divider)', boxShadow: 'var(--mui-shadows-16)' }}>
|
||||
<Stack direction="row" spacing={2} sx={{ display: 'flex', p: 3 }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ display: 'flex', p: 3 }}
|
||||
>
|
||||
<Avatar sx={{ bgcolor: 'var(--mui-palette-error-50)', color: 'var(--mui-palette-error-main)' }}>
|
||||
<NoteIcon fontSize="var(--Icon-fontSize)" />
|
||||
</Avatar>
|
||||
<Stack spacing={3}>
|
||||
<Stack spacing={1}>
|
||||
<Typography variant="h5">{t('Delete Lesson Type ?')}</Typography>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
<Typography
|
||||
color="text.secondary"
|
||||
variant="body2"
|
||||
>
|
||||
{t('Are you sure you want to delete lesson type ?')}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" spacing={2} sx={{ justifyContent: 'flex-end' }}>
|
||||
<Button color="secondary" onClick={handleClose}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ justifyContent: 'flex-end' }}
|
||||
>
|
||||
<Button
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<LoadingButton
|
||||
|
@@ -20,11 +20,9 @@ import Typography from '@mui/material/Typography';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { paths } from '@/paths';
|
||||
// import { pb } from '@/lib/pb';
|
||||
import { FilterButton, FilterPopover, useFilterContext } from '@/components/core/filter-button';
|
||||
import { Option } from '@/components/core/option';
|
||||
|
||||
// import { LessonCategory } from '../lp_categories/type';
|
||||
import { useLpCategoriesSelection } from './lp-categories-selection-context';
|
||||
import { LpCategory } from './type';
|
||||
|
||||
@@ -196,10 +194,21 @@ export function LpCategoriesFilters({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs onChange={handleVisibleChange} sx={{ px: 3 }} value={visible ?? ''} variant="scrollable">
|
||||
<Tabs
|
||||
onChange={handleVisibleChange}
|
||||
sx={{ px: 3 }}
|
||||
value={visible ?? ''}
|
||||
variant="scrollable"
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<Tab
|
||||
icon={<Chip label={tab.count} size="small" variant="soft" />}
|
||||
icon={
|
||||
<Chip
|
||||
label={tab.count}
|
||||
size="small"
|
||||
variant="soft"
|
||||
/>
|
||||
}
|
||||
iconPosition="end"
|
||||
key={tab.value}
|
||||
label={tab.label}
|
||||
@@ -210,8 +219,16 @@ export function LpCategoriesFilters({
|
||||
))}
|
||||
</Tabs>
|
||||
<Divider />
|
||||
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flexWrap: 'wrap', px: 3, py: 2 }}>
|
||||
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: '1 1 auto', flexWrap: 'wrap' }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ alignItems: 'center', flexWrap: 'wrap', px: 3, py: 2 }}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ alignItems: 'center', flex: '1 1 auto', flexWrap: 'wrap' }}
|
||||
>
|
||||
<FilterButton
|
||||
displayValue={name}
|
||||
label={t('Name')}
|
||||
@@ -241,16 +258,31 @@ export function LpCategoriesFilters({
|
||||
{hasFilters ? <Button onClick={handleClearFilters}>{t('Clear filters')}</Button> : null}
|
||||
</Stack>
|
||||
{selection.selectedAny ? (
|
||||
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ alignItems: 'center' }}
|
||||
>
|
||||
<Typography
|
||||
color="text.secondary"
|
||||
variant="body2"
|
||||
>
|
||||
{selection.selected.size} {t('selected')}
|
||||
</Typography>
|
||||
<Button color="error" variant="contained">
|
||||
<Button
|
||||
color="error"
|
||||
variant="contained"
|
||||
>
|
||||
{t('Delete')}
|
||||
</Button>
|
||||
</Stack>
|
||||
) : null}
|
||||
<Select name="sort" onChange={handleSortChange} sx={{ maxWidth: '100%', width: '120px' }} value={sortDir}>
|
||||
<Select
|
||||
name="sort"
|
||||
onChange={handleSortChange}
|
||||
sx={{ maxWidth: '100%', width: '120px' }}
|
||||
value={sortDir}
|
||||
>
|
||||
<Option value="desc">{t('Newest')}</Option>
|
||||
<Option value="asc">{t('Oldest')}</Option>
|
||||
</Select>
|
||||
@@ -269,7 +301,12 @@ function TypeFilterPopover(): React.JSX.Element {
|
||||
}, [initialValue]);
|
||||
|
||||
return (
|
||||
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title={t('Filter by type')}>
|
||||
<FilterPopover
|
||||
anchorEl={anchorEl}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
title={t('Filter by type')}
|
||||
>
|
||||
<FormControl>
|
||||
<OutlinedInput
|
||||
onChange={(event) => {
|
||||
@@ -305,7 +342,12 @@ function NameFilterPopover(): React.JSX.Element {
|
||||
}, [initialValue]);
|
||||
|
||||
return (
|
||||
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title={t('Filter by name')}>
|
||||
<FilterPopover
|
||||
anchorEl={anchorEl}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
title={t('Filter by name')}
|
||||
>
|
||||
<FormControl>
|
||||
<OutlinedInput
|
||||
onChange={(event) => {
|
||||
@@ -341,7 +383,12 @@ function EmailFilterPopover(): React.JSX.Element {
|
||||
}, [initialValue]);
|
||||
|
||||
return (
|
||||
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title="Filter by email">
|
||||
<FilterPopover
|
||||
anchorEl={anchorEl}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
title="Filter by email"
|
||||
>
|
||||
<FormControl>
|
||||
<OutlinedInput
|
||||
onChange={(event) => {
|
||||
@@ -377,7 +424,12 @@ function PhoneFilterPopover(): React.JSX.Element {
|
||||
}, [initialValue]);
|
||||
|
||||
return (
|
||||
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title="Filter by phone number">
|
||||
<FilterPopover
|
||||
anchorEl={anchorEl}
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
title="Filter by phone number"
|
||||
>
|
||||
<FormControl>
|
||||
<OutlinedInput
|
||||
onChange={(event) => {
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
@@ -159,22 +160,23 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LpCateg
|
||||
direction="row"
|
||||
spacing={1}
|
||||
>
|
||||
<IconButton
|
||||
<LoadingButton
|
||||
//
|
||||
color="secondary"
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.lp_categories.details(row.id)}
|
||||
>
|
||||
<PencilSimpleIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
<PencilSimpleIcon size={24} />
|
||||
</LoadingButton>
|
||||
<LoadingButton
|
||||
color="error"
|
||||
disabled={row.isEmpty}
|
||||
onClick={() => {
|
||||
handleDeleteClick(row.id);
|
||||
}}
|
||||
>
|
||||
<TrashSimpleIcon />
|
||||
</IconButton>
|
||||
<TrashSimpleIcon size={24} />
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
),
|
||||
name: 'Actions',
|
||||
|
@@ -3,7 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { Avatar, Divider, MenuItem, Select } from '@mui/material';
|
||||
@@ -101,7 +101,7 @@ export function LpCategoryCreateForm(): React.JSX.Element {
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).create(payload);
|
||||
const result = await pb.collection(COL_QUIZ_LP_CATEGORIES).create(payload);
|
||||
|
||||
logger.debug(result);
|
||||
toast.success(t('create.success'));
|
||||
|
@@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
//
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
//
|
||||
@@ -129,7 +129,7 @@ export function LpCategoryEditForm(): React.JSX.Element {
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).update(catId, tempUpdate);
|
||||
const result = await pb.collection(COL_QUIZ_LP_CATEGORIES).update(catId, tempUpdate);
|
||||
logger.debug(result);
|
||||
toast.success(t('edit.success'));
|
||||
router.push(paths.dashboard.lp_categories.list);
|
||||
@@ -171,7 +171,7 @@ export function LpCategoryEditForm(): React.JSX.Element {
|
||||
setShowLoading(true);
|
||||
|
||||
try {
|
||||
const result = await pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).getOne(id);
|
||||
const result = await pb.collection(COL_QUIZ_LP_CATEGORIES).getOne(id);
|
||||
|
||||
reset({ ...defaultValues, ...result, init_answer: JSON.stringify(result.init_answer) });
|
||||
setTextDescription(result.description);
|
||||
|
@@ -1,9 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { COL_LESSON_TYPES } from '@/constants';
|
||||
import deleteQuizListening from '@/db/QuizListenings/Delete';
|
||||
import deleteQuizMFCategories from '@/db/QuizMFCategories/Delete';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { Button, Container, Modal, Paper } from '@mui/material';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
@@ -17,8 +15,6 @@ import { useTranslation } from 'react-i18next';
|
||||
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,
|
||||
setOpen,
|
||||
@@ -45,30 +41,19 @@ export default function ConfirmDeleteModal({
|
||||
transform: 'translate(-50%, -50%)',
|
||||
};
|
||||
|
||||
function performDelete(id: string): Promise<void> {
|
||||
return deleteQuizListening(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);
|
||||
performDelete(idToDelete)
|
||||
deleteQuizMFCategories(idToDelete)
|
||||
.then(() => {
|
||||
reloadRows();
|
||||
handleClose();
|
||||
toast(t('delete.success'));
|
||||
})
|
||||
.catch((err) => {
|
||||
// console.error(err)
|
||||
logger.error(err);
|
||||
toast(t('dashboard.lessonTypes.delete.error'));
|
||||
toast(t('delete.error'));
|
||||
})
|
||||
.finally(() => {
|
||||
setIsDeleteing(false);
|
||||
@@ -87,19 +72,33 @@ export default function ConfirmDeleteModal({
|
||||
<Box sx={style}>
|
||||
<Container maxWidth="sm">
|
||||
<Paper sx={{ border: '1px solid var(--mui-palette-divider)', boxShadow: 'var(--mui-shadows-16)' }}>
|
||||
<Stack direction="row" spacing={2} sx={{ display: 'flex', p: 3 }}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ display: 'flex', p: 3 }}
|
||||
>
|
||||
<Avatar sx={{ bgcolor: 'var(--mui-palette-error-50)', color: 'var(--mui-palette-error-main)' }}>
|
||||
<NoteIcon fontSize="var(--Icon-fontSize)" />
|
||||
</Avatar>
|
||||
<Stack spacing={3}>
|
||||
<Stack spacing={1}>
|
||||
<Typography variant="h5">{t('Delete Lesson Type ?')}</Typography>
|
||||
<Typography color="text.secondary" variant="body2">
|
||||
<Typography
|
||||
color="text.secondary"
|
||||
variant="body2"
|
||||
>
|
||||
{t('Are you sure you want to delete lesson type ?')}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" spacing={2} sx={{ justifyContent: 'flex-end' }}>
|
||||
<Button color="secondary" onClick={handleClose}>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
sx={{ justifyContent: 'flex-end' }}
|
||||
>
|
||||
<Button
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<LoadingButton
|
||||
|
@@ -3,9 +3,9 @@
|
||||
import * as React from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
// import { COL_LESSON_CATEGORIES } from '@/constants';
|
||||
import GetAllCount from '@/db/QuizListenings/GetAllCount';
|
||||
import GetHiddenCount from '@/db/QuizListenings/GetHiddenCount';
|
||||
import GetVisibleCount from '@/db/QuizListenings/GetVisibleCount';
|
||||
import GetAllCount from '@/db/QuizMFCategories/GetAllCount';
|
||||
import GetHiddenCount from '@/db/QuizMFCategories/GetHiddenCount';
|
||||
import GetVisibleCount from '@/db/QuizMFCategories/GetVisibleCount';
|
||||
import Button from '@mui/material/Button';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Divider from '@mui/material/Divider';
|
||||
@@ -20,13 +20,11 @@ import Typography from '@mui/material/Typography';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { paths } from '@/paths';
|
||||
// import { pb } from '@/lib/pb';
|
||||
import { FilterButton, FilterPopover, useFilterContext } from '@/components/core/filter-button';
|
||||
import { Option } from '@/components/core/option';
|
||||
|
||||
// import { LessonCategory } from '../mf_categories/type';
|
||||
import { useLpCategoriesSelection } from './mf-categories-selection-context';
|
||||
import { LpCategory } from './type';
|
||||
import type { LpCategory } from './type';
|
||||
|
||||
export interface Filters {
|
||||
email?: string;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
@@ -159,22 +160,23 @@ function columns(handleDeleteClick: (testId: string) => void): ColumnDef<LpCateg
|
||||
direction="row"
|
||||
spacing={1}
|
||||
>
|
||||
<IconButton
|
||||
<LoadingButton
|
||||
//
|
||||
color="secondary"
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.mf_categories.details(row.id)}
|
||||
>
|
||||
<PencilSimpleIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
<PencilSimpleIcon size={24} />
|
||||
</LoadingButton>
|
||||
<LoadingButton
|
||||
color="error"
|
||||
disabled={row.isEmpty}
|
||||
onClick={() => {
|
||||
handleDeleteClick(row.id);
|
||||
}}
|
||||
>
|
||||
<TrashSimpleIcon />
|
||||
</IconButton>
|
||||
<TrashSimpleIcon size={24} />
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
),
|
||||
name: 'Actions',
|
||||
|
@@ -3,7 +3,7 @@
|
||||
import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { Avatar, Divider, MenuItem, Select } from '@mui/material';
|
||||
@@ -101,7 +101,7 @@ export function LpCategoryCreateForm(): React.JSX.Element {
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).create(payload);
|
||||
const result = await pb.collection(COL_QUIZ_MF_CATEGORIES).create(payload);
|
||||
|
||||
logger.debug(result);
|
||||
toast.success(t('create.success'));
|
||||
|
@@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
import RouterLink from 'next/link';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
//
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
//
|
||||
@@ -129,7 +129,7 @@ export function LpCategoryEditForm(): React.JSX.Element {
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).update(catId, tempUpdate);
|
||||
const result = await pb.collection(COL_QUIZ_MF_CATEGORIES).update(catId, tempUpdate);
|
||||
logger.debug(result);
|
||||
toast.success(t('edit.success'));
|
||||
router.push(paths.dashboard.mf_categories.list);
|
||||
@@ -171,7 +171,7 @@ export function LpCategoryEditForm(): React.JSX.Element {
|
||||
setShowLoading(true);
|
||||
|
||||
try {
|
||||
const result = await pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).getOne(id);
|
||||
const result = await pb.collection(COL_QUIZ_MF_CATEGORIES).getOne(id);
|
||||
|
||||
reset({ ...defaultValues, ...result, init_answer: JSON.stringify(result.init_answer) });
|
||||
setTextDescription(result.description);
|
||||
|
@@ -1,4 +1,6 @@
|
||||
// RULES: COL_<COLLECTION_NAME> = "<name in dbml file>"
|
||||
// RULES:
|
||||
// COL_<COLLECTION_NAME> = "<table name in dbml file>"
|
||||
// e.g. COL_APPLE = "Apple" table in dbml
|
||||
const COL_LESSON_TYPES = 'LessonsTypes';
|
||||
const COL_LESSON_CATEGORIES = 'LessonsCategories';
|
||||
const NO_VALUE = 'NO_VALUE';
|
||||
@@ -7,10 +9,12 @@ const NS_LESSON_CATEGORY = 'lesson_category';
|
||||
const COL_USERS = 'users';
|
||||
const COL_USER_METAS = 'UserMetas';
|
||||
|
||||
// do not use LP_CATEGORIES
|
||||
const COL_LISTENINGS_PRACTICE_CATEGORIES = 'QuizLPCategories';
|
||||
// RULES:
|
||||
// do not use LP_CATEGORIES anymore
|
||||
const COL_QUIZ_LP_CATEGORIES = 'QuizLPCategories';
|
||||
const COL_QUIZ_LP_QUESTIONS = 'QuizLPQuestions';
|
||||
const COL_MF_CATEGORIES = 'QuizMFCategories';
|
||||
//
|
||||
const COL_QUIZ_MF_CATEGORIES = 'QuizMFCategories';
|
||||
|
||||
export {
|
||||
COL_LESSON_TYPES,
|
||||
@@ -21,9 +25,9 @@ export {
|
||||
COL_USERS,
|
||||
COL_USER_METAS,
|
||||
//
|
||||
COL_LISTENINGS_PRACTICE_CATEGORIES,
|
||||
COL_QUIZ_LP_CATEGORIES,
|
||||
COL_QUIZ_LP_QUESTIONS,
|
||||
//
|
||||
COL_MF_CATEGORIES,
|
||||
COL_QUIZ_MF_CATEGORIES,
|
||||
//
|
||||
};
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default function deleteQuizListening(id: string): Promise<boolean> {
|
||||
return pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).delete(id);
|
||||
export default function deleteQuizLPCategories(id: string): Promise<boolean> {
|
||||
return pb.collection(COL_QUIZ_LP_CATEGORIES).delete(id);
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default function getAllQuizListenings(): Promise<RecordModel[]> {
|
||||
return pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).getFullList();
|
||||
return pb.collection(COL_QUIZ_LP_CATEGORIES).getFullList();
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
// REQ0006
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default async function GetAllCount(): Promise<number> {
|
||||
const { totalItems: count } = await pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).getList(1, 9999, {});
|
||||
const { totalItems: count } = await pb.collection(COL_QUIZ_LP_CATEGORIES).getList(1, 9999, {});
|
||||
return count;
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
import type { RecordModel } from 'pocketbase';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default function getQuizListeningById(id: string): Promise<RecordModel> {
|
||||
return pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).getOne(id);
|
||||
return pb.collection(COL_QUIZ_LP_CATEGORIES).getOne(id);
|
||||
}
|
||||
|
@@ -1,13 +1,11 @@
|
||||
// REQ0006
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default async function GetHiddenCount(): Promise<number> {
|
||||
try {
|
||||
const result = await pb
|
||||
.collection(COL_LISTENINGS_PRACTICE_CATEGORIES)
|
||||
.getList(1, 9999, { filter: 'visible = "hidden"' });
|
||||
const result = await pb.collection(COL_QUIZ_LP_CATEGORIES).getList(1, 9999, { filter: 'visible = "hidden"' });
|
||||
const { totalItems: count } = result;
|
||||
return count;
|
||||
} catch (error) {
|
||||
|
@@ -1,13 +1,11 @@
|
||||
// REQ0006
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default async function GetVisibleCount(): Promise<number> {
|
||||
try {
|
||||
const result = await pb
|
||||
.collection(COL_LISTENINGS_PRACTICE_CATEGORIES)
|
||||
.getList(1, 9999, { filter: 'visible = "visible"' });
|
||||
const result = await pb.collection(COL_QUIZ_LP_CATEGORIES).getList(1, 9999, { filter: 'visible = "visible"' });
|
||||
const { totalItems: count } = result;
|
||||
return count;
|
||||
} catch (error) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { COL_LISTENINGS_PRACTICE_CATEGORIES } from '@/constants';
|
||||
import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
|
||||
import type { ListResult, RecordModel } from 'pocketbase';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
@@ -18,5 +18,5 @@ export default function listWithOption({
|
||||
rowsPerPage,
|
||||
listOption = {},
|
||||
}: ListWithOptionParams): Promise<ListResult<RecordModel>> {
|
||||
return pb.collection(COL_LISTENINGS_PRACTICE_CATEGORIES).getList(currentPage + 1, rowsPerPage, listOption);
|
||||
return pb.collection(COL_QUIZ_LP_CATEGORIES).getList(currentPage + 1, rowsPerPage, listOption);
|
||||
}
|
||||
|
7
002_source/cms/src/db/QuizMFCategories/Delete.tsx
Normal file
7
002_source/cms/src/db/QuizMFCategories/Delete.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default function deleteQuizMFCategories(id: string): Promise<boolean> {
|
||||
return pb.collection(COL_QUIZ_MF_CATEGORIES).delete(id);
|
||||
}
|
9
002_source/cms/src/db/QuizMFCategories/GetAllCount.tsx
Normal file
9
002_source/cms/src/db/QuizMFCategories/GetAllCount.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
// REQ0006
|
||||
import { COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default async function GetAllCount(): Promise<number> {
|
||||
const { totalItems: count } = await pb.collection(COL_QUIZ_MF_CATEGORIES).getList(1, 9999, {});
|
||||
return count;
|
||||
}
|
14
002_source/cms/src/db/QuizMFCategories/GetHiddenCount.tsx
Normal file
14
002_source/cms/src/db/QuizMFCategories/GetHiddenCount.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
// REQ0006
|
||||
import { COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default async function GetHiddenCount(): Promise<number> {
|
||||
try {
|
||||
const result = await pb.collection(COL_QUIZ_MF_CATEGORIES).getList(1, 9999, { filter: 'visible = "hidden"' });
|
||||
const { totalItems: count } = result;
|
||||
return count;
|
||||
} catch (error) {
|
||||
return 0;
|
||||
}
|
||||
}
|
14
002_source/cms/src/db/QuizMFCategories/GetVisibleCount.tsx
Normal file
14
002_source/cms/src/db/QuizMFCategories/GetVisibleCount.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
// REQ0006
|
||||
import { COL_QUIZ_MF_CATEGORIES } from '@/constants';
|
||||
|
||||
import { pb } from '@/lib/pb';
|
||||
|
||||
export default async function GetVisibleCount(): Promise<number> {
|
||||
try {
|
||||
const result = await pb.collection(COL_QUIZ_MF_CATEGORIES).getList(1, 9999, { filter: 'visible = "visible"' });
|
||||
const { totalItems: count } = result;
|
||||
return count;
|
||||
} catch (error) {
|
||||
return 0;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user