update build ok,

This commit is contained in:
louiscklaw
2025-04-22 02:05:08 +08:00
parent f4f4a0eb7c
commit 374e3de59b
11 changed files with 6 additions and 643 deletions

View File

@@ -12,6 +12,7 @@
**/*.log
**/*.tmp
**/*.del
**/*.plan
**/_archive
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

View File

@@ -1,79 +0,0 @@
'use client';
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import Card from '@mui/material/Card';
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 { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple';
import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User';
import { useTranslation } from 'react-i18next';
import { PropertyItem } from '@/components/core/property-item';
import { PropertyList } from '@/components/core/property-list';
import { LpQuestion } from '@/components/dashboard/lp_questions_del/type';
export default function BasicDetailCard({
lpModel: model,
handleEditClick,
}: {
lpModel: LpQuestion;
handleEditClick: () => void;
}): React.JSX.Element {
const { t } = useTranslation();
return (
<Card>
<CardHeader
action={
<IconButton
onClick={() => {
handleEditClick();
}}
>
<PencilSimpleIcon />
</IconButton>
}
avatar={
<Avatar>
<UserIcon fontSize="var(--Icon-fontSize)" />
</Avatar>
}
title={t('list.basic-details')}
/>
<PropertyList
divider={<Divider />}
orientation="vertical"
sx={{ '--PropertyItem-padding': '12px 24px' }}
>
{(
[
{
key: 'Customer ID',
value: (
<Chip
label={model.id}
size="small"
variant="soft"
/>
),
},
{ key: 'Name', value: model.question_name },
{ key: 'Remarks', value: model.remarks },
{ key: 'Description', value: model.description },
] satisfies { key: string; value: React.ReactNode }[]
).map(
(item): React.JSX.Element => (
<PropertyItem
key={item.key}
name={item.key}
value={item.value}
/>
)
)}
</PropertyList>
</Card>
);
}

View File

@@ -1,73 +0,0 @@
'use client';
import * as React from 'react';
import { Button } from '@mui/material';
import Avatar from '@mui/material/Avatar';
import Chip from '@mui/material/Chip';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown';
import { CheckCircle as CheckCircleIcon } from '@phosphor-icons/react/dist/ssr/CheckCircle';
import { useTranslation } from 'react-i18next';
import { LpQuestion } from '@/components/dashboard/lp_questions_del/type';
function getImageUrlFrRecord(record: LpQuestion): string {
return `http://127.0.0.1:8090/api/files/${record.collectionId}/${record.id}/${record.question_image}`;
}
export default function SampleTitleCard({ lpModel }: { lpModel: LpQuestion }): React.JSX.Element {
const { t } = useTranslation();
return (
<>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flex: '1 1 auto' }}
>
<Avatar
variant="rounded"
src={getImageUrlFrRecord(lpModel)}
sx={{ '--Avatar-size': '64px' }}
>
{t('empty')}
</Avatar>
<div>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flexWrap: 'wrap' }}
>
<Typography variant="h4">{lpModel.question_name}</Typography>
<Chip
icon={
<CheckCircleIcon
color="var(--mui-palette-success-main)"
weight="fill"
/>
}
label={lpModel.visible}
size="small"
variant="outlined"
/>
</Stack>
<Typography
color="text.secondary"
variant="body1"
>
{lpModel.slug}
</Typography>
</div>
</Stack>
<div>
<Button
endIcon={<CaretDownIcon />}
variant="contained"
>
{t('list.action')}
</Button>
</div>
</>
);
}

View File

