diff --git a/002_source/cms/src/app/api/db/lesson_categories/create/route.ts b/002_source/cms/src/app/api/db/lesson_categories/create/route.ts deleted file mode 100644 index ebb04c5..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/create/route.ts +++ /dev/null @@ -1,26 +0,0 @@ -import PocketBase from 'pocketbase'; - -const pb = new PocketBase(`http://localhost:8090`); -const COLLECTION_NAME = 'LessonsTypes'; - -interface LessonTypeCreate { - name: string; - type: string; - pos: number; - visible: string; -} - -interface ReqLessonTypeCreate { - data: LessonTypeCreate; -} - -// POST - Create a new lesson type -export async function POST(request: Request): Promise { - const { data } = (await request.json()) as ReqLessonTypeCreate; - const record = await pb.collection(COLLECTION_NAME).create(data); - - return new Response(JSON.stringify(record), { - status: 201, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/002_source/cms/src/app/api/db/lesson_categories/delete/route.ts b/002_source/cms/src/app/api/db/lesson_categories/delete/route.ts deleted file mode 100644 index 1b7f929..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/delete/route.ts +++ /dev/null @@ -1,19 +0,0 @@ -// please keep the comment and update to perform delete operation -import PocketBase from 'pocketbase'; - -const { PB_HOSTNAME } = process.env; -export async function DELETE(request: Request) { - try { - const pb = new PocketBase(`http://${PB_HOSTNAME}:8090`); - const { id } = await request.json(); - await pb.collection('LessonsTypes').delete(id); - - return new Response('Record deleted successfully', { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - console.error(error); - return new Response('Failed to delete record', { status: 500 }); - } -} diff --git a/002_source/cms/src/app/api/db/lesson_categories/getById/[id]/route.ts b/002_source/cms/src/app/api/db/lesson_categories/getById/[id]/route.ts deleted file mode 100644 index fe84906..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/getById/[id]/route.ts +++ /dev/null @@ -1,31 +0,0 @@ -// https://nextjs.org/blog/building-apis-with-nextjs -import { NextRequest } from 'next/server'; -import PocketBase from 'pocketbase'; - -const { PB_HOSTNAME } = process.env; -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const id = (await params).id; - - const fields = ['id', 'name', 'type', 'visible', 'pos'].join(','); - const pb = new PocketBase(`http://${PB_HOSTNAME}:8090`); - const lessonType = await pb.collection('LessonsTypes').getOne(id, { fields }); - - return new Response(JSON.stringify(lessonType), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); -} - -export async function POST(request: Request) { - // Parse the request body - const body = await request.json(); - const { name } = body; - - // e.g. Insert new user into your DB - const newUser = { id: Date.now(), name }; - - return new Response(JSON.stringify(newUser), { - status: 201, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/002_source/cms/src/app/api/db/lesson_categories/getById/[id]/test.http b/002_source/cms/src/app/api/db/lesson_categories/getById/[id]/test.http deleted file mode 100644 index ae7f1fb..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/getById/[id]/test.http +++ /dev/null @@ -1,5 +0,0 @@ -### - -GET http://localhost:3000/api/db/lesson_types/getById/e60v95892466775 - -### diff --git a/002_source/cms/src/app/api/db/lesson_categories/list/route.ts b/002_source/cms/src/app/api/db/lesson_categories/list/route.ts deleted file mode 100644 index 7a86af7..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/list/route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import PocketBase from 'pocketbase'; - -// const { PB_HOSTNAME } = process.env; - -export async function GET(): Promise { - const pb = new PocketBase(`http://localhost:8090`); - const resultList = await pb.collection('LessonsCategories').getList(1, 50, {}); - - // console.log(resultList); - - return Response.json(resultList); -} diff --git a/002_source/cms/src/app/api/db/lesson_categories/list/test.http b/002_source/cms/src/app/api/db/lesson_categories/list/test.http deleted file mode 100644 index 1e4a5ff..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/list/test.http +++ /dev/null @@ -1,17 +0,0 @@ -### - -GET http://localhost:3000/api/db/lesson_categories/list - -Content-Type: application/json -cache: no-store -cache-control: no-cache1 - -### - -GET http://localhost:8090/api/collections/LessonsCategories/records - -### - -POST http://localhost:3000/api/lesson_categories/helloworld - -Content-Type: application/json diff --git a/002_source/cms/src/app/api/db/lesson_categories/test/route.ts b/002_source/cms/src/app/api/db/lesson_categories/test/route.ts deleted file mode 100644 index c6c67ab..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/test/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -export async function GET(): Promise { - const record = { hello: 'world' }; - - return Response.json(record); -} diff --git a/002_source/cms/src/app/api/db/lesson_categories/update/route.ts b/002_source/cms/src/app/api/db/lesson_categories/update/route.ts deleted file mode 100644 index a905e31..0000000 --- a/002_source/cms/src/app/api/db/lesson_categories/update/route.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NextRequest } from 'next/server'; -import PocketBase from 'pocketbase'; - -const { PB_HOSTNAME } = process.env; -export async function PUT(request: NextRequest) { - try { - const { id, data } = await request.json(); - - const pb = new PocketBase(`http://${PB_HOSTNAME}:8090`); - const updatedRecord = await pb.collection('LessonsTypes').update(id, data); - - return new Response(JSON.stringify(updatedRecord), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - console.error(error); - return new Response('Failed to update record', { status: 500 }); - } -} diff --git a/002_source/cms/src/app/api/db/lesson_types/create/route.ts b/002_source/cms/src/app/api/db/lesson_types/create/route.ts deleted file mode 100644 index ebb04c5..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/create/route.ts +++ /dev/null @@ -1,26 +0,0 @@ -import PocketBase from 'pocketbase'; - -const pb = new PocketBase(`http://localhost:8090`); -const COLLECTION_NAME = 'LessonsTypes'; - -interface LessonTypeCreate { - name: string; - type: string; - pos: number; - visible: string; -} - -interface ReqLessonTypeCreate { - data: LessonTypeCreate; -} - -// POST - Create a new lesson type -export async function POST(request: Request): Promise { - const { data } = (await request.json()) as ReqLessonTypeCreate; - const record = await pb.collection(COLLECTION_NAME).create(data); - - return new Response(JSON.stringify(record), { - status: 201, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/002_source/cms/src/app/api/db/lesson_types/delete/route.ts b/002_source/cms/src/app/api/db/lesson_types/delete/route.ts deleted file mode 100644 index 1b7f929..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/delete/route.ts +++ /dev/null @@ -1,19 +0,0 @@ -// please keep the comment and update to perform delete operation -import PocketBase from 'pocketbase'; - -const { PB_HOSTNAME } = process.env; -export async function DELETE(request: Request) { - try { - const pb = new PocketBase(`http://${PB_HOSTNAME}:8090`); - const { id } = await request.json(); - await pb.collection('LessonsTypes').delete(id); - - return new Response('Record deleted successfully', { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - console.error(error); - return new Response('Failed to delete record', { status: 500 }); - } -} diff --git a/002_source/cms/src/app/api/db/lesson_types/getById/[id]/route.ts b/002_source/cms/src/app/api/db/lesson_types/getById/[id]/route.ts deleted file mode 100644 index fe84906..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/getById/[id]/route.ts +++ /dev/null @@ -1,31 +0,0 @@ -// https://nextjs.org/blog/building-apis-with-nextjs -import { NextRequest } from 'next/server'; -import PocketBase from 'pocketbase'; - -const { PB_HOSTNAME } = process.env; -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { - const id = (await params).id; - - const fields = ['id', 'name', 'type', 'visible', 'pos'].join(','); - const pb = new PocketBase(`http://${PB_HOSTNAME}:8090`); - const lessonType = await pb.collection('LessonsTypes').getOne(id, { fields }); - - return new Response(JSON.stringify(lessonType), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); -} - -export async function POST(request: Request) { - // Parse the request body - const body = await request.json(); - const { name } = body; - - // e.g. Insert new user into your DB - const newUser = { id: Date.now(), name }; - - return new Response(JSON.stringify(newUser), { - status: 201, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/002_source/cms/src/app/api/db/lesson_types/getById/[id]/test.http b/002_source/cms/src/app/api/db/lesson_types/getById/[id]/test.http deleted file mode 100644 index ae7f1fb..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/getById/[id]/test.http +++ /dev/null @@ -1,5 +0,0 @@ -### - -GET http://localhost:3000/api/db/lesson_types/getById/e60v95892466775 - -### diff --git a/002_source/cms/src/app/api/db/lesson_types/list/route.ts b/002_source/cms/src/app/api/db/lesson_types/list/route.ts deleted file mode 100644 index f07a0e9..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/list/route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import PocketBase from 'pocketbase'; - -// const { PB_HOSTNAME } = process.env; - -export async function GET(): Promise { - const pb = new PocketBase(`http://localhost:8090`); - const resultList = await pb.collection('LessonsTypes').getList(1, 50, {}); - - // console.log(resultList); - - return Response.json(resultList); -} diff --git a/002_source/cms/src/app/api/db/lesson_types/list/test.http b/002_source/cms/src/app/api/db/lesson_types/list/test.http deleted file mode 100644 index 6910602..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/list/test.http +++ /dev/null @@ -1,17 +0,0 @@ -### - -GET http://localhost:3000/api/db/lesson_types/list - -Content-Type: application/json -cache: no-store -cache-control: no-cache1 - -### - -GET http://localhost:8090/api/collections/LessonsTypes/records - -### - -POST http://localhost:3000/api/lesson_types/helloworld - -Content-Type: application/json diff --git a/002_source/cms/src/app/api/db/lesson_types/test/route.ts b/002_source/cms/src/app/api/db/lesson_types/test/route.ts deleted file mode 100644 index c6c67ab..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/test/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -export async function GET(): Promise { - const record = { hello: 'world' }; - - return Response.json(record); -} diff --git a/002_source/cms/src/app/api/db/lesson_types/update/route.ts b/002_source/cms/src/app/api/db/lesson_types/update/route.ts deleted file mode 100644 index a905e31..0000000 --- a/002_source/cms/src/app/api/db/lesson_types/update/route.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NextRequest } from 'next/server'; -import PocketBase from 'pocketbase'; - -const { PB_HOSTNAME } = process.env; -export async function PUT(request: NextRequest) { - try { - const { id, data } = await request.json(); - - const pb = new PocketBase(`http://${PB_HOSTNAME}:8090`); - const updatedRecord = await pb.collection('LessonsTypes').update(id, data); - - return new Response(JSON.stringify(updatedRecord), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - console.error(error); - return new Response('Failed to update record', { status: 500 }); - } -} diff --git a/002_source/cms/src/app/api/db/vocabularies/list/route.ts b/002_source/cms/src/app/api/db/vocabularies/list/route.ts deleted file mode 100644 index ccff16f..0000000 --- a/002_source/cms/src/app/api/db/vocabularies/list/route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import PocketBase from 'pocketbase'; - -export async function GET(): Promise { - const { PB_HOSTNAME } = process.env; - const pb = new PocketBase(`http://${PB_HOSTNAME}:8090`); - const resultList = await pb.collection('Vocabularies').getList(1, 50, {}); - - return Response.json(resultList); -} diff --git a/002_source/cms/src/app/api/db/vocabularies/list/test.http b/002_source/cms/src/app/api/db/vocabularies/list/test.http deleted file mode 100644 index 33999ce..0000000 --- a/002_source/cms/src/app/api/db/vocabularies/list/test.http +++ /dev/null @@ -1,5 +0,0 @@ -### - -GET http://localhost:3000/api/db/lesson_types/list - -### diff --git a/002_source/cms/src/app/api/helloworld/route.ts b/002_source/cms/src/app/api/helloworld/route.ts deleted file mode 100644 index 97d76cd..0000000 --- a/002_source/cms/src/app/api/helloworld/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const dynamic = 'force-static'; - -export async function GET(): Promise { - return Response.json({ hello: 'world' }); -} diff --git a/002_source/cms/src/app/dashboard/lesson_categories/[customerId]/page.tsx b/002_source/cms/src/app/dashboard/lesson_categories/[customerId]/page.tsx deleted file mode 100644 index a12ec06..0000000 --- a/002_source/cms/src/app/dashboard/lesson_categories/[customerId]/page.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import * as React from 'react'; -import type { Metadata } from 'next'; -import RouterLink from 'next/link'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import CardHeader from '@mui/material/CardHeader'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import IconButton from '@mui/material/IconButton'; -import LinearProgress from '@mui/material/LinearProgress'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import Grid from '@mui/material/Unstable_Grid2'; -import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft'; -import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown'; -import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle'; -import { CreditCard as CreditCardIcon } from '@phosphor-icons/react/dist/ssr/CreditCard'; -import { House as HouseIcon } from '@phosphor-icons/react/dist/ssr/House'; -import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; -import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus'; -import { ShieldWarning as ShieldWarningIcon } from '@phosphor-icons/react/dist/ssr/ShieldWarning'; -import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User'; - -import { config } from '@/config'; -import { paths } from '@/paths'; -import { dayjs } from '@/lib/dayjs'; -import { PropertyItem } from '@/components/core/property-item'; -import { PropertyList } from '@/components/core/property-list'; -import { Notifications } from '@/components/dashboard/lesson_category/notifications'; -import { Payments } from '@/components/dashboard/lesson_category/payments'; -import type { Address } from '@/components/dashboard/lesson_category/shipping-address'; -import { ShippingAddress } from '@/components/dashboard/lesson_category/shipping-address'; - -export const metadata = { title: `Details | Customers | Dashboard | ${config.site.name}` } satisfies Metadata; - -export default function Page(): React.JSX.Element { - return ( - - - -
- - - Lesson Categories - -
- - - - MV - -
- - Miron Vitold - } - label="Active" - size="small" - variant="outlined" - /> - - - miron.vitold@domain.com - -
-
-
- -
-
-
- - - - - - - - } - avatar={ - - - - } - title="Basic details" - /> - } - orientation="vertical" - sx={{ '--PropertyItem-padding': '12px 24px' }} - > - {( - [ - { key: 'Customer ID', value: }, - { key: 'Name', value: 'Miron Vitold' }, - { key: 'Email', value: 'miron.vitold@domain.com' }, - { key: 'Phone', value: '(425) 434-5535' }, - { key: 'Company', value: 'Devias IO' }, - { - key: 'Quota', - value: ( - - - - 50% - - - ), - }, - ] satisfies { key: string; value: React.ReactNode }[] - ).map( - (item): React.JSX.Element => ( - - ) - )} - - - - - - - } - title="Security" - /> - - -
- -
- - A deleted customer cannot be restored. All data will be permanently removed. - -
-
-
-
-
- - - - - }> - Edit - - } - avatar={ - - - - } - title="Billing details" - /> - - - } sx={{ '--PropertyItem-padding': '16px' }}> - {( - [ - { key: 'Credit card', value: '**** 4142' }, - { key: 'Country', value: 'United States' }, - { key: 'State', value: 'Michigan' }, - { key: 'City', value: 'Southfield' }, - { key: 'Address', value: '1721 Bartlett Avenue, 48034' }, - { key: 'Tax ID', value: 'EU87956621' }, - ] satisfies { key: string; value: React.ReactNode }[] - ).map( - (item): React.JSX.Element => ( - - ) - )} - - - - - - }> - Add - - } - avatar={ - - - - } - title="Shipping addresses" - /> - - - {( - [ - { - id: 'ADR-001', - country: 'United States', - state: 'Michigan', - city: 'Lansing', - zipCode: '48933', - street: '480 Haven Lane', - primary: true, - }, - { - id: 'ADR-002', - country: 'United States', - state: 'Missouri', - city: 'Springfield', - zipCode: '65804', - street: '4807 Lighthouse Drive', - }, - ] satisfies Address[] - ).map((address) => ( - - - - ))} - - - - - - -
-
-
- ); -} diff --git a/002_source/cms/src/app/dashboard/lesson_categories/create/page.tsx b/002_source/cms/src/app/dashboard/lesson_categories/create/page.tsx deleted file mode 100644 index 6ffef92..0000000 --- a/002_source/cms/src/app/dashboard/lesson_categories/create/page.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import * as React from 'react'; -import type { Metadata } from 'next'; -import RouterLink from 'next/link'; -import Box from '@mui/material/Box'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft'; - -import { config } from '@/config'; -import { paths } from '@/paths'; -import { CustomerCreateForm } from '@/components/dashboard/lesson_category/lesson-category-create-form'; - -export const metadata = { title: `Create | Customers | Dashboard | ${config.site.name}` } satisfies Metadata; - -export default function Page(): React.JSX.Element { - return ( - - - -
- - - Lesson Categories - -
-
- Create customer -
-
- -
-
- ); -} diff --git a/002_source/cms/src/app/dashboard/lesson_categories/lesson-categories-sample-data.tsx b/002_source/cms/src/app/dashboard/lesson_categories/lesson-categories-sample-data.tsx deleted file mode 100644 index 41405ea..0000000 --- a/002_source/cms/src/app/dashboard/lesson_categories/lesson-categories-sample-data.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { dayjs } from '@/lib/dayjs'; -import type { LessonCategory } from '@/components/dashboard/lesson_category/lesson-categories-table'; - -export const lessonCategoriesSampleData = [ - { - id: 'USR-005', - name: 'Fran Perez', - avatar: '/assets/avatar-5.png', - email: 'fran.perez@domain.com', - phone: '(815) 704-0045', - quota: 50, - status: 'active', - createdAt: dayjs().subtract(1, 'hour').toDate(), - }, - { - id: 'USR-004', - name: 'Penjani Inyene', - avatar: '/assets/avatar-4.png', - email: 'penjani.inyene@domain.com', - phone: '(803) 937-8925', - quota: 100, - status: 'active', - createdAt: dayjs().subtract(3, 'hour').toDate(), - }, - { - id: 'USR-003', - name: 'Carson Darrin', - avatar: '/assets/avatar-3.png', - email: 'carson.darrin@domain.com', - phone: '(715) 278-5041', - quota: 10, - status: 'blocked', - createdAt: dayjs().subtract(1, 'hour').subtract(1, 'day').toDate(), - }, - { - id: 'USR-002', - name: 'Siegbert Gottfried', - avatar: '/assets/avatar-2.png', - email: 'siegbert.gottfried@domain.com', - phone: '(603) 766-0431', - quota: 0, - status: 'pending', - createdAt: dayjs().subtract(7, 'hour').subtract(1, 'day').toDate(), - }, - { - id: 'USR-001', - name: 'Miron Vitold', - avatar: '/assets/avatar-1.png', - email: 'miron.vitold@domain.com', - phone: '(425) 434-5535', - quota: 50, - status: 'active', - createdAt: dayjs().subtract(2, 'hour').subtract(2, 'day').toDate(), - }, -] satisfies LessonCategory[]; diff --git a/002_source/cms/src/app/dashboard/lesson_categories/page.tsx b/002_source/cms/src/app/dashboard/lesson_categories/page.tsx deleted file mode 100644 index 4c5b739..0000000 --- a/002_source/cms/src/app/dashboard/lesson_categories/page.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import * as React from 'react'; -import type { Metadata } from 'next'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -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 { config } from '@/config'; -import { LessonCategoriesFilters } 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 { LessonCategoriesSelectionProvider } from '@/components/dashboard/lesson_category/lesson-categories-selection-context'; -import { LessonCategoriesTable } from '@/components/dashboard/lesson_category/lesson-categories-table'; -import type { LessonCategory } from '@/components/dashboard/lesson_category/lesson-categories-table'; - -import { lessonCategoriesSampleData } from './lesson-categories-sample-data'; - -export const metadata = { title: `List | Customers | Dashboard | ${config.site.name}` } satisfies Metadata; - -interface PageProps { - searchParams: { email?: string; phone?: string; sortDir?: 'asc' | 'desc'; status?: string }; -} - -export default function Page({ searchParams }: PageProps): React.JSX.Element { - const { email, phone, sortDir, status } = searchParams; - - const sortedLessonCategories = applySort(lessonCategoriesSampleData, sortDir); - const filteredLessonCategories = applyFilters(sortedLessonCategories, { email, phone, status }); - - return ( - - - - - Lesson Categories - - - - - - - - - - - - - - - - - - - ); -} - -// Sorting and filtering has to be done on the server. - -function applySort(row: LessonCategory[], sortDir: 'asc' | 'desc' | undefined): LessonCategory[] { - return row.sort((a, b) => { - if (sortDir === 'asc') { - return a.createdAt.getTime() - b.createdAt.getTime(); - } - - return b.createdAt.getTime() - a.createdAt.getTime(); - }); -} - -function applyFilters(row: LessonCategory[], { email, phone, status }: Filters): LessonCategory[] { - return row.filter((item) => { - if (email) { - if (!item.email?.toLowerCase().includes(email.toLowerCase())) { - return false; - } - } - - if (phone) { - if (!item.phone?.toLowerCase().includes(phone.toLowerCase())) { - return false; - } - } - - if (status) { - if (item.status !== status) { - return false; - } - } - - return true; - }); -} diff --git a/002_source/cms/src/app/dashboard/lesson_types/[typeId]/page.tsx b/002_source/cms/src/app/dashboard/lesson_types/[typeId]/page.tsx deleted file mode 100644 index b7038f0..0000000 --- a/002_source/cms/src/app/dashboard/lesson_types/[typeId]/page.tsx +++ /dev/null @@ -1,339 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import { useParams, useRouter } from 'next/navigation'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import CardHeader from '@mui/material/CardHeader'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import IconButton from '@mui/material/IconButton'; -import LinearProgress from '@mui/material/LinearProgress'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import Grid from '@mui/material/Unstable_Grid2'; -import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft'; -import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown'; -import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle'; -import { CreditCard as CreditCardIcon } from '@phosphor-icons/react/dist/ssr/CreditCard'; -import { House as HouseIcon } from '@phosphor-icons/react/dist/ssr/House'; -import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; -import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus'; -import { ShieldWarning as ShieldWarningIcon } from '@phosphor-icons/react/dist/ssr/ShieldWarning'; -import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User'; -import { useTranslation } from 'react-i18next'; - -import { paths } from '@/paths'; -import { dayjs } from '@/lib/dayjs'; -import { PropertyItem } from '@/components/core/property-item'; -import { PropertyList } from '@/components/core/property-list'; -import { getLessonTypeById } from '@/components/dashboard/lesson_type/http-actions'; -import { LessonTypeDefaultValue, type LessonType } from '@/components/dashboard/lesson_type/ILessonType'; -import { Notifications } from '@/components/dashboard/lesson_type/notifications'; -import { Payments } from '@/components/dashboard/lesson_type/payments'; -import type { Address } from '@/components/dashboard/lesson_type/shipping-address'; -import { ShippingAddress } from '@/components/dashboard/lesson_type/shipping-address'; - -export default function Page(): React.JSX.Element { - const { t } = useTranslation(); - const { typeId } = useParams<{ typeId: string }>(); - const [isLoading, setIsLoading] = React.useState(true); - const [showLessonType, setShowLessonType] = React.useState(LessonTypeDefaultValue); - const router = useRouter(); - - function handleEditClick() { - router.push(paths.dashboard.lesson_types.edit(showLessonType.id)); - } - - React.useEffect(() => { - getLessonTypeById(typeId) - .then((lessonType: LessonType) => { - setIsLoading(false); - setShowLessonType(lessonType); - }) - .catch((err) => { - // console.error(err); - console.error(t('lessonType.load_error')); - }); - // console.log('hello'); - }, []); - - if (isLoading) return
{t('common.loading')}
; - - return ( - - - -
- - - {t('Lesson Types')} - -
- - - - MV - -
- - {showLessonType.name} - } - label={showLessonType.visible} - size="small" - variant="outlined" - /> - - - {showLessonType.id} - -
-
-
- -
-
-
- - - - - { - handleEditClick(); - }} - > - - - } - avatar={ - - - - } - title="Basic details" - /> - } - orientation="vertical" - sx={{ '--PropertyItem-padding': '12px 24px' }} - > - {( - [ - { key: 'Customer ID', value: }, - { key: 'Name', value: showLessonType.name }, - { key: 'Type', value: showLessonType.type }, - { key: 'Pos', value: showLessonType.pos }, - { key: 'Visible', value: }, - { - key: 'Quota', - value: ( - - - - 50% - - - ), - }, - ] satisfies { key: string; value: React.ReactNode }[] - ).map( - (item): React.JSX.Element => ( - - ) - )} - - - - - - - } - title="Security" - /> - - -
- -
- - A deleted lesson type cannot be restored. All data will be permanently removed. - -
-
-
-
-
- - - - - }> - Edit - - } - avatar={ - - - - } - title="Billing details" - /> - - - } sx={{ '--PropertyItem-padding': '16px' }}> - {( - [ - { key: 'Credit card', value: '**** 4142' }, - { key: 'Country', value: 'United States' }, - { key: 'State', value: 'Michigan' }, - { key: 'City', value: 'Southfield' }, - { key: 'Address', value: '1721 Bartlett Avenue, 48034' }, - { key: 'Tax ID', value: 'EU87956621' }, - ] satisfies { key: string; value: React.ReactNode }[] - ).map( - (item): React.JSX.Element => ( - - ) - )} - - - - - - }> - Add - - } - avatar={ - - - - } - title="Shipping addresses" - /> - - - {( - [ - { - id: 'ADR-001', - country: 'United States', - state: 'Michigan', - city: 'Lansing', - zipCode: '48933', - street: '480 Haven Lane', - primary: true, - }, - { - id: 'ADR-002', - country: 'United States', - state: 'Missouri', - city: 'Springfield', - zipCode: '65804', - street: '4807 Lighthouse Drive', - }, - ] satisfies Address[] - ).map((address) => ( - - - - ))} - - - - - - -
-
-
- ); -} diff --git a/002_source/cms/src/app/dashboard/lesson_types/create/page.tsx b/002_source/cms/src/app/dashboard/lesson_types/create/page.tsx deleted file mode 100644 index df7a8ae..0000000 --- a/002_source/cms/src/app/dashboard/lesson_types/create/page.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import Box from '@mui/material/Box'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft'; -import { useTranslation } from 'react-i18next'; - -import { paths } from '@/paths'; -import { LessonTypeCreateForm } from '@/components/dashboard/lesson_type/lesson-type-create-form'; - -export default function Page(): React.JSX.Element { - const { t } = useTranslation(); - return ( - - - -
- - - {t('dashboard.lessonTypes.title')} - -
-
- {t('dashboard.lessonTypes.create.title')} -
-
- -
-
- ); -} diff --git a/002_source/cms/src/app/dashboard/lesson_types/edit/[typeId]/page.tsx b/002_source/cms/src/app/dashboard/lesson_types/edit/[typeId]/page.tsx deleted file mode 100644 index 91e89ef..0000000 --- a/002_source/cms/src/app/dashboard/lesson_types/edit/[typeId]/page.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import Box from '@mui/material/Box'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft'; -import { useTranslation } from 'react-i18next'; - -import { paths } from '@/paths'; -import { LessonTypeEditForm } from '@/components/dashboard/lesson_type/lesson-type-edit-form'; - -export default function Page(): React.JSX.Element { - const { t } = useTranslation(); - return ( - - - -
- - - {t('dashboard.lessonTypes.title')} - -
-
- {t('dashboard.lessonTypes.edit.title')} -
-
- -
-
- ); -} diff --git a/002_source/cms/src/app/dashboard/lesson_types/lesson-types-data.tsx b/002_source/cms/src/app/dashboard/lesson_types/lesson-types-data.tsx deleted file mode 100644 index cf88d17..0000000 --- a/002_source/cms/src/app/dashboard/lesson_types/lesson-types-data.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { dayjs } from '@/lib/dayjs'; -import type { LessonType } from '@/components/dashboard/lesson_type/ILessonType'; - -// import { helloworld } from '@/components/dashboard/lesson_type/helloworld'; -// export const metadata = { title: `List | Customers | Dashboard | ${config.site.name}` } satisfies Metadata; -export const lessonTypesSampleData = [ - { - id: 'USR-005', - name: 'Fran Perez', - type: 'vocabulary', - pos: 1, - visible: 'visible', - avatar: '/assets/avatar-5.png', - email: 'fran.perez@domain.com', - phone: '(815) 704-0045', - quota: 50, - status: 'active', - createdAt: dayjs().subtract(1, 'hour').toDate(), - }, - { - id: 'USR-004', - name: 'Penjani Inyene', - type: 'connectives', - pos: 1, - visible: 'visible', - avatar: '/assets/avatar-4.png', - email: 'penjani.inyene@domain.com', - phone: '(803) 937-8925', - quota: 100, - status: 'active', - createdAt: dayjs().subtract(3, 'hour').toDate(), - }, -] satisfies LessonType[]; - -export const lessonTypesData = (): LessonType[] => { - return lessonTypesSampleData; -}; diff --git a/002_source/cms/src/app/dashboard/lesson_types/lesson-types-sample-data.tsx b/002_source/cms/src/app/dashboard/lesson_types/lesson-types-sample-data.tsx deleted file mode 100644 index 1d2deed..0000000 --- a/002_source/cms/src/app/dashboard/lesson_types/lesson-types-sample-data.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { dayjs } from '@/lib/dayjs'; -import type { LessonType } from '@/components/dashboard/lesson_type/ILessonType'; - -// import { helloworld } from '@/components/dashboard/lesson_type/helloworld'; -// export const metadata = { title: `List | Customers | Dashboard | ${config.site.name}` } satisfies Metadata; -export const lessonTypesSampleData = [ - { - id: 'USR-005', - name: 'Vocabulary', - type: 'vocabulary', - pos: 1, - visible: 'visible', - avatar: '/assets/avatar-5.png', - email: 'fran.perez@domain.com', - phone: '(815) 704-0045', - quota: 50, - status: 'active', - createdAt: dayjs().subtract(1, 'hour').toDate(), - }, - { - id: 'USR-004', - name: 'Connectives', - type: 'connectives', - pos: 2, - visible: 'visible', - avatar: '/assets/avatar-4.png', - email: 'penjani.inyene@domain.com', - phone: '(803) 937-8925', - quota: 100, - status: 'active', - createdAt: dayjs().subtract(3, 'hour').toDate(), - }, -] satisfies LessonType[]; diff --git a/002_source/cms/src/app/dashboard/lesson_types/page.tsx b/002_source/cms/src/app/dashboard/lesson_types/page.tsx deleted file mode 100644 index ad1d7ab..0000000 --- a/002_source/cms/src/app/dashboard/lesson_types/page.tsx +++ /dev/null @@ -1,169 +0,0 @@ -'use client'; - -import * as React from 'react'; -import { useRouter } from 'next/navigation'; -import { LoadingButton } from '@mui/lab'; -import Box from '@mui/material/Box'; -import Card from '@mui/material/Card'; -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 { useTranslation } from 'react-i18next'; - -import { paths } from '@/paths'; -import { logger } from '@/lib/default-logger'; -import { toast } from '@/components/core/toaster'; -import { listLessonTypes } from '@/components/dashboard/lesson_type/http-actions'; -import type { LessonType } from '@/components/dashboard/lesson_type/ILessonType'; -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'; -import { LessonTypesSelectionProvider } from '@/components/dashboard/lesson_type/lesson-types-selection-context'; -import { LessonTypesTable } from '@/components/dashboard/lesson_type/lesson-types-table'; -import FormLoading from '@/components/loading'; - -interface PageProps { - searchParams: { - email?: string; - phone?: string; - sortDir?: 'asc' | 'desc'; - status?: string; - name?: string; - visible?: string; - type?: string; - // - }; -} - -export default function Page({ searchParams }: PageProps): React.JSX.Element { - const { t } = useTranslation(); - const { email, phone, sortDir, status, name, visible, type } = searchParams; - const router = useRouter(); - - const [isLoadingAddPage, setIsLoadingAddPage] = React.useState(false); - const [lessonTypesData, setLessonTypesData] = React.useState([]); - const sortedLessonTypes = applySort(lessonTypesData, sortDir); - const filteredLessonTypes = applyFilters(sortedLessonTypes, { - email, - phone, - status, - name, - type, - visible, - // - }); - - const reloadRows = () => { - listLessonTypes() - .then((lessonTypes: LessonType[]) => { - setLessonTypesData(lessonTypes); - }) - .catch((err) => { - logger.error(err); - toast(t('dashboard.lessonTypes.list.error')); - }); - }; - - React.useEffect(() => { - reloadRows(); - }, []); - - if (lessonTypesData.length < 1) return ; - - return ( - - - - - {t('Lesson Types')} - - - { - setIsLoadingAddPage(true); - router.push(paths.dashboard.lesson_types.create); - }} - startIcon={} - variant="contained" - > - {/* add new lesson type */} - {t('dashboard.lessonTypes.add')} - - - - - - - - - - - - - - - - - ); -} - -// Sorting and filtering has to be done on the server. - -function applySort(row: LessonType[], sortDir: 'asc' | 'desc' | undefined): LessonType[] { - return row.sort((a, b) => { - if (sortDir === 'asc') { - return a.createdAt.getTime() - b.createdAt.getTime(); - } - - return b.createdAt.getTime() - a.createdAt.getTime(); - }); -} - -function applyFilters(row: LessonType[], { email, phone, status, name, visible }: Filters): LessonType[] { - return row.filter((item) => { - if (email) { - if (!item.email?.toLowerCase().includes(email.toLowerCase())) { - return false; - } - } - - if (phone) { - if (!item.phone?.toLowerCase().includes(phone.toLowerCase())) { - return false; - } - } - - if (status) { - if (item.status !== status) { - return false; - } - } - - if (name) { - if (!item.name?.toLowerCase().includes(name.toLowerCase())) { - return false; - } - } - - if (visible) { - if (!item.visible?.toLowerCase().includes(visible.toLowerCase())) { - return false; - } - } - - return true; - }); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/helloworld.tsx b/002_source/cms/src/components/dashboard/lesson_category/helloworld.tsx deleted file mode 100644 index 3989cb1..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/helloworld.tsx +++ /dev/null @@ -1,3 +0,0 @@ -const helloworld = 'helloworld'; - -export { helloworld }; diff --git a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-filters.tsx b/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-filters.tsx deleted file mode 100644 index ab380d4..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-filters.tsx +++ /dev/null @@ -1,244 +0,0 @@ -'use client'; - -import * as React from 'react'; -import { useRouter } from 'next/navigation'; -import Button from '@mui/material/Button'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -import OutlinedInput from '@mui/material/OutlinedInput'; -import Select from '@mui/material/Select'; -import type { SelectChangeEvent } from '@mui/material/Select'; -import Stack from '@mui/material/Stack'; -import Tab from '@mui/material/Tab'; -import Tabs from '@mui/material/Tabs'; -import Typography from '@mui/material/Typography'; - -import { paths } from '@/paths'; -import { FilterButton, FilterPopover, useFilterContext } from '@/components/core/filter-button'; -import { Option } from '@/components/core/option'; - -import { useLessonCategoriesSelection } from './lesson-categories-selection-context'; - -// The tabs should be generated using API data. -const tabs = [ - { label: 'All', value: '', count: 5 }, - { label: 'Active', value: 'active', count: 3 }, - { label: 'Pending', value: 'pending', count: 1 }, - { label: 'Blocked', value: 'blocked', count: 1 }, -] as const; - -export interface Filters { - email?: string; - phone?: string; - status?: string; -} - -export type SortDir = 'asc' | 'desc'; - -export interface LessonCategoriesFiltersProps { - filters?: Filters; - sortDir?: SortDir; -} - -export function LessonCategoriesFilters({ - filters = {}, - sortDir = 'desc', -}: LessonCategoriesFiltersProps): React.JSX.Element { - const { email, phone, status } = filters; - - const router = useRouter(); - - const selection = useLessonCategoriesSelection(); - - const updateSearchParams = React.useCallback( - (newFilters: Filters, newSortDir: SortDir): void => { - const searchParams = new URLSearchParams(); - - if (newSortDir === 'asc') { - searchParams.set('sortDir', newSortDir); - } - - if (newFilters.status) { - searchParams.set('status', newFilters.status); - } - - if (newFilters.email) { - searchParams.set('email', newFilters.email); - } - - if (newFilters.phone) { - searchParams.set('phone', newFilters.phone); - } - - router.push(`${paths.dashboard.lesson_categories.list}?${searchParams.toString()}`); - }, - [router] - ); - - const handleClearFilters = React.useCallback(() => { - updateSearchParams({}, sortDir); - }, [updateSearchParams, sortDir]); - - const handleStatusChange = React.useCallback( - (_: React.SyntheticEvent, value: string) => { - updateSearchParams({ ...filters, status: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handleEmailChange = React.useCallback( - (value?: string) => { - updateSearchParams({ ...filters, email: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handlePhoneChange = React.useCallback( - (value?: string) => { - updateSearchParams({ ...filters, phone: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handleSortChange = React.useCallback( - (event: SelectChangeEvent) => { - updateSearchParams(filters, event.target.value as SortDir); - }, - [updateSearchParams, filters] - ); - - const hasFilters = status || email || phone; - - return ( -
- - {tabs.map((tab) => ( - } - iconPosition="end" - key={tab.value} - label={tab.label} - sx={{ minHeight: 'auto' }} - tabIndex={0} - value={tab.value} - /> - ))} - - - - - { - handleEmailChange(value as string); - }} - onFilterDelete={() => { - handleEmailChange(); - }} - popover={} - value={email} - /> - { - handlePhoneChange(value as string); - }} - onFilterDelete={() => { - handlePhoneChange(); - }} - popover={} - value={phone} - /> - {hasFilters ? : null} - - {selection.selectedAny ? ( - - - {selection.selected.size} selected - - - - ) : null} - - -
- ); -} - -function EmailFilterPopover(): React.JSX.Element { - const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext(); - const [value, setValue] = React.useState(''); - - React.useEffect(() => { - setValue((initialValue as string | undefined) ?? ''); - }, [initialValue]); - - return ( - - - { - setValue(event.target.value); - }} - onKeyUp={(event) => { - if (event.key === 'Enter') { - onApply(value); - } - }} - value={value} - /> - - - - ); -} - -function PhoneFilterPopover(): React.JSX.Element { - const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext(); - const [value, setValue] = React.useState(''); - - React.useEffect(() => { - setValue((initialValue as string | undefined) ?? ''); - }, [initialValue]); - - return ( - - - { - setValue(event.target.value); - }} - onKeyUp={(event) => { - if (event.key === 'Enter') { - onApply(value); - } - }} - value={value} - /> - - - - ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-pagination.tsx b/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-pagination.tsx deleted file mode 100644 index a78bea3..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-pagination.tsx +++ /dev/null @@ -1,30 +0,0 @@ -'use client'; - -import * as React from 'react'; -import TablePagination from '@mui/material/TablePagination'; - -function noop(): void { - return undefined; -} - -interface LessonCategoriesPaginationProps { - count: number; - page: number; -} - -export function LessonCategoriesPagination({ count, page }: LessonCategoriesPaginationProps): 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. - - return ( - - ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-selection-context.tsx b/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-selection-context.tsx deleted file mode 100644 index 26fefa6..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-selection-context.tsx +++ /dev/null @@ -1,47 +0,0 @@ -'use client'; - -import * as React from 'react'; - -import { useSelection } from '@/hooks/use-selection'; -import type { Selection } from '@/hooks/use-selection'; - -import type { LessonCategory } from './lesson-categories-table'; - -function noop(): void { - return undefined; -} - -export interface LessonCategoriesSelectionContextValue extends Selection {} - -export const LessonCategoriesSelectionContext = React.createContext({ - deselectAll: noop, - deselectOne: noop, - selectAll: noop, - selectOne: noop, - selected: new Set(), - selectedAny: false, - selectedAll: false, -}); - -interface LessonCategoriesSelectionProviderProps { - children: React.ReactNode; - lessonCategories: LessonCategory[]; -} - -export function LessonCategoriesSelectionProvider({ - children, - lessonCategories = [], -}: LessonCategoriesSelectionProviderProps): React.JSX.Element { - const customerIds = React.useMemo(() => lessonCategories.map((customer) => customer.id), [lessonCategories]); - const selection = useSelection(customerIds); - - return ( - - {children} - - ); -} - -export function useLessonCategoriesSelection(): LessonCategoriesSelectionContextValue { - return React.useContext(LessonCategoriesSelectionContext); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-table.tsx b/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-table.tsx deleted file mode 100644 index 9ee83d8..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/lesson-categories-table.tsx +++ /dev/null @@ -1,139 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Chip from '@mui/material/Chip'; -import IconButton from '@mui/material/IconButton'; -import LinearProgress from '@mui/material/LinearProgress'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle'; -import { Clock as ClockIcon } from '@phosphor-icons/react/dist/ssr/Clock'; -import { Minus as MinusIcon } from '@phosphor-icons/react/dist/ssr/Minus'; -import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; - -import { paths } from '@/paths'; -import { dayjs } from '@/lib/dayjs'; -import { DataTable } from '@/components/core/data-table'; -import type { ColumnDef } from '@/components/core/data-table'; - -import { useLessonCategoriesSelection } from './lesson-categories-selection-context'; - -export interface LessonCategory { - id: string; - name: string; - avatar?: string; - email: string; - phone?: string; - quota: number; - status: 'pending' | 'active' | 'blocked'; - createdAt: Date; -} - -const columns = [ - { - formatter: (row): React.JSX.Element => ( - - {' '} -
- - {row.name} - - - {row.email} - -
-
- ), - name: 'Name', - width: '250px', - }, - { - formatter: (row): React.JSX.Element => ( - - - - {new Intl.NumberFormat('en-US', { style: 'percent', maximumFractionDigits: 2 }).format(row.quota / 100)} - - - ), - name: 'Quota', - width: '250px', - }, - { field: 'phone', name: 'Phone number', width: '150px' }, - { - formatter(row) { - return dayjs(row.createdAt).format('MMM D, YYYY h:mm A'); - }, - name: 'Created at', - width: '200px', - }, - { - formatter: (row): React.JSX.Element => { - const mapping = { - active: { label: 'Active', icon: }, - blocked: { label: 'Blocked', icon: }, - pending: { label: 'Pending', icon: }, - } as const; - const { label, icon } = mapping[row.status] ?? { label: 'Unknown', icon: null }; - - return ; - }, - name: 'Status', - width: '150px', - }, - { - formatter: (): React.JSX.Element => ( - - - - ), - name: 'Actions', - hideName: true, - width: '100px', - align: 'right', - }, -] satisfies ColumnDef[]; - -export interface LessonCategoriesTableProps { - rows: LessonCategory[]; -} - -export function LessonCategoriesTable({ rows }: LessonCategoriesTableProps): React.JSX.Element { - const { deselectAll, deselectOne, selectAll, selectOne, selected } = useLessonCategoriesSelection(); - - return ( - - - columns={columns} - onDeselectAll={deselectAll} - onDeselectOne={(_, row) => { - deselectOne(row.id); - }} - onSelectAll={selectAll} - onSelectOne={(_, row) => { - selectOne(row.id); - }} - rows={rows} - selectable - selected={selected} - /> - {!rows.length ? ( - - - No lesson categories found - - - ) : null} - - ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/lesson-category-create-form.tsx b/002_source/cms/src/components/dashboard/lesson_category/lesson-category-create-form.tsx deleted file mode 100644 index c8c4f35..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/lesson-category-create-form.tsx +++ /dev/null @@ -1,398 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import { useRouter } from 'next/navigation'; -import { zodResolver } from '@hookform/resolvers/zod'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardActions from '@mui/material/CardActions'; -import CardContent from '@mui/material/CardContent'; -import Checkbox from '@mui/material/Checkbox'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -import FormControlLabel from '@mui/material/FormControlLabel'; -import FormHelperText from '@mui/material/FormHelperText'; -import InputLabel from '@mui/material/InputLabel'; -import OutlinedInput from '@mui/material/OutlinedInput'; -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 { Camera as CameraIcon } from '@phosphor-icons/react/dist/ssr/Camera'; -import { Controller, useForm } from 'react-hook-form'; -import { z as zod } from 'zod'; - -import { paths } from '@/paths'; -import { logger } from '@/lib/default-logger'; -import { Option } from '@/components/core/option'; -import { toast } from '@/components/core/toaster'; - -function fileToBase64(file: Blob): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = () => { - resolve(reader.result as string); - }; - reader.onerror = () => { - reject(new Error('Error converting file to base64')); - }; - }); -} - -const schema = zod.object({ - avatar: zod.string().optional(), - name: zod.string().min(1, 'Name is required').max(255), - email: zod.string().email('Must be a valid email').min(1, 'Email is required').max(255), - phone: zod.string().min(1, 'Phone is required').max(15), - company: zod.string().max(255), - billingAddress: zod.object({ - country: zod.string().min(1, 'Country is required').max(255), - state: zod.string().min(1, 'State is required').max(255), - city: zod.string().min(1, 'City is required').max(255), - zipCode: zod.string().min(1, 'Zip code is required').max(255), - line1: zod.string().min(1, 'Street line 1 is required').max(255), - line2: zod.string().max(255).optional(), - }), - taxId: zod.string().max(255).optional(), - timezone: zod.string().min(1, 'Timezone is required').max(255), - language: zod.string().min(1, 'Language is required').max(255), - currency: zod.string().min(1, 'Currency is required').max(255), -}); - -type Values = zod.infer; - -const defaultValues = { - avatar: '', - name: '', - email: '', - phone: '', - company: '', - billingAddress: { country: '', state: '', city: '', zipCode: '', line1: '', line2: '' }, - taxId: '', - timezone: 'new_york', - language: 'en', - currency: 'USD', -} satisfies Values; - -export function CustomerCreateForm(): React.JSX.Element { - const router = useRouter(); - - const { - control, - handleSubmit, - formState: { errors }, - setValue, - watch, - } = useForm({ defaultValues, resolver: zodResolver(schema) }); - - const onSubmit = React.useCallback( - async (_: Values): Promise => { - try { - // Make API request - toast.success('Customer updated'); - router.push(paths.dashboard.lesson_categories.details('1')); - } catch (err) { - logger.error(err); - toast.error('Something went wrong!'); - } - }, - [router] - ); - - const avatarInputRef = React.useRef(null); - const avatar = watch('avatar'); - - const handleAvatarChange = React.useCallback( - async (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - - if (file) { - const url = await fileToBase64(file); - setValue('avatar', url); - } - }, - [setValue] - ); - - return ( -
- - - } spacing={4}> - - Account information - - - - - - - - - - Avatar - Min 400x400px, PNG or JPEG - - - - - - - ( - - Name - - {errors.name ? {errors.name.message} : null} - - )} - /> - - - ( - - Email address - - {errors.email ? {errors.email.message} : null} - - )} - /> - - - ( - - Phone number - - {errors.phone ? {errors.phone.message} : null} - - )} - /> - - - ( - - Company - - {errors.company ? {errors.company.message} : null} - - )} - /> - - - - - Billing information - - - ( - - Country - - {errors.billingAddress?.country ? ( - {errors.billingAddress?.country?.message} - ) : null} - - )} - /> - - - ( - - State - - {errors.billingAddress?.state ? ( - {errors.billingAddress?.state?.message} - ) : null} - - )} - /> - - - ( - - City - - {errors.billingAddress?.city ? ( - {errors.billingAddress?.city?.message} - ) : null} - - )} - /> - - - ( - - Zip code - - {errors.billingAddress?.zipCode ? ( - {errors.billingAddress?.zipCode?.message} - ) : null} - - )} - /> - - - ( - - Address - - {errors.billingAddress?.line1 ? ( - {errors.billingAddress?.line1?.message} - ) : null} - - )} - /> - - - ( - - Tax ID - - {errors.taxId ? {errors.taxId.message} : null} - - )} - /> - - - - - Shipping information - } label="Same as billing address" /> - - - Additional information - - - ( - - Timezone - - {errors.timezone ? {errors.timezone.message} : null} - - )} - /> - - - ( - - Language - - {errors.language ? {errors.language.message} : null} - - )} - /> - - - ( - - Currency - - {errors.currency ? {errors.currency.message} : null} - - )} - /> - - - - - - - - - - -
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/notifications.tsx b/002_source/cms/src/components/dashboard/lesson_category/notifications.tsx deleted file mode 100644 index a6c16bd..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/notifications.tsx +++ /dev/null @@ -1,101 +0,0 @@ -'use client'; - -import * as React from 'react'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import CardHeader from '@mui/material/CardHeader'; -import Chip from '@mui/material/Chip'; -import Select from '@mui/material/Select'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { EnvelopeSimple as EnvelopeSimpleIcon } from '@phosphor-icons/react/dist/ssr/EnvelopeSimple'; - -import { dayjs } from '@/lib/dayjs'; -import { DataTable } from '@/components/core/data-table'; -import type { ColumnDef } from '@/components/core/data-table'; -import { Option } from '@/components/core/option'; - -export interface Notification { - id: string; - type: string; - status: 'delivered' | 'pending' | 'failed'; - createdAt: Date; -} - -const columns = [ - { - formatter: (row): React.JSX.Element => ( - - {row.type} - - ), - name: 'Type', - width: '300px', - }, - { - formatter: (row): React.JSX.Element => { - const mapping = { - delivered: { label: 'Delivered', color: 'success' }, - pending: { label: 'Pending', color: 'warning' }, - failed: { label: 'Failed', color: 'error' }, - } as const; - const { label, color } = mapping[row.status] ?? { label: 'Unknown', color: 'secondary' }; - - return ; - }, - name: 'Status', - width: '200px', - }, - { - formatter: (row): React.JSX.Element => ( - - {dayjs(row.createdAt).format('MMM D, YYYY hh:mm A')} - - ), - name: 'Date', - align: 'right', - }, -] satisfies ColumnDef[]; - -export interface NotificationsProps { - notifications: Notification[]; -} - -export function Notifications({ notifications }: NotificationsProps): React.JSX.Element { - return ( - - - - - } - title="Notifications" - /> - - - - -
- -
-
- - - columns={columns} rows={notifications} /> - - -
-
-
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/payments.tsx b/002_source/cms/src/components/dashboard/lesson_category/payments.tsx deleted file mode 100644 index 0420d32..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/payments.tsx +++ /dev/null @@ -1,138 +0,0 @@ -'use client'; - -import * as React from 'react'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import CardHeader from '@mui/material/CardHeader'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus'; -import { ShoppingCartSimple as ShoppingCartSimpleIcon } from '@phosphor-icons/react/dist/ssr/ShoppingCartSimple'; - -import { dayjs } from '@/lib/dayjs'; -import type { ColumnDef } from '@/components/core/data-table'; -import { DataTable } from '@/components/core/data-table'; - -export interface Payment { - currency: string; - amount: number; - invoiceId: string; - status: 'pending' | 'completed' | 'canceled' | 'refunded'; - createdAt: Date; -} - -const columns = [ - { - formatter: (row): React.JSX.Element => ( - - {new Intl.NumberFormat('en-US', { style: 'currency', currency: row.currency }).format(row.amount)} - - ), - name: 'Amount', - width: '200px', - }, - { - formatter: (row): React.JSX.Element => { - const mapping = { - pending: { label: 'Pending', color: 'warning' }, - completed: { label: 'Completed', color: 'success' }, - canceled: { label: 'Canceled', color: 'error' }, - refunded: { label: 'Refunded', color: 'error' }, - } as const; - const { label, color } = mapping[row.status] ?? { label: 'Unknown', color: 'secondary' }; - - return ; - }, - name: 'Status', - width: '200px', - }, - { - formatter: (row): React.JSX.Element => { - return {row.invoiceId}; - }, - name: 'Invoice ID', - width: '150px', - }, - { - formatter: (row): React.JSX.Element => ( - - {dayjs(row.createdAt).format('MMM D, YYYY hh:mm A')} - - ), - name: 'Date', - align: 'right', - }, -] satisfies ColumnDef[]; - -export interface PaymentsProps { - ordersValue: number; - payments: Payment[]; - refundsValue: number; - totalOrders: number; -} - -export function Payments({ ordersValue, payments = [], refundsValue, totalOrders }: PaymentsProps): React.JSX.Element { - return ( - - }> - Create Payment - - } - avatar={ - - - - } - title="Payments" - /> - - - - } - spacing={3} - sx={{ justifyContent: 'space-between', p: 2 }} - > -
- - Total orders - - {new Intl.NumberFormat('en-US').format(totalOrders)} -
-
- - Orders value - - - {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(ordersValue)} - -
-
- - Refunds - - - {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(refundsValue)} - -
-
-
- - - columns={columns} rows={payments} /> - - -
-
-
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_category/shipping-address.tsx b/002_source/cms/src/components/dashboard/lesson_category/shipping-address.tsx deleted file mode 100644 index 8793e5c..0000000 --- a/002_source/cms/src/components/dashboard/lesson_category/shipping-address.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import Chip from '@mui/material/Chip'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; - -export interface Address { - id: string; - country: string; - state: string; - city: string; - zipCode: string; - street: string; - primary?: boolean; -} - -export interface ShippingAddressProps { - address: Address; -} - -export function ShippingAddress({ address }: ShippingAddressProps): React.ReactElement { - return ( - - - - - {address.street}, -
- {address.city}, {address.state}, {address.country}, -
- {address.zipCode} -
- - {address.primary ? : } - - -
-
-
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/ILessonType.tsx b/002_source/cms/src/components/dashboard/lesson_type/ILessonType.tsx deleted file mode 100644 index 65a4930..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/ILessonType.tsx +++ /dev/null @@ -1,57 +0,0 @@ -'use client'; - -import { dayjs } from '@/lib/dayjs'; - -export interface LessonType { - id: string; - name: string; - type: string; - pos: number; - visible: 'visible' | 'hidden'; - createdAt: Date; - // - // original - // id: string; - // name: string; - // - avatar?: string; - email: string; - phone?: string; - quota: number; - status: 'pending' | 'active' | 'blocked'; - // createdAt: Date; -} - -export const LessonTypeDefaultValue: LessonType = { - id: 'string', - name: 'string', - type: 'string', - pos: 1, - visible: 'visible', - createdAt: dayjs().toDate(), - // - // original - // id: 'string', - // name: 'string', - // - avatar: 'string', - email: 'string', - phone: 'string', - quota: 1, - status: 'pending', - // createdAt: Date; -}; - -export interface DBLessonType { - id: string; - name: string; - type: string; - pos: number; - visible: 'visible' | 'hidden'; - createdAt: Date; - created: 'string'; -} - -export interface Helloworld { - id: string; -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/confirm-delete-modal.tsx b/002_source/cms/src/components/dashboard/lesson_type/confirm-delete-modal.tsx deleted file mode 100644 index 1aea8ae..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/confirm-delete-modal.tsx +++ /dev/null @@ -1,104 +0,0 @@ -'use client'; - -import * as React from 'react'; -import { LoadingButton } from '@mui/lab'; -import { Button, Container, Modal, Paper } from '@mui/material'; -import Avatar from '@mui/material/Avatar'; -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 { useTranslation } from 'react-i18next'; - -import { deleteLessonType } from './http-actions'; - -export default function ConfirmDeleteModal({ - open, - setOpen, - idToDelete, - reloadRows, -}: { - open: boolean; - setOpen: (b: boolean) => void; - idToDelete: string; - reloadRows: () => void; -}): React.JSX.Element { - const { t } = useTranslation(); - - // const handleClose = () => setOpen(false); - function handleClose(): void { - setOpen(false); - } - - const [isDeleteing, setIsDeleteing] = React.useState(false); - const style = { - position: 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - }; - - const handleUserConfirmDelete = (): void => { - if (idToDelete) { - setIsDeleteing(true); - deleteLessonType(idToDelete) - .then(() => { - reloadRows(); - handleClose(); - }) - .catch((err) => { - // console.error(err); - setIsDeleteing(false); - }) - .finally(() => { - setIsDeleteing(false); - }); - } - }; - - return ( -
- - - - - - - - - - - {t('Delete Lesson Type ?')} - - {t('Are you sure you want to delete lesson type ?')} - - - - - { - handleUserConfirmDelete(); - }} - loading={isDeleteing} - > - {t('Delete')} - - - - - - - - -
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/helloworld.tsx b/002_source/cms/src/components/dashboard/lesson_type/helloworld.tsx deleted file mode 100644 index 3989cb1..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/helloworld.tsx +++ /dev/null @@ -1,3 +0,0 @@ -const helloworld = 'helloworld'; - -export { helloworld }; diff --git a/002_source/cms/src/components/dashboard/lesson_type/http-actions.ts b/002_source/cms/src/components/dashboard/lesson_type/http-actions.ts deleted file mode 100644 index f4042e5..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/http-actions.ts +++ /dev/null @@ -1,170 +0,0 @@ -'use client'; - -// store CRUD operations of lesson_types -// import { lessonTypesSampleData } from '@/app/dashboard/lesson_types/lesson-types-data'; -import axios, { AxiosResponse } from 'axios'; - -import { dayjs } from '@/lib/dayjs'; - -import { DBLessonType, LessonType } from './ILessonType'; -import { LessonTypeCreateForm, LessonTypeEditFormProps, RestLessonTypeUpdateForm } from './interfaces'; - -// { AxiosError } - -// const ERR_CANNOT_CONNECT_POCKETBASE = new AxiosError( -// 'Request failed with status code 500', -// AxiosError.ERR_BAD_RESPONSE -// ); - -export const defaultGetJsonHeaders = { - 'Content-Type': 'application/json', - 'cache-control': 'no-cache', -}; - -export const axiosGetJson = (url: string): Promise => { - return axios.get(url, { - headers: defaultGetJsonHeaders, - }); -}; - -const axiosUpdateJson = (url: string, jsonToUpdate: T): Promise => { - return axios.put(url, jsonToUpdate, { - headers: defaultGetJsonHeaders, - }); -}; - -interface ApiResponseItems { - data: { - items: T; - // ... other possible fields in data - }; - // ... other possible fields in response -} - -interface ApiResponseItem { - data: T; -} - -export interface LessonTypeEditForm { - name: string; - type: string; - pos: number; - visible: string; -} - -interface LessonTypeUpdateForm { - id: string; - name: string; - type: string; - pos: number; - visible: string; -} - -export async function listLessonTypes(): Promise { - const restResult = await axiosGetJson('/api/db/lesson_types/list'); - const { - data: { items: lessonTypes }, - } = restResult as ApiResponseItems; - - const output: LessonType[] = []; - for (const lessonType of lessonTypes) { - output.push({ - id: lessonType.id, - name: lessonType.name, - pos: lessonType.pos, - type: lessonType.type, - visible: lessonType.visible, - createdAt: dayjs(lessonType.created).toDate(), - // TODO: remove me - avatar: 'string', - email: 'string', - phone: 'string', - quota: 1, - status: 'pending', - }); - } - return output; -} - -export async function getLessonTypeById(id: string): Promise { - const restResult = await axiosGetJson(`/api/db/lesson_types/getById/${id}`); - const { data: lessonType } = restResult as ApiResponseItem; - - const output: LessonType = { - name: '', - id: '', - pos: 1, - type: '', - visible: 'visible', - createdAt: dayjs().toDate(), - // not used - email: '', - phone: '', - quota: 1, - status: 'pending', - avatar: 'string', - }; - output.id = lessonType.id; - output.name = lessonType.name; - output.pos = lessonType.pos; - output.type = lessonType.type; - output.visible = lessonType.visible; - output.createdAt = dayjs(lessonType.created).toDate(); - - return output; -} - -export async function updateLessonType(updateContent: LessonTypeEditFormProps, typeId: string): Promise { - const restResult = await axiosUpdateJson(`/api/db/lesson_types/update`, { - id: typeId, - data: updateContent, - }); - return restResult; -} - -export async function deleteLessonType(id: string): Promise { - const restResult = await axios.delete(`/api/db/lesson_types/delete`, { data: { id } }); - return restResult; -} - -export async function createLessonType(lessonTypeData: LessonTypeCreateForm): Promise { - const restResult = await axios.post(`/api/db/lesson_types/create`, { data: lessonTypeData }); - return restResult; -} - -// const createLessonType = async (lessonTypeData: any) => { -// let { data } = await axios.post('/api/db/lesson_types/helloworld', lessonTypeData, { -// headers: defaultGetJsonHeaders, -// }); -// return data; -// }; - -// const deleteLessonType = async (id: string) => { -// // throw ERR_CANNOT_CONNECT_POCKETBASE; - -// let { data } = await axios.delete(`/api/db/lesson_types/helloworld`, { -// headers: defaultGetJsonHeaders, -// data: { id }, -// }); - -// // axios.delete(`/api/db/lesson_types/helloworld`, -// return data; -// }; - -// const getLessonTypeById = async (id: string) => { -// let { data } = await axiosGetJson(`/api/db/lesson_types/getById/${id}`); -// return data; -// }; - -// const updateLessonType = async (id: string, lessonTypeData: any) => { -// let data_payload = { -// id, -// content: lessonTypeData, -// }; - -// // throw ERR_CANNOT_CONNECT_POCKETBASE; - -// let { data } = await axios.put('/api/db/lesson_types/helloworld', data_payload); - -// return data; -// }; diff --git a/002_source/cms/src/components/dashboard/lesson_type/interfaces.ts b/002_source/cms/src/components/dashboard/lesson_type/interfaces.ts deleted file mode 100644 index 5a74d89..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/interfaces.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface LessonTypeEditFormProps { - name: string; - type: string; - pos: number; - visible: string; -} - -export interface RestLessonTypeUpdateForm { - id: string; - data: LessonTypeEditFormProps; -} - -export interface LessonTypeCreateForm { - name: string; - type: string; - pos: number; - visible: string; -} - -export const LessonTypeCreateFormDefault: LessonTypeCreateForm = { - name: '', - type: '', - pos: 1, - visible: 'visible', -}; diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form copy.tsx.1 b/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form copy.tsx.1 deleted file mode 100644 index 9977546..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form copy.tsx.1 +++ /dev/null @@ -1,230 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import { useRouter } from 'next/navigation'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { LoadingButton } from '@mui/lab'; -import { MenuItem } from '@mui/material'; -// import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardActions from '@mui/material/CardActions'; -import CardContent from '@mui/material/CardContent'; -// import Checkbox from '@mui/material/Checkbox'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -// import FormControlLabel from '@mui/material/FormControlLabel'; -import FormHelperText from '@mui/material/FormHelperText'; -import InputLabel from '@mui/material/InputLabel'; -import OutlinedInput from '@mui/material/OutlinedInput'; -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 { 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'; - -import { paths } from '@/paths'; -import { logger } from '@/lib/default-logger'; -// import { Option } from '@/components/core/option'; -import { toast } from '@/components/core/toaster'; - -import { createLessonType } from './http-actions'; -import { LessonTypeCreateForm, LessonTypeCreateFormDefault } from './interfaces'; - -// import { createLessonType } from './httpActions'; - -// function fileToBase64(file: Blob): Promise { -// return new Promise((resolve, reject) => { -// const reader = new FileReader(); -// reader.readAsDataURL(file); -// reader.onload = () => { -// resolve(reader.result as string); -// }; -// reader.onerror = () => { -// reject(new Error('Error converting file to base64')); -// }; -// }); -// } - -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), - visible_to_user: zod.string().max(255), -}); - -type Values = zod.infer; - -const defaultValues = { - name: '', - type: '', - pos: '1', - visible_to_user: 'visible', -} satisfies Values; - -export function LessonTypeCreateForm(): React.JSX.Element { - const router = useRouter(); - const { t } = useTranslation(); - const [isCreating, setIsCreating] = React.useState(false); - - const { - control, - handleSubmit, - formState: { errors, isSubmitting, isSubmitted }, - setValue, - // watch, - } = useForm({ defaultValues, resolver: zodResolver(schema) }); - - const onSubmit = React.useCallback( - async (values: Values): Promise => { - setIsCreating(true); - const tempCreate: LessonTypeCreateForm = LessonTypeCreateFormDefault; - - tempCreate.name = values.name; - tempCreate.type = values.type; - tempCreate.pos = 1; - tempCreate.visible = 'visible'; - - createLessonType(tempCreate) - .then((res) => { - router.push(paths.dashboard.lesson_types.list); - toast.success(t('dashboard.lessonTypes.create.success')); - }) - .catch((err) => { - logger.error(err); - toast.error(t('dashboard.lessonTypes.create.error')); - setIsCreating(false); - }); - }, - [router] - ); - - const avatarInputRef = React.useRef(null); - // const avatar = watch('avatar'); - - const handleAvatarChange = React.useCallback( - async (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - - if (file) { - // const url = await fileToBase64(file); - // setValue('avatar', url); - } - }, - [setValue] - ); - - return ( -
- - - } spacing={4}> - - {t('dashboard.lessonTypes.create.typeInformation')} - - - - - - {t('dashboard.lessonTypes.create.avatar')} - {t('dashboard.lessonTypes.create.avatarRequirements')} - - - - - - - ( - - {t('dashboard.lessonTypes.create.name')} - - {errors.name ? {errors.name.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.create.type')} - - {errors.type ? {errors.type.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.create.position')} - - {errors.pos ? {errors.pos.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.create.visibleToUser')} - - - {errors.visible_to_user ? ( - {errors.visible_to_user.message} - ) : null} - - )} - /> - - - - - - - - - {t('dashboard.lessonTypes.create.createButton')} - - - -
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form.tsx deleted file mode 100644 index 9977546..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-create-form.tsx +++ /dev/null @@ -1,230 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import { useRouter } from 'next/navigation'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { LoadingButton } from '@mui/lab'; -import { MenuItem } from '@mui/material'; -// import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardActions from '@mui/material/CardActions'; -import CardContent from '@mui/material/CardContent'; -// import Checkbox from '@mui/material/Checkbox'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -// import FormControlLabel from '@mui/material/FormControlLabel'; -import FormHelperText from '@mui/material/FormHelperText'; -import InputLabel from '@mui/material/InputLabel'; -import OutlinedInput from '@mui/material/OutlinedInput'; -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 { 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'; - -import { paths } from '@/paths'; -import { logger } from '@/lib/default-logger'; -// import { Option } from '@/components/core/option'; -import { toast } from '@/components/core/toaster'; - -import { createLessonType } from './http-actions'; -import { LessonTypeCreateForm, LessonTypeCreateFormDefault } from './interfaces'; - -// import { createLessonType } from './httpActions'; - -// function fileToBase64(file: Blob): Promise { -// return new Promise((resolve, reject) => { -// const reader = new FileReader(); -// reader.readAsDataURL(file); -// reader.onload = () => { -// resolve(reader.result as string); -// }; -// reader.onerror = () => { -// reject(new Error('Error converting file to base64')); -// }; -// }); -// } - -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), - visible_to_user: zod.string().max(255), -}); - -type Values = zod.infer; - -const defaultValues = { - name: '', - type: '', - pos: '1', - visible_to_user: 'visible', -} satisfies Values; - -export function LessonTypeCreateForm(): React.JSX.Element { - const router = useRouter(); - const { t } = useTranslation(); - const [isCreating, setIsCreating] = React.useState(false); - - const { - control, - handleSubmit, - formState: { errors, isSubmitting, isSubmitted }, - setValue, - // watch, - } = useForm({ defaultValues, resolver: zodResolver(schema) }); - - const onSubmit = React.useCallback( - async (values: Values): Promise => { - setIsCreating(true); - const tempCreate: LessonTypeCreateForm = LessonTypeCreateFormDefault; - - tempCreate.name = values.name; - tempCreate.type = values.type; - tempCreate.pos = 1; - tempCreate.visible = 'visible'; - - createLessonType(tempCreate) - .then((res) => { - router.push(paths.dashboard.lesson_types.list); - toast.success(t('dashboard.lessonTypes.create.success')); - }) - .catch((err) => { - logger.error(err); - toast.error(t('dashboard.lessonTypes.create.error')); - setIsCreating(false); - }); - }, - [router] - ); - - const avatarInputRef = React.useRef(null); - // const avatar = watch('avatar'); - - const handleAvatarChange = React.useCallback( - async (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - - if (file) { - // const url = await fileToBase64(file); - // setValue('avatar', url); - } - }, - [setValue] - ); - - return ( -
- - - } spacing={4}> - - {t('dashboard.lessonTypes.create.typeInformation')} - - - - - - {t('dashboard.lessonTypes.create.avatar')} - {t('dashboard.lessonTypes.create.avatarRequirements')} - - - - - - - ( - - {t('dashboard.lessonTypes.create.name')} - - {errors.name ? {errors.name.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.create.type')} - - {errors.type ? {errors.type.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.create.position')} - - {errors.pos ? {errors.pos.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.create.visibleToUser')} - - - {errors.visible_to_user ? ( - {errors.visible_to_user.message} - ) : null} - - )} - /> - - - - - - - - - {t('dashboard.lessonTypes.create.createButton')} - - - -
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-edit-form.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-type-edit-form.tsx deleted file mode 100644 index 4846915..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-type-edit-form.tsx +++ /dev/null @@ -1,266 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import { useParams, useRouter } from 'next/navigation'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { LoadingButton } from '@mui/lab'; -import { MenuItem } from '@mui/material'; -// import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardActions from '@mui/material/CardActions'; -import CardContent from '@mui/material/CardContent'; -// import Checkbox from '@mui/material/Checkbox'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -// import FormControlLabel from '@mui/material/FormControlLabel'; -import FormHelperText from '@mui/material/FormHelperText'; -import InputLabel from '@mui/material/InputLabel'; -import OutlinedInput from '@mui/material/OutlinedInput'; -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 { 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'; - -import { paths } from '@/paths'; -import { logger } from '@/lib/default-logger'; -// import { Option } from '@/components/core/option'; -import { toast } from '@/components/core/toaster'; - -import { getLessonTypeById, updateLessonType } from './http-actions'; -// TODO: this may be wrong -import type { LessonType } from './ILessonType'; -import type { LessonTypeEditFormProps } from './interfaces'; - -// function fileToBase64(file: Blob): Promise { -// return new Promise((resolve, reject) => { -// const reader = new FileReader(); -// reader.readAsDataURL(file); -// reader.onload = () => { -// resolve(reader.result as string); -// }; -// reader.onerror = () => { -// reject(new Error('Error converting file to base64')); -// }; -// }); -// } - -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.number().min(1, 'Phone is required').max(15), - visible_to_user: zod.string().max(255), -}); - -type Values = zod.infer; - -const defaultValues = { - name: '', - type: '', - pos: 1, - visible_to_user: 'visible', -} satisfies Values; - -export function LessonTypeEditForm(): React.JSX.Element { - const router = useRouter(); - const { t } = useTranslation(); - const { typeId } = useParams<{ typeId: string }>(); - const [isUpdating, setIsUpdating] = React.useState(false); - - const { - control, - handleSubmit, - formState: { errors }, - setValue, - reset, - // watch, - } = useForm({ defaultValues, resolver: zodResolver(schema) }); - - const onSubmit = React.useCallback( - async (values: Values): Promise => { - setIsUpdating(true); - const tempUpdate: LessonTypeEditFormProps = { - name: values.name, - type: values.type, - pos: values.pos, - visible: values.visible_to_user ? 'visible' : 'hidden', - }; - - updateLessonType(tempUpdate, typeId) - .then((res) => { - logger.debug(res); - toast.success(t('dashboard.lessonTypes.update.success')); - setIsUpdating(false); - router.push(paths.dashboard.lesson_types.list); - }) - .catch((err) => { - logger.error(err); - toast.error('Something went wrong!'); - setIsUpdating(false); - }); - }, - [router] - ); - - const avatarInputRef = React.useRef(null); - // const avatar = watch('avatar'); - - const handleAvatarChange = React.useCallback( - async (event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - - if (file) { - // const url = await fileToBase64(file); - // setValue('avatar', url); - } - }, - [setValue] - ); - - React.useEffect(() => { - getLessonTypeById(typeId) - .then((lessonType: LessonType) => { - reset({ - name: lessonType.name, - type: lessonType.type, - pos: lessonType.pos, - visible_to_user: lessonType.visible, - }); - }) - .catch((err) => { - // console.error(err); - }); - }, []); - - return ( -
- - - } spacing={4}> - - {t('dashboard.lessonTypes.edit.typeInformation')} - - - - - {/* - - - - */} - - - {t('dashboard.lessonTypes.edit.avatar')} - {t('dashboard.lessonTypes.edit.avatarRequirements')} - - - - - - - ( - - {t('dashboard.lessonTypes.edit.name')} - - {errors.name ? {errors.name.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.edit.type')} - - {errors.type ? {errors.type.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.edit.position')} - - {errors.pos ? {errors.pos.message} : null} - - )} - /> - - - ( - - {t('dashboard.lessonTypes.edit.visibleToUser')} - - - {errors.visible_to_user ? ( - {errors.visible_to_user.message} - ) : null} - - )} - /> - - - - - - - - - {t('dashboard.lessonTypes.edit.updateButton')} - - - -
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-filters.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-filters.tsx deleted file mode 100644 index abb6f42..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-filters.tsx +++ /dev/null @@ -1,403 +0,0 @@ -'use client'; - -import * as React from 'react'; -import { useRouter } from 'next/navigation'; -import Button from '@mui/material/Button'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -import OutlinedInput from '@mui/material/OutlinedInput'; -import Select from '@mui/material/Select'; -import type { SelectChangeEvent } from '@mui/material/Select'; -import Stack from '@mui/material/Stack'; -import Tab from '@mui/material/Tab'; -import Tabs from '@mui/material/Tabs'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; - -import { paths } from '@/paths'; -import { FilterButton, FilterPopover, useFilterContext } from '@/components/core/filter-button'; -import { Option } from '@/components/core/option'; - -import { LessonType } from './ILessonType'; -import { useLessonTypesSelection } from './lesson-types-selection-context'; - -export interface Filters { - email?: string; - phone?: string; - status?: string; - name?: string; - visible?: string; - type?: string; -} - -export type SortDir = 'asc' | 'desc'; - -export interface LessonTypesFiltersProps { - filters?: Filters; - sortDir?: SortDir; - fullData: LessonType[]; -} - -export function LessonTypesFilters({ - filters = {}, - sortDir = 'desc', - fullData, -}: LessonTypesFiltersProps): React.JSX.Element { - const { t } = useTranslation(); - const { email, phone, status, name, visible, type } = filters; - - const router = useRouter(); - - const selection = useLessonTypesSelection(); - - function getVisible(): number { - return fullData.reduce((count, item: LessonType) => { - return item.visible === 'visible' ? count + 1 : count; - }, 0); - } - - function getHidden(): number { - return fullData.reduce((count, item: LessonType) => { - return item.visible === 'hidden' ? count + 1 : count; - }, 0); - } - - // The tabs should be generated using API data. - const tabs = [ - { label: 'All', value: '', count: fullData.length }, - // { label: 'Active', value: 'active', count: 3 }, - // { label: 'Pending', value: 'pending', count: 1 }, - // { label: 'Blocked', value: 'blocked', count: 1 }, - { label: t('visible'), value: 'visible', count: getVisible() }, - { label: t('hidden'), value: 'hidden', count: getHidden() }, - ] as const; - - const updateSearchParams = React.useCallback( - (newFilters: Filters, newSortDir: SortDir): void => { - const searchParams = new URLSearchParams(); - - if (newSortDir === 'asc') { - searchParams.set('sortDir', newSortDir); - } - - if (newFilters.status) { - searchParams.set('status', newFilters.status); - } - - if (newFilters.email) { - searchParams.set('email', newFilters.email); - } - - if (newFilters.phone) { - searchParams.set('phone', newFilters.phone); - } - - if (newFilters.name) { - searchParams.set('name', newFilters.name); - } - - if (newFilters.type) { - searchParams.set('type', newFilters.type); - } - - if (newFilters.visible) { - searchParams.set('visible', newFilters.visible); - } - - router.push(`${paths.dashboard.lesson_types.list}?${searchParams.toString()}`); - }, - [router] - ); - - const handleClearFilters = React.useCallback(() => { - updateSearchParams({}, sortDir); - }, [updateSearchParams, sortDir]); - - const handleStatusChange = React.useCallback( - (_: React.SyntheticEvent, value: string) => { - updateSearchParams({ ...filters, status: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handleVisibleChange = React.useCallback( - (_: React.SyntheticEvent, value: string) => { - updateSearchParams({ ...filters, visible: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handleNameChange = React.useCallback( - (value?: string) => { - updateSearchParams({ ...filters, name: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handleTypeChange = React.useCallback( - (value?: string) => { - updateSearchParams({ ...filters, type: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handleEmailChange = React.useCallback( - (value?: string) => { - updateSearchParams({ ...filters, email: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handlePhoneChange = React.useCallback( - (value?: string) => { - updateSearchParams({ ...filters, phone: value }, sortDir); - }, - [updateSearchParams, filters, sortDir] - ); - - const handleSortChange = React.useCallback( - (event: SelectChangeEvent) => { - updateSearchParams(filters, event.target.value as SortDir); - }, - [updateSearchParams, filters] - ); - - const hasFilters = status || email || phone || visible || name || type; - - return ( -
- - {tabs.map((tab) => ( - } - iconPosition="end" - key={tab.value} - label={tab.label} - sx={{ minHeight: 'auto' }} - tabIndex={0} - value={tab.value} - /> - ))} - - - - - { - handleNameChange(value as string); - }} - onFilterDelete={() => { - handleNameChange(); - }} - popover={} - value={name} - /> - - { - handleTypeChange(value as string); - }} - onFilterDelete={() => { - handleTypeChange(); - }} - popover={} - value={type} - /> - - {/* - { - handleEmailChange(value as string); - }} - onFilterDelete={() => { - handleEmailChange(); - }} - popover={} - value={email} - /> - */} - - {/* - { - handlePhoneChange(value as string); - }} - onFilterDelete={() => { - handlePhoneChange(); - }} - popover={} - value={phone} - /> - */} - - {hasFilters ? : null} - - {selection.selectedAny ? ( - - - {selection.selected.size} {t('selected')} - - - - ) : null} - - -
- ); -} - -function TypeFilterPopover(): React.JSX.Element { - const { t } = useTranslation(); - const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext(); - const [value, setValue] = React.useState(''); - - React.useEffect(() => { - setValue((initialValue as string | undefined) ?? ''); - }, [initialValue]); - - return ( - - - { - setValue(event.target.value); - }} - onKeyUp={(event) => { - if (event.key === 'Enter') { - onApply(value); - } - }} - value={value} - /> - - - - ); -} - -function NameFilterPopover(): React.JSX.Element { - const { t } = useTranslation(); - const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext(); - const [value, setValue] = React.useState(''); - - React.useEffect(() => { - setValue((initialValue as string | undefined) ?? ''); - }, [initialValue]); - - return ( - - - { - setValue(event.target.value); - }} - onKeyUp={(event) => { - if (event.key === 'Enter') { - onApply(value); - } - }} - value={value} - /> - - - - ); -} - -function EmailFilterPopover(): React.JSX.Element { - const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext(); - const [value, setValue] = React.useState(''); - - React.useEffect(() => { - setValue((initialValue as string | undefined) ?? ''); - }, [initialValue]); - - return ( - - - { - setValue(event.target.value); - }} - onKeyUp={(event) => { - if (event.key === 'Enter') { - onApply(value); - } - }} - value={value} - /> - - - - ); -} - -function PhoneFilterPopover(): React.JSX.Element { - const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext(); - const [value, setValue] = React.useState(''); - - React.useEffect(() => { - setValue((initialValue as string | undefined) ?? ''); - }, [initialValue]); - - return ( - - - { - setValue(event.target.value); - }} - onKeyUp={(event) => { - if (event.key === 'Enter') { - onApply(value); - } - }} - value={value} - /> - - - - ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-pagination.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-pagination.tsx deleted file mode 100644 index a124e14..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-pagination.tsx +++ /dev/null @@ -1,30 +0,0 @@ -'use client'; - -import * as React from 'react'; -import TablePagination from '@mui/material/TablePagination'; - -function noop(): void { - return undefined; -} - -interface LessonTypesPaginationProps { - count: number; - page: number; -} - -export function LessonTypesPagination({ count, page }: 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. - - return ( - - ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-selection-context.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-selection-context.tsx deleted file mode 100644 index e492466..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-selection-context.tsx +++ /dev/null @@ -1,45 +0,0 @@ -'use client'; - -import * as React from 'react'; - -import { useSelection } from '@/hooks/use-selection'; -import type { Selection } from '@/hooks/use-selection'; - -import { LessonType } from './ILessonType'; - -function noop(): void { - return undefined; -} - -export interface LessonTypesSelectionContextValue extends Selection {} - -export const LessonTypesSelectionContext = React.createContext({ - deselectAll: noop, - deselectOne: noop, - selectAll: noop, - selectOne: noop, - selected: new Set(), - selectedAny: false, - selectedAll: false, -}); - -interface LessonTypesSelectionProviderProps { - children: React.ReactNode; - lessonTypes: LessonType[]; -} - -export function LessonTypesSelectionProvider({ - children, - lessonTypes = [], -}: LessonTypesSelectionProviderProps): React.JSX.Element { - const lessonTypeIds = React.useMemo(() => lessonTypes.map((lessonType) => lessonType.id), [lessonTypes]); - const selection = useSelection(lessonTypeIds); - - return ( - {children} - ); -} - -export function useLessonTypesSelection(): LessonTypesSelectionContextValue { - return React.useContext(LessonTypesSelectionContext); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-table.tsx b/002_source/cms/src/components/dashboard/lesson_type/lesson-types-table.tsx deleted file mode 100644 index 63541b8..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/lesson-types-table.tsx +++ /dev/null @@ -1,162 +0,0 @@ -'use client'; - -import * as React from 'react'; -import RouterLink from 'next/link'; -import { Button } from '@mui/material'; -import Box from '@mui/material/Box'; -import Chip from '@mui/material/Chip'; -import IconButton from '@mui/material/IconButton'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle'; -import { Clock as ClockIcon } from '@phosphor-icons/react/dist/ssr/Clock'; -import { Minus as MinusIcon } from '@phosphor-icons/react/dist/ssr/Minus'; -import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; -import { TrashSimple as TrashSimpleIcon } from '@phosphor-icons/react/dist/ssr/TrashSimple'; -import { useTranslation } from 'react-i18next'; -import { toast } from 'sonner'; - -import { paths } from '@/paths'; -import { dayjs } from '@/lib/dayjs'; -import { i18n } from '@/lib/i18n'; -import { DataTable } from '@/components/core/data-table'; -import type { ColumnDef } from '@/components/core/data-table'; - -import ConfirmDeleteModal from './confirm-delete-modal'; -import type { LessonType } from './ILessonType'; -import { useLessonTypesSelection } from './lesson-types-selection-context'; - -function columns(handleDeleteClick: (testId: string) => void): ColumnDef[] { - return [ - { - formatter: (row): React.JSX.Element => ( - -
- - {row.name} - -
-
- ), - name: 'Name', - width: '250px', - }, - { 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 - const { t } = useTranslation(); - - const mapping = { - active: { label: 'Active', icon: }, - blocked: { label: 'Blocked', icon: }, - pending: { label: 'Pending', icon: }, - visible: { - label: t('visible'), - icon: , - }, - hidden: { - label: t('hidden'), - icon: , - }, - } as const; - - const { label, icon } = mapping[row.visible] ?? { label: 'Unknown', icon: null }; - - return ( - - ); - }, - name: 'visible', - width: '150px', - }, - { - formatter(row) { - return dayjs(row.createdAt).format('MMM D, YYYY h:mm A'); - }, - name: 'Created at', - width: '200px', - }, - - { - formatter: (row): React.JSX.Element => ( - - - - - { - handleDeleteClick(row.id); - }} - > - - - - ), - name: 'Actions', - width: '100px', - align: 'right', - }, - ]; -} - -export interface LessonTypesTableProps { - rows: LessonType[]; - reloadRows: () => void; -} - -export function LessonTypesTable({ rows, reloadRows }: LessonTypesTableProps): React.JSX.Element { - const { t } = useTranslation(); - const { deselectAll, deselectOne, selectAll, selectOne, selected } = useLessonTypesSelection(); - - const [idToDelete, setIdToDelete] = React.useState(''); - const [open, setOpen] = React.useState(false); - - function handleDeleteClick(testId: string): void { - setOpen(true); - setIdToDelete(testId); - } - - return ( - - - - columns={columns(handleDeleteClick)} - onDeselectAll={deselectAll} - onDeselectOne={(_, row) => { - deselectOne(row.id); - }} - onSelectAll={selectAll} - onSelectOne={(_, row) => { - selectOne(row.id); - }} - rows={rows} - selectable - selected={selected} - /> - {!rows.length ? ( - - - {t('No lesson types found')} - - - ) : null} - - ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/notifications.tsx b/002_source/cms/src/components/dashboard/lesson_type/notifications.tsx deleted file mode 100644 index a6c16bd..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/notifications.tsx +++ /dev/null @@ -1,101 +0,0 @@ -'use client'; - -import * as React from 'react'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import CardHeader from '@mui/material/CardHeader'; -import Chip from '@mui/material/Chip'; -import Select from '@mui/material/Select'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { EnvelopeSimple as EnvelopeSimpleIcon } from '@phosphor-icons/react/dist/ssr/EnvelopeSimple'; - -import { dayjs } from '@/lib/dayjs'; -import { DataTable } from '@/components/core/data-table'; -import type { ColumnDef } from '@/components/core/data-table'; -import { Option } from '@/components/core/option'; - -export interface Notification { - id: string; - type: string; - status: 'delivered' | 'pending' | 'failed'; - createdAt: Date; -} - -const columns = [ - { - formatter: (row): React.JSX.Element => ( - - {row.type} - - ), - name: 'Type', - width: '300px', - }, - { - formatter: (row): React.JSX.Element => { - const mapping = { - delivered: { label: 'Delivered', color: 'success' }, - pending: { label: 'Pending', color: 'warning' }, - failed: { label: 'Failed', color: 'error' }, - } as const; - const { label, color } = mapping[row.status] ?? { label: 'Unknown', color: 'secondary' }; - - return ; - }, - name: 'Status', - width: '200px', - }, - { - formatter: (row): React.JSX.Element => ( - - {dayjs(row.createdAt).format('MMM D, YYYY hh:mm A')} - - ), - name: 'Date', - align: 'right', - }, -] satisfies ColumnDef[]; - -export interface NotificationsProps { - notifications: Notification[]; -} - -export function Notifications({ notifications }: NotificationsProps): React.JSX.Element { - return ( - - - - - } - title="Notifications" - /> - - - - -
- -
-
- - - columns={columns} rows={notifications} /> - - -
-
-
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/payments.tsx b/002_source/cms/src/components/dashboard/lesson_type/payments.tsx deleted file mode 100644 index 0420d32..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/payments.tsx +++ /dev/null @@ -1,138 +0,0 @@ -'use client'; - -import * as React from 'react'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import CardHeader from '@mui/material/CardHeader'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import Link from '@mui/material/Link'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus'; -import { ShoppingCartSimple as ShoppingCartSimpleIcon } from '@phosphor-icons/react/dist/ssr/ShoppingCartSimple'; - -import { dayjs } from '@/lib/dayjs'; -import type { ColumnDef } from '@/components/core/data-table'; -import { DataTable } from '@/components/core/data-table'; - -export interface Payment { - currency: string; - amount: number; - invoiceId: string; - status: 'pending' | 'completed' | 'canceled' | 'refunded'; - createdAt: Date; -} - -const columns = [ - { - formatter: (row): React.JSX.Element => ( - - {new Intl.NumberFormat('en-US', { style: 'currency', currency: row.currency }).format(row.amount)} - - ), - name: 'Amount', - width: '200px', - }, - { - formatter: (row): React.JSX.Element => { - const mapping = { - pending: { label: 'Pending', color: 'warning' }, - completed: { label: 'Completed', color: 'success' }, - canceled: { label: 'Canceled', color: 'error' }, - refunded: { label: 'Refunded', color: 'error' }, - } as const; - const { label, color } = mapping[row.status] ?? { label: 'Unknown', color: 'secondary' }; - - return ; - }, - name: 'Status', - width: '200px', - }, - { - formatter: (row): React.JSX.Element => { - return {row.invoiceId}; - }, - name: 'Invoice ID', - width: '150px', - }, - { - formatter: (row): React.JSX.Element => ( - - {dayjs(row.createdAt).format('MMM D, YYYY hh:mm A')} - - ), - name: 'Date', - align: 'right', - }, -] satisfies ColumnDef[]; - -export interface PaymentsProps { - ordersValue: number; - payments: Payment[]; - refundsValue: number; - totalOrders: number; -} - -export function Payments({ ordersValue, payments = [], refundsValue, totalOrders }: PaymentsProps): React.JSX.Element { - return ( - - }> - Create Payment - - } - avatar={ - - - - } - title="Payments" - /> - - - - } - spacing={3} - sx={{ justifyContent: 'space-between', p: 2 }} - > -
- - Total orders - - {new Intl.NumberFormat('en-US').format(totalOrders)} -
-
- - Orders value - - - {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(ordersValue)} - -
-
- - Refunds - - - {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(refundsValue)} - -
-
-
- - - columns={columns} rows={payments} /> - - -
-
-
- ); -} diff --git a/002_source/cms/src/components/dashboard/lesson_type/shipping-address.tsx b/002_source/cms/src/components/dashboard/lesson_type/shipping-address.tsx deleted file mode 100644 index 8793e5c..0000000 --- a/002_source/cms/src/components/dashboard/lesson_type/shipping-address.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material/Button'; -import Card from '@mui/material/Card'; -import CardContent from '@mui/material/CardContent'; -import Chip from '@mui/material/Chip'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; - -export interface Address { - id: string; - country: string; - state: string; - city: string; - zipCode: string; - street: string; - primary?: boolean; -} - -export interface ShippingAddressProps { - address: Address; -} - -export function ShippingAddress({ address }: ShippingAddressProps): React.ReactElement { - return ( - - - - - {address.street}, -
- {address.city}, {address.state}, {address.country}, -
- {address.zipCode} -
- - {address.primary ? : } - - -
-
-
- ); -}