update,
This commit is contained in:
36
002_source/cms/src/app/dashboard/SampleEvents.tsx
Normal file
36
002_source/cms/src/app/dashboard/SampleEvents.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { dayjs } from '@/lib/dayjs';
|
||||||
|
|
||||||
|
export interface Event {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
export const events: Event[] = [
|
||||||
|
{
|
||||||
|
id: 'EV-004',
|
||||||
|
title: 'Meeting with partners',
|
||||||
|
description: '17:00 to 18:00',
|
||||||
|
createdAt: dayjs().add(1, 'day').toDate(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'EV-003',
|
||||||
|
title: 'Interview with Jonas',
|
||||||
|
description: '15:30 to 16:45',
|
||||||
|
createdAt: dayjs().add(4, 'day').toDate(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'EV-002',
|
||||||
|
title: "Doctor's appointment",
|
||||||
|
description: '12:30 to 15:30',
|
||||||
|
createdAt: dayjs().add(4, 'day').toDate(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'EV-001',
|
||||||
|
title: 'Weekly meeting',
|
||||||
|
description: '09:00 to 09:30',
|
||||||
|
createdAt: dayjs().add(7, 'day').toDate(),
|
||||||
|
},
|
||||||
|
];
|
42
002_source/cms/src/app/dashboard/SampleMessages.tsx
Normal file
42
002_source/cms/src/app/dashboard/SampleMessages.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { dayjs } from '@/lib/dayjs';
|
||||||
|
|
||||||
|
interface Message {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
author: { name: string; avatar?: string; status?: 'online' | 'offline' | 'away' | 'busy' };
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
export const messages: Message[] = [
|
||||||
|
{
|
||||||
|
id: 'MSG-001',
|
||||||
|
content: 'Hello, we spoke earlier on the phone',
|
||||||
|
author: { name: 'Alcides Antonio', avatar: '/assets/avatar-10.png', status: 'online' },
|
||||||
|
createdAt: dayjs().subtract(2, 'minute').toDate(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'MSG-002',
|
||||||
|
content: 'Is the job still available?',
|
||||||
|
author: { name: 'Marcus Finn', avatar: '/assets/avatar-9.png', status: 'offline' },
|
||||||
|
createdAt: dayjs().subtract(56, 'minute').toDate(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'MSG-003',
|
||||||
|
content: "What is a screening task? I'd like to",
|
||||||
|
author: { name: 'Carson Darrin', avatar: '/assets/avatar-3.png', status: 'online' },
|
||||||
|
createdAt: dayjs().subtract(3, 'hour').subtract(23, 'minute').toDate(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'MSG-004',
|
||||||
|
content: 'Still waiting for feedback',
|
||||||
|
author: { name: 'Fran Perez', avatar: '/assets/avatar-5.png', status: 'online' },
|
||||||
|
createdAt: dayjs().subtract(8, 'hour').subtract(6, 'minute').toDate(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'MSG-005',
|
||||||
|
content: 'Need more information about campaigns',
|
||||||
|
author: { name: 'Jie Yan', avatar: '/assets/avatar-8.png', status: 'offline' },
|
||||||
|
createdAt: dayjs().subtract(10, 'hour').subtract(18, 'minute').toDate(),
|
||||||
|
},
|
||||||
|
];
|
51
002_source/cms/src/app/dashboard/SamplesubScriptions.tsx
Normal file
51
002_source/cms/src/app/dashboard/SamplesubScriptions.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
'use client';
|
||||||
|
interface Subscription {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
costs: string;
|
||||||
|
billingCycle: string;
|
||||||
|
status: 'paid' | 'canceled' | 'expiring';
|
||||||
|
}
|
||||||
|
export const SamplesubScriptions: Subscription[] = [
|
||||||
|
{
|
||||||
|
id: 'supabase',
|
||||||
|
title: 'Supabase',
|
||||||
|
icon: '/assets/company-avatar-5.png',
|
||||||
|
costs: '$599',
|
||||||
|
billingCycle: 'year',
|
||||||
|
status: 'paid',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'vercel',
|
||||||
|
title: 'Vercel',
|
||||||
|
icon: '/assets/company-avatar-4.png',
|
||||||
|
costs: '$20',
|
||||||
|
billingCycle: 'month',
|
||||||
|
status: 'expiring',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'auth0',
|
||||||
|
title: 'Auth0',
|
||||||
|
icon: '/assets/company-avatar-3.png',
|
||||||
|
costs: '$20-80',
|
||||||
|
billingCycle: 'month',
|
||||||
|
status: 'canceled',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'google_cloud',
|
||||||
|
title: 'Google Cloud',
|
||||||
|
icon: '/assets/company-avatar-2.png',
|
||||||
|
costs: '$100-200',
|
||||||
|
billingCycle: 'month',
|
||||||
|
status: 'paid',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'stripe',
|
||||||
|
title: 'Stripe',
|
||||||
|
icon: '/assets/company-avatar-1.png',
|
||||||
|
costs: '$70',
|
||||||
|
billingCycle: 'month',
|
||||||
|
status: 'paid',
|
||||||
|
},
|
||||||
|
];
|
@@ -1,7 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
// import type { Metadata } from 'next';
|
import GetAllLessonCategoriesCount from '@/db/LessonCategories/GetAllCount';
|
||||||
|
import GetAllLessonTypesCount from '@/db/LessonTypes/GetAllCount';
|
||||||
|
import GetAllUsersCount from '@/db/Users/GetAllCount';
|
||||||
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 Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
@@ -11,14 +13,12 @@ import { ArrowRight as ArrowRightIcon } from '@phosphor-icons/react/dist/ssr/Arr
|
|||||||
import { Briefcase as BriefcaseIcon } from '@phosphor-icons/react/dist/ssr/Briefcase';
|
import { Briefcase as BriefcaseIcon } from '@phosphor-icons/react/dist/ssr/Briefcase';
|
||||||
import { FileCode as FileCodeIcon } from '@phosphor-icons/react/dist/ssr/FileCode';
|
import { FileCode as FileCodeIcon } from '@phosphor-icons/react/dist/ssr/FileCode';
|
||||||
import { Info as InfoIcon } from '@phosphor-icons/react/dist/ssr/Info';
|
import { Info as InfoIcon } from '@phosphor-icons/react/dist/ssr/Info';
|
||||||
|
// import type { Metadata } from 'next';
|
||||||
import { ListChecks as ListChecksIcon } from '@phosphor-icons/react/dist/ssr/ListChecks';
|
import { ListChecks as ListChecksIcon } from '@phosphor-icons/react/dist/ssr/ListChecks';
|
||||||
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
|
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
|
||||||
import { Users as UsersIcon } from '@phosphor-icons/react/dist/ssr/Users';
|
|
||||||
import { Warning as WarningIcon } from '@phosphor-icons/react/dist/ssr/Warning';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
// import { config } from '@/config';
|
// import { config } from '@/config';
|
||||||
import { dayjs } from '@/lib/dayjs';
|
|
||||||
import { AppChat } from '@/components/dashboard/overview/app-chat';
|
import { AppChat } from '@/components/dashboard/overview/app-chat';
|
||||||
import { AppLimits } from '@/components/dashboard/overview/app-limits';
|
import { AppLimits } from '@/components/dashboard/overview/app-limits';
|
||||||
import { AppUsage } from '@/components/dashboard/overview/app-usage';
|
import { AppUsage } from '@/components/dashboard/overview/app-usage';
|
||||||
@@ -27,13 +27,27 @@ import { HelperWidget } from '@/components/dashboard/overview/helper-widget';
|
|||||||
import { Subscriptions } from '@/components/dashboard/overview/subscriptions';
|
import { Subscriptions } from '@/components/dashboard/overview/subscriptions';
|
||||||
import { Summary } from '@/components/dashboard/overview/summary';
|
import { Summary } from '@/components/dashboard/overview/summary';
|
||||||
|
|
||||||
// TODO: remove me
|
import ActiveUserCount from '../../components/dashboard/overview/summary/ActiveUserCount';
|
||||||
// export const metadata = { title: `Overview | Dashboard | ${config.site.name}` } satisfies Metadata;
|
import { events as SampleEvents } from './SampleEvents';
|
||||||
|
import { messages as SampleMessages } from './SampleMessages';
|
||||||
|
import { SamplesubScriptions } from './SamplesubScriptions';
|
||||||
|
|
||||||
export default function Page(): React.JSX.Element {
|
export default function Page(): React.JSX.Element {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
// return <>helloworld</>;
|
const [lessonTypesCount, setLessonTypesCount] = React.useState(0);
|
||||||
|
const [lessonCategoriesCount, setLessonCategoriesCount] = React.useState(0);
|
||||||
|
const [usersCount, setUsersCount] = React.useState(0);
|
||||||
|
|
||||||
|
async function fetchAllCount(): Promise<void> {
|
||||||
|
setLessonTypesCount(await GetAllLessonTypesCount());
|
||||||
|
setLessonCategoriesCount(await GetAllLessonCategoriesCount());
|
||||||
|
setUsersCount(await GetAllUsersCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
void fetchAllCount();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -57,13 +71,25 @@ export default function Page(): React.JSX.Element {
|
|||||||
</Stack>
|
</Stack>
|
||||||
<Grid container spacing={4}>
|
<Grid container spacing={4}>
|
||||||
<Grid md={4} xs={12}>
|
<Grid md={4} xs={12}>
|
||||||
<Summary amount={31} diff={15} icon={ListChecksIcon} title={t('Tickets')} trend="up" />
|
<ActiveUserCount />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid md={4} xs={12}>
|
<Grid md={4} xs={12}>
|
||||||
<Summary amount={240} diff={5} icon={UsersIcon} title={t('Sign ups')} trend="down" />
|
<Summary
|
||||||
|
amount={lessonCategoriesCount}
|
||||||
|
diff={15}
|
||||||
|
icon={ListChecksIcon}
|
||||||
|
title={t('lessonCategoriesCount')}
|
||||||
|
trend="up"
|
||||||
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid md={4} xs={12}>
|
<Grid md={4} xs={12}>
|
||||||
<Summary amount={21} diff={12} icon={WarningIcon} title={t('Open issues')} trend="up" />
|
<Summary
|
||||||
|
amount={lessonTypesCount}
|
||||||
|
diff={15}
|
||||||
|
icon={ListChecksIcon}
|
||||||
|
title={t('lessonTypesCount')}
|
||||||
|
trend="up"
|
||||||
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid md={8} xs={12}>
|
<Grid md={8} xs={12}>
|
||||||
<AppUsage
|
<AppUsage
|
||||||
@@ -84,116 +110,13 @@ export default function Page(): React.JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid md={4} xs={12}>
|
<Grid md={4} xs={12}>
|
||||||
<Subscriptions
|
<Subscriptions subscriptions={SamplesubScriptions} />
|
||||||
subscriptions={[
|
|
||||||
{
|
|
||||||
id: 'supabase',
|
|
||||||
title: 'Supabase',
|
|
||||||
icon: '/assets/company-avatar-5.png',
|
|
||||||
costs: '$599',
|
|
||||||
billingCycle: 'year',
|
|
||||||
status: 'paid',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'vercel',
|
|
||||||
title: 'Vercel',
|
|
||||||
icon: '/assets/company-avatar-4.png',
|
|
||||||
costs: '$20',
|
|
||||||
billingCycle: 'month',
|
|
||||||
status: 'expiring',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'auth0',
|
|
||||||
title: 'Auth0',
|
|
||||||
icon: '/assets/company-avatar-3.png',
|
|
||||||
costs: '$20-80',
|
|
||||||
billingCycle: 'month',
|
|
||||||
status: 'canceled',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'google_cloud',
|
|
||||||
title: 'Google Cloud',
|
|
||||||
icon: '/assets/company-avatar-2.png',
|
|
||||||
costs: '$100-200',
|
|
||||||
billingCycle: 'month',
|
|
||||||
status: 'paid',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'stripe',
|
|
||||||
title: 'Stripe',
|
|
||||||
icon: '/assets/company-avatar-1.png',
|
|
||||||
costs: '$70',
|
|
||||||
billingCycle: 'month',
|
|
||||||
status: 'paid',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid md={4} xs={12}>
|
<Grid md={4} xs={12}>
|
||||||
<AppChat
|
<AppChat messages={SampleMessages} />
|
||||||
messages={[
|
|
||||||
{
|
|
||||||
id: 'MSG-001',
|
|
||||||
content: 'Hello, we spoke earlier on the phone',
|
|
||||||
author: { name: 'Alcides Antonio', avatar: '/assets/avatar-10.png', status: 'online' },
|
|
||||||
createdAt: dayjs().subtract(2, 'minute').toDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'MSG-002',
|
|
||||||
content: 'Is the job still available?',
|
|
||||||
author: { name: 'Marcus Finn', avatar: '/assets/avatar-9.png', status: 'offline' },
|
|
||||||
createdAt: dayjs().subtract(56, 'minute').toDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'MSG-003',
|
|
||||||
content: "What is a screening task? I'd like to",
|
|
||||||
author: { name: 'Carson Darrin', avatar: '/assets/avatar-3.png', status: 'online' },
|
|
||||||
createdAt: dayjs().subtract(3, 'hour').subtract(23, 'minute').toDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'MSG-004',
|
|
||||||
content: 'Still waiting for feedback',
|
|
||||||
author: { name: 'Fran Perez', avatar: '/assets/avatar-5.png', status: 'online' },
|
|
||||||
createdAt: dayjs().subtract(8, 'hour').subtract(6, 'minute').toDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'MSG-005',
|
|
||||||
content: 'Need more information about campaigns',
|
|
||||||
author: { name: 'Jie Yan', avatar: '/assets/avatar-8.png', status: 'offline' },
|
|
||||||
createdAt: dayjs().subtract(10, 'hour').subtract(18, 'minute').toDate(),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid md={4} xs={12}>
|
<Grid md={4} xs={12}>
|
||||||
<Events
|
<Events events={SampleEvents} />
|
||||||
events={[
|
|
||||||
{
|
|
||||||
id: 'EV-004',
|
|
||||||
title: t('Meeting with partners'),
|
|
||||||
description: '17:00 to 18:00',
|
|
||||||
createdAt: dayjs().add(1, 'day').toDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'EV-003',
|
|
||||||
title: 'Interview with Jonas',
|
|
||||||
description: '15:30 to 16:45',
|
|
||||||
createdAt: dayjs().add(4, 'day').toDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'EV-002',
|
|
||||||
title: "Doctor's appointment",
|
|
||||||
description: '12:30 to 15:30',
|
|
||||||
createdAt: dayjs().add(4, 'day').toDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'EV-001',
|
|
||||||
title: 'Weekly meeting',
|
|
||||||
description: '09:00 to 09:30',
|
|
||||||
createdAt: dayjs().add(7, 'day').toDate(),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid md={4} xs={12}>
|
<Grid md={4} xs={12}>
|
||||||
<AppLimits usage={80} />
|
<AppLimits usage={80} />
|
||||||
|
@@ -0,0 +1,44 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import getAllUserMetasCount from '@/db/UserMetas/GetAllCount';
|
||||||
|
// import GetAllUsersCount from '@/db/Users/GetAllCount.tsx';
|
||||||
|
import { Typography } from '@mui/material';
|
||||||
|
import { Users as UsersIcon } from '@phosphor-icons/react/dist/ssr/Users';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Summary } from '@/components/dashboard/overview/summary';
|
||||||
|
|
||||||
|
function ActiveUserCount(): React.JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [amount, setAmount] = React.useState<number>(0);
|
||||||
|
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
getAllUserMetasCount()
|
||||||
|
.then((count) => {
|
||||||
|
setAmount(count);
|
||||||
|
setIsLoading(false);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setAmount(-99);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Typography>Loading...</Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Summary
|
||||||
|
amount={amount}
|
||||||
|
diff={10}
|
||||||
|
icon={UsersIcon}
|
||||||
|
title={t('用戶數量1')}
|
||||||
|
trend="up"
|
||||||
|
//
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActiveUserCount;
|
@@ -0,0 +1,52 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
/*
|
||||||
|
# PROMPT
|
||||||
|
|
||||||
|
this is a subset of a typescript project
|
||||||
|
|
||||||
|
clone `LessonTypeCount`, `LessonCategoriesCount` to `UserCount` and do modifiy to get the count of users, thanks.
|
||||||
|
*/
|
||||||
|
import * as React from 'react';
|
||||||
|
import GetAllCount from '@/db/LessonCategories/GetAllCount.tsx';
|
||||||
|
import { Typography } from '@mui/material';
|
||||||
|
import { ListChecks as ListChecksIcon } from '@phosphor-icons/react/dist/ssr/ListChecks';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Summary } from '@/components/dashboard/overview/summary';
|
||||||
|
|
||||||
|
function LessonCategoriesCount(): React.JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [amount, setAmount] = React.useState<number>(0);
|
||||||
|
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
GetAllCount()
|
||||||
|
.then((count) => {
|
||||||
|
setAmount(count);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
// console.error(err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Typography>Loading</Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Summary
|
||||||
|
amount={amount}
|
||||||
|
diff={15}
|
||||||
|
icon={ListChecksIcon}
|
||||||
|
title={t('課程類別')}
|
||||||
|
trend="up"
|
||||||
|
//
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(LessonCategoriesCount);
|
@@ -0,0 +1,52 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
/*
|
||||||
|
# PROMPT
|
||||||
|
|
||||||
|
this is a subset of a typescript project
|
||||||
|
|
||||||
|
clone `LessonTypeCount` to `LessonCategoriesCount` and do modifiy to get the count of lesson category, thanks.
|
||||||
|
*/
|
||||||
|
import * as React from 'react';
|
||||||
|
import GetAllCount from '@/db/LessonTypes/GetAllCount.tsx';
|
||||||
|
import { Typography } from '@mui/material';
|
||||||
|
import { ListChecks as ListChecksIcon } from '@phosphor-icons/react/dist/ssr/ListChecks';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Summary } from '@/components/dashboard/overview/summary';
|
||||||
|
|
||||||
|
function LessonTypeCount(): React.JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [amount, setAmount] = React.useState<number>(0);
|
||||||
|
const [isLoading, setIsLoading] = React.useState<boolean>(true);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
GetAllCount()
|
||||||
|
.then((count) => {
|
||||||
|
setAmount(count);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
// console.error(err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Typography>Loading...</Typography>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Summary
|
||||||
|
amount={amount}
|
||||||
|
diff={15}
|
||||||
|
icon={ListChecksIcon}
|
||||||
|
title={t('課程類型')}
|
||||||
|
trend="up"
|
||||||
|
//
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(LessonTypeCount);
|
@@ -3,5 +3,16 @@ const COL_LESSON_CATEGORIES = 'LessonsCategories';
|
|||||||
const NO_VALUE = 'NO_VALUE';
|
const NO_VALUE = 'NO_VALUE';
|
||||||
const NO_NUM = -Infinity;
|
const NO_NUM = -Infinity;
|
||||||
const NS_LESSON_CATEGORY = 'lesson_category';
|
const NS_LESSON_CATEGORY = 'lesson_category';
|
||||||
|
const COL_USERS = 'users';
|
||||||
|
const COL_USER_METAS = 'UserMetas';
|
||||||
|
|
||||||
export { COL_LESSON_TYPES, NO_VALUE, NO_NUM, COL_LESSON_CATEGORIES, NS_LESSON_CATEGORY };
|
export {
|
||||||
|
COL_LESSON_TYPES,
|
||||||
|
NO_VALUE,
|
||||||
|
NO_NUM,
|
||||||
|
COL_LESSON_CATEGORIES,
|
||||||
|
NS_LESSON_CATEGORY,
|
||||||
|
COL_USERS,
|
||||||
|
COL_USER_METAS,
|
||||||
|
//
|
||||||
|
};
|
||||||
|
37
002_source/cms/src/db/DB_AI_GUIDELINE.MD
Normal file
37
002_source/cms/src/db/DB_AI_GUIDELINE.MD
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# AI GUIDELINE
|
||||||
|
|
||||||
|
## Background information and References
|
||||||
|
|
||||||
|
please read `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/001_documentation/Requirements/REQ0006/schema.dbml`
|
||||||
|
|
||||||
|
please look into the md files in `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/_AI_GUIDELINE`
|
||||||
|
|
||||||
|
please read, remember and link up the ideas, i will tell you the task afterwards
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
working directory: `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/db`
|
||||||
|
|
||||||
|
pleaes clone the `tsx` files from `LessonTypes` and `LessonCategories` to `UserMetas` and update the content
|
||||||
|
|
||||||
|
when you draft coding, review file and append with `.tsx.draft`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- this is part of react typescript project, with pocketbase
|
||||||
|
- `schema.dbml`, describe the collections(tables)
|
||||||
|
- folder `LessonCategories`, the correct references
|
||||||
|
- folder `LessonTypes`, the correct references
|
||||||
|
- you can find the `schema.dbml` and schema information from `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/001_documentation/Requirements/REQ0006`
|
||||||
|
- do not read root directory, assume it is a fresh copy of nextjs project is ok
|
||||||
|
|
||||||
|
## instruction
|
||||||
|
|
||||||
|
- break the questions into smaller parts
|
||||||
|
- review file append with `.draft`, see if the content aligned with the correct references
|
||||||
|
- read and understand `dbml` file
|
||||||
|
- lookup the every folder
|
||||||
|
|
||||||
|
## tasks
|
||||||
|
|
||||||
|
Thanks
|
11
002_source/cms/src/db/LessonCategories/GetAllCount.tsx
Normal file
11
002_source/cms/src/db/LessonCategories/GetAllCount.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// REQ0006
|
||||||
|
import { COL_LESSON_CATEGORIES } from '@/constants';
|
||||||
|
|
||||||
|
import { pb } from '@/lib/pb';
|
||||||
|
|
||||||
|
export default async function GetAllCount(): Promise<number> {
|
||||||
|
const { totalItems: count } = await pb
|
||||||
|
.collection(COL_LESSON_CATEGORIES)
|
||||||
|
.getList(1, 9999, { filter: 'visible = "visible"' });
|
||||||
|
return count;
|
||||||
|
}
|
@@ -4,7 +4,6 @@ import type { RecordModel } from 'pocketbase';
|
|||||||
import { pb } from '@/lib/pb';
|
import { pb } from '@/lib/pb';
|
||||||
import type { CreateForm } from '@/components/dashboard/lesson_type/types';
|
import type { CreateForm } from '@/components/dashboard/lesson_type/types';
|
||||||
|
|
||||||
// import type { CreateForm } from '@/components/dashboard/lesson_type/interfaces.ts.del';
|
|
||||||
|
|
||||||
export default function createLessonType(data: CreateForm): Promise<RecordModel> {
|
export default function createLessonType(data: CreateForm): Promise<RecordModel> {
|
||||||
return pb.collection(COL_LESSON_TYPES).create(data);
|
return pb.collection(COL_LESSON_TYPES).create(data);
|
||||||
|
14
002_source/cms/src/db/LessonTypes/GetAllCount.tsx
Normal file
14
002_source/cms/src/db/LessonTypes/GetAllCount.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// REQ0006
|
||||||
|
import { COL_LESSON_TYPES } from '@/constants';
|
||||||
|
|
||||||
|
import { pb } from '@/lib/pb';
|
||||||
|
|
||||||
|
export default async function GetAllCount(): Promise<number> {
|
||||||
|
try {
|
||||||
|
const result = await pb.collection(COL_LESSON_TYPES).getList(1, 9999, { filter: 'visible = "visible"' });
|
||||||
|
const { totalItems: count } = result;
|
||||||
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
13
002_source/cms/src/db/UserMetas/GetAllCount.tsx
Normal file
13
002_source/cms/src/db/UserMetas/GetAllCount.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { COL_USER_METAS } from '@/constants';
|
||||||
|
|
||||||
|
import { pb } from '@/lib/pb';
|
||||||
|
|
||||||
|
export default async function getAllUserMetasCount(): Promise<number> {
|
||||||
|
try {
|
||||||
|
const result = await pb.collection(COL_USER_METAS).getList(1, 9998);
|
||||||
|
return result.totalItems;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return -99;
|
||||||
|
}
|
||||||
|
}
|
15
002_source/cms/src/db/Users/GetAllCount.tsx
Normal file
15
002_source/cms/src/db/Users/GetAllCount.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// REQ0006
|
||||||
|
import { COL_USERS } from '@/constants';
|
||||||
|
|
||||||
|
import { pb } from '@/lib/pb';
|
||||||
|
|
||||||
|
export default async function GetAllCount(): Promise<number> {
|
||||||
|
try {
|
||||||
|
const result = await pb.collection(`users`).getList(1, 9999, { filter: 'email != ""' });
|
||||||
|
const { totalItems: count } = result;
|
||||||
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return -99;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,19 +0,0 @@
|
|||||||
# PROMPT
|
|
||||||
|
|
||||||
with reference to `src/db/LessonTypes/DBLessonTypes.tsx`, clone and modify to fit `DBLessonCategories.tsx` thanks.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PROMPT
|
|
||||||
|
|
||||||
from `LessonTypes`, clone and modify to fit `LessonCategories` (e.g. CRUD) thanks.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PROMPT
|
|
||||||
|
|
||||||
with reference to
|
|
||||||
- `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/001_documentation/Requirements/REQ0006/schema.dbml`
|
|
||||||
- `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/db/LessonTypes`
|
|
||||||
|
|
||||||
draft `UserMeta` thanks
|
|
Reference in New Issue
Block a user