@@ -1,138 +0,0 @@
'use client';
import * as React from 'react';
import RouterLink from 'next/link';
import { useParams, useRouter } from 'next/navigation';
import SampleAddressCard from '@/app/dashboard/Sample/AddressCard';
import { SampleNotifications } from '@/app/dashboard/Sample/Notifications';
import SamplePaymentCard from '@/app/dashboard/Sample/SamplePaymentCard';
import SampleSecurityCard from '@/app/dashboard/Sample/SampleSecurityCard';
import { COL_QUIZ_LP_QUESTIONS } from '@/constants';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Stack from '@mui/material/Stack';
import Grid from '@mui/material/Unstable_Grid2';
import { ArrowLeft as ArrowLeftIcon } from '@phosphor-icons/react/dist/ssr/ArrowLeft';
import type { RecordModel } from 'pocketbase';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import { toast } from '@/components/core/toaster';
import ErrorDisplay from '@/components/dashboard/error';
import { defaultLpQuestion } from '@/components/dashboard/lp_questions_del/_constants';
import { Notifications } from '@/components/dashboard/lp_questions_del/notifications';
import type { LpQuestion } from '@/components/dashboard/lp_questions_del/type';
import FormLoading from '@/components/loading';
import BasicDetailCard from './BasicDetailCard';
import TitleCard from './TitleCard';
export default function Page(): React.JSX.Element {
const { t } = useTranslation();
const router = useRouter();
//
const { cat_id: catId } = useParams<{ cat_id: string }>();
//
const [showLoading, setShowLoading] = React.useState<boolean>(true);
const [showError, setShowError] = React.useState({ show: false, detail: '' });
//
const [showLessonQuestion, setShowLessonQuestion] = React.useState<LpQuestion>(defaultLpQuestion);
function handleEditClick() {
router.push(paths.dashboard.lp_questions.edit(showLessonQuestion.id));
}
React.useEffect(() => {
if (catId) {
pb.collection(COL_QUIZ_LP_QUESTIONS)
.getOne(catId)
.then((model: RecordModel) => {
setShowLessonQuestion({ ...defaultLpQuestion, ...model });
})
.catch((err) => {
logger.error(err);
toast(t('list.error'));
setShowError({ show: true, detail: JSON.stringify(err) });
})
.finally(() => {
setShowLoading(false);
});
}
}, [catId]);
if (showLoading) return <FormLoading />;
if (showError.show)
return (
<ErrorDisplay
message={t('error.unable-to-process-request')}
code="500"
details={showError.detail}
/>
);
return (
<Box
sx={{
maxWidth: 'var(--Content-maxWidth)',
m: 'var(--Content-margin)',
p: 'var(--Content-padding)',
width: 'var(--Content-width)',
}}
>
<Stack spacing={4}>
<Stack spacing={3}>
<div>
<Link
color="text.primary"
component={RouterLink}
href={paths.dashboard.lp_questions.list}
sx={{ alignItems: 'center', display: 'inline-flex', gap: 1 }}
variant="subtitle2"
>
<ArrowLeftIcon fontSize="var(--icon-fontSize-md)" />
{t('list.title')}
</Link>
</div>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={3}
sx={{ alignItems: 'flex-start' }}
>
<TitleCard lpModel={showLessonQuestion} />
</Stack>
</Stack>
<Grid
container
spacing={4}
>
<Grid
lg={4}
xs={12}
>
<Stack spacing={4}>
<BasicDetailCard
lpModel={showLessonQuestion}
handleEditClick={handleEditClick}
/>
<SampleSecurityCard />
</Stack>
</Grid>
<Grid
lg={8}
xs={12}
>
<Stack spacing={4}>
<SamplePaymentCard />
<SampleAddressCard />
<Notifications notifications={SampleNotifications} />
</Stack>
</Grid>
</Grid>
</Stack>
</Box>
);
}

View File

@@ -1,17 +0,0 @@
please help to review the `tsx` file in this folder
`/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/app/dashboard/lp/questions`
it was clone from
`category`/`categories`, `lp_category`/`lp_categories`
please help to modify to `question`/`questions`, `lp_question`/`lp_questions`
please also help to modify the name of
`variables`, `constants`, `functions`, `classes`, components's name, paths
the db fields structures are the same
do not move the files
do not create directories
keep current folder structure is important
thanks

View File

@@ -1,53 +0,0 @@
'use client';
// RULES:
// T.B.A.
//
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 { LpQuestionCreateForm } from '@/components/dashboard/lp_questions_del/lp-question-create-form';
export default function Page(): React.JSX.Element {
// RULES: follow the name of page directory
const { t } = useTranslation(['lp_questions']);
return (
<Box
sx={{
maxWidth: 'var(--Content-maxWidth)',
m: 'var(--Content-margin)',
p: 'var(--Content-padding)',
width: 'var(--Content-width)',
}}
>
<Stack spacing={4}>
<Stack spacing={3}>
<div>
<Link
color="text.primary"
component={RouterLink}
href={paths.dashboard.lp_questions.list}
sx={{ alignItems: 'center', display: 'inline-flex', gap: 1 }}
variant="subtitle2"
>
<ArrowLeftIcon fontSize="var(--icon-fontSize-md)" />
{t('title')}
</Link>
</div>
<div>
<Typography variant="h4">{t('create.title')}</Typography>
</div>
</Stack>
<LpCategoryCreateForm />
</Stack>
</Box>
);
}

View File

@@ -1,11 +0,0 @@
# task
## instruction
with reference to `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/app/_helloworld/page.tsx`
with reference to `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/app/dashboard/lesson_types/edit/[typeId]/page.tsx`
please modify `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/app/dashboard/lesson_categories/edit/page.tsx`
please draft a tsx for showing error to user thanks,

View File

@@ -1,53 +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 { LpQuestionEditForm } from '@/components/dashboard/lp_questions_del/lp-question-edit-form';
export default function Page(): React.JSX.Element {
const { t } = useTranslation(['lp_questions']);
React.useEffect(() => {
// console.log('helloworld');
}, []);
return (
<Box
sx={{
maxWidth: 'var(--Content-maxWidth)',
m: 'var(--Content-margin)',
p: 'var(--Content-padding)',
width: 'var(--Content-width)',
}}
>
<Stack spacing={4}>
<Stack spacing={3}>
<div>
<Link
color="text.primary"
component={RouterLink}
href={paths.dashboard.lp_questions.list}
sx={{ alignItems: 'center', display: 'inline-flex', gap: 1 }}
variant="subtitle2"
>
<ArrowLeftIcon fontSize="var(--icon-fontSize-md)" />
{t('edit.title')}
</Link>
</div>
<div>
<Typography variant="h4">{t('edit.title')}</Typography>
</div>
</Stack>
<LpCategoryEditForm />
</Stack>
</Box>
);
}

View File

@@ -1,213 +0,0 @@
'use client';
// RULES:
// contains list page for lp_questions (QuizLPQuestions)
// contain definition to collection only
//
import * as React from 'react';
import { useRouter } from 'next/navigation';
import { COL_QUIZ_LP_QUESTIONS } from '@/constants';
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 type { ListResult, RecordModel } from 'pocketbase';
import { useTranslation } from 'react-i18next';
import { paths } from '@/paths';
import { logger } from '@/lib/default-logger';
import { pb } from '@/lib/pb';
import { toast } from '@/components/core/toaster';
import ErrorDisplay from '@/components/dashboard/error';
import { defaultLpQuestion } from '@/components/dashboard/lp_questions_del/_constants';
import { LpQuestionsFilters } from '@/components/dashboard/lp_questions_del/lp-questions-filters';
import type { Filters } from '@/components/dashboard/lp_questions_del/lp-questions-filters';
import { LpQuestionsPagination } from '@/components/dashboard/lp_questions_del/lp-questions-pagination';
import { LpQuestionsSelectionProvider } from '@/components/dashboard/lp_questions_del/lp-questions-selection-context';
import { LpQuestionsTable } from '@/components/dashboard/lp_questions_del/lp-questions-table';
import type { LpQuestion } from '@/components/dashboard/lp_questions_del/type';
import FormLoading from '@/components/loading';
export default function Page({ searchParams }: PageProps): React.JSX.Element {
const { t } = useTranslation(['lp_questions']);
const { email, phone, sortDir, status, name, visible, type } = searchParams;
const router = useRouter();
const [lessonQuestionsData, setLessonQuestionsData] = React.useState<LpQuestion[]>([]);
//
const [isLoadingAddPage, setIsLoadingAddPage] = React.useState<boolean>(false);
const [showLoading, setShowLoading] = React.useState<boolean>(true);
const [showError, setShowError] = React.useState({ show: false, detail: '' });
//
const [rowsPerPage, setRowsPerPage] = React.useState<number>(5);
const [filteredQuestions, setFilteredQuestions] = React.useState<LpQuestion[]>([]);
const [currentPage, setCurrentPage] = React.useState<number>(1);
const [recordCount, setRecordCount] = React.useState<number>(0);
const [listOption, setListOption] = React.useState({});
const [listSort, setListSort] = React.useState({});
//
const sortedLessonQuestions = applySort(lessonQuestionsData, sortDir);
const filteredLessonQuestions = applyFilters(sortedLessonQuestions, { email, phone, status });
const reloadRows = async (): Promise<void> => {
try {
const models: ListResult<RecordModel> = await pb
.collection(COL_QUIZ_LP_QUESTIONS)
.getList(currentPage + 1, rowsPerPage, {});
const { items, totalItems } = models;
const tempLessonTypes: LpQuestion[] = items.map((lt) => {
return { ...defaultLpQuestion, ...lt };
});
setLessonQuestionsData(tempLessonTypes);
setRecordCount(totalItems);
setF(tempLessonTypes);
console.log({ currentPage, f });
} catch (error) {
//
setShowError({ show: true, detail: JSON.stringify(error) });
} finally {
setShowLoading(false);
}
};
React.useEffect(() => {
void reloadRows();
}, [currentPage, rowsPerPage, listOption]);
if (showLoading) return <FormLoading />;
if (showError.show)
return (
<ErrorDisplay
message={t('error.unable-to-process-request')}
code="500"
details={showError.detail}
/>
);
return (
<Box
sx={{
maxWidth: 'var(--Content-maxWidth)',
m: 'var(--Content-margin)',
p: 'var(--Content-padding)',
width: 'var(--Content-width)',
}}
>
<Stack spacing={4}>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={3}
sx={{ alignItems: 'flex-start' }}
>
<Box sx={{ flex: '1 1 auto' }}>
<Typography variant="h4">{t('list.title')}</Typography>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<LoadingButton
loading={isLoadingAddPage}
onClick={(): void => {
setIsLoadingAddPage(true);
router.push(paths.dashboard.lp_questions.create);
}}
startIcon={<PlusIcon />}
variant="contained"
>
{t('list.add')}
</LoadingButton>
</Box>
</Stack>
<LpQuestionsSelectionProvider lessonQuestions={filteredQuestions}>
<Card>
<LpQuestionsFilters
filters={{ email, phone, status, name, visible, type }}
fullData={lessonQuestionsData}
sortDir={sortDir}
/>
<Divider />
<Box sx={{ overflowX: 'auto' }}>
<LpQuestionsTable
reloadRows={reloadRows}
rows={filteredQuestions}
/>
</Box>
<Divider />
<LpQuestionsPagination
count={recordCount}
page={currentPage}
rowsPerPage={rowsPerPage}
setPage={setCurrentPage}
setRowsPerPage={setRowsPerPage}
/>
</Card>
</LpQuestionsSelectionProvider>
</Stack>
</Box>
);
}
// Sorting and filtering has to be done on the server.
function applySort(row: LpQuestion[], sortDir: 'asc' | 'desc' | undefined): LpQuestion[] {
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: LpQuestion[], { email, phone, status, name, visible }: Filters): LpQuestion[] {
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;
});
}
interface PageProps {
searchParams: {
email?: string;
phone?: string;
sortDir?: 'asc' | 'desc';
status?: string;
name?: string;
visible?: string;
type?: string;
//
};
}

View File

@@ -25,8 +25,10 @@ this is the content of `@/constants`
- read, remember and link up the ideas in file stated above,
i will tell them the task afterwards
The software engineer will provide solutions, while QA engineer will feedback the opinion.
---
The software engineer will provide solutions,
while QA engineer will feedback the opinion.
this is now not in debug phase,
so, no need to reply me what they are going on or their insight throught the prompt.

View File

@@ -2,11 +2,8 @@ import { COL_QUIZ_LP_CATEGORIES } from '@/constants';
import type { RecordModel } from 'pocketbase';
import { pb } from '@/lib/pb';
import { CreateFormProps } from '@/components/dashboard/lp_categories/type';
interface CreateForm {
// TODO: Add QuizLPCategories fields
}
export default function createQuizLPCategory(data: CreateForm): Promise<RecordModel> {
export default function createQuizLPCategory(data: CreateFormProps): Promise<RecordModel> {
return pb.collection(COL_QUIZ_LP_CATEGORIES).create(data);
}