update left menu path,

This commit is contained in:
louiscklaw
2025-04-24 23:31:33 +08:00
parent 6884f1466f
commit d73e5f9c22
45 changed files with 247 additions and 5474 deletions

View File

@@ -1,104 +0,0 @@
# Connective Revision Guidelines
## Files and component highlight
1. `_GUIDELINES.md` - this document
1. categories
- list (page.tsx), also containing a button to delete record
- read/view ([cat_id]/page.tsx)
- create (create/page.tsx)
- edit/update (edit/[cat_id]/page.tsx)
- optional data for testing(lp-categories-sample-data.tsx)
1. questions
- list (page.tsx), also containing a button to delete record
- read/view ([cat_id]/page.tsx)
- create (create/page.tsx)
- edit/update (edit/[cat_id]/page.tsx)
- optional data for testing(lp-categories-sample-data.tsx)
## Prompt Documents
Each edit page contains `_PROMPT.md` file that provides guidance for editing.
## Sample Data
- `categories/lp-categories-sample-data.tsx`: Categories sample data
- `questions/cr-categories-sample-data.tsx`: Questions sample data
## Assumptions & Requirements
1. Using PocketBase to handle ConnectiveRevision records
2. Each ConnectiveRevision record has:
- `id` (autogenerated)
- `collectionId` (autogenerated)
- `collectionName` (autogenerated)
- `created` (autogenerated)
- `updated` (autogenerated)
- `title` (string)
- `description` (string)
- `category` (string)
- `status` (string)
- `priority` (number)
- `dueDate` (string)
- `assignee` (string)
- `reporter` (string)
- `comments` (array)
- `attachments` (array)
- `tags` (array)
- `related` (array)
- `history` (array)
## Assumption and Requirements
- the `@` sign refer to `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src`
- assume `pb` is located in `@/lib/pb`
- type information defined in `/home/logic/_wsl_workspace/001_github_ws/lettersoup-online-ws/lettersoup-online/project/002_source/cms/src/db/Customers/type.d.tsx`
## Component Development Guidelines
### Requirements
1. **Single Responsibility Principle**:
2. **File Organization**:
- One file per component
- File name should match component name (PascalCase)
- Place components in logical directories based on their purpose
3. **Type Safety**:
- Always use TypeScript types/interfaces
- Import types from `@/db/Customers/type.d.tsx`
4. **PocketBase Integration**:
- Use `pb` instance from `@/lib/pb`
### Component Example
```typescript
'use client';
import { pb } from '@/lib/pb';
import { COL_ExampleModel } from '@/constants';
import type { ExampleType } from '@/db/ExampleModel/type';
// common reference to error display, Provide user-friendly error messages
import ErrorDisplay from '@/components/dashboard/error';
// declare `Props` explicitively
interface Props {
initialData?: ExampleType;
}
export default function ExampleForm({ initialData }: Props) {
let { t } = useTranslate();
// Render form UI
return <>helloworld</>
}
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -135,7 +135,11 @@ export default function Layout({ children, params }: LayoutProps): React.JSX.Ele
const filteredThreads = filterThreads(threads, labelId);
return (
<MailProvider currentLabelId={labelId} labels={labels} threads={filteredThreads}>
<MailProvider
currentLabelId={labelId}
labels={labels}
threads={filteredThreads}
>
<MailView>{children}</MailView>
</MailProvider>
);

View File

@@ -0,0 +1,17 @@
import * as React from 'react';
import type { Metadata } from 'next';
import { config } from '@/config';
import { ThreadView } from '@/components/dashboard/mail/thread-view';
export const metadata = { title: `Thread | Mail | Dashboard | ${config.site.name}` } satisfies Metadata;
interface PageProps {
params: { threadId: string };
}
export default function Page({ params }: PageProps): React.JSX.Element {
const { threadId } = params;
return <ThreadView threadId={threadId} />;
}

View File

@@ -0,0 +1,146 @@
import * as React from 'react';
import { dayjs } from '@/lib/dayjs';
import { MailProvider } from '@/components/dashboard/mail/mail-context';
import { MailView } from '@/components/dashboard/mail/mail-view';
import type { Label, Thread } from '@/components/dashboard/mail/types';
function filterThreads(threads: Thread[], labelId: string): Thread[] {
return threads.filter((thread) => {
if (['inbox', 'sent', 'drafts', 'spam', 'trash'].includes(labelId)) {
return thread.folder === labelId;
}
if (labelId === 'important') {
return thread.isImportant;
}
if (labelId === 'starred') {
return thread.isStarred;
}
if (thread.labels.includes(labelId)) {
return true;
}
return false;
});
}
const labels = [
{ id: 'inbox', type: 'system', name: 'Inbox', unreadCount: 1, totalCount: 0 },
{ id: 'sent', type: 'system', name: 'Sent', unreadCount: 0, totalCount: 0 },
{ id: 'drafts', type: 'system', name: 'Drafts', unreadCount: 0, totalCount: 0 },
{ id: 'spam', type: 'system', name: 'Spam', unreadCount: 0, totalCount: 0 },
{ id: 'trash', type: 'system', name: 'Trash', unreadCount: 0, totalCount: 1 },
{ id: 'important', type: 'system', name: 'Important', unreadCount: 0, totalCount: 1 },
{ id: 'starred', type: 'system', name: 'Starred', unreadCount: 1, totalCount: 1 },
{ id: 'work', type: 'custom', name: 'Work', color: '#43A048', unreadCount: 0, totalCount: 1 },
{ id: 'business', type: 'custom', name: 'Business', color: '#1E88E5', unreadCount: 1, totalCount: 2 },
{ id: 'personal', type: 'custom', name: 'Personal', color: '#FB8A00', unreadCount: 0, totalCount: 1 },
] satisfies Label[];
const threads = [
{
id: 'TRD-004',
from: { avatar: '/assets/avatar-9.png', email: 'marcus.finn@domain.com', name: 'Marcus Finn' },
to: [{ avatar: '/assets/avatar.png', email: 'sofia@devias.io', name: 'Sofia Rivers' }],
subject: 'Website redesign. Interested in collaboration',
message: `Hey there,
I hope this email finds you well. I'm glad you liked my projects, and I would be happy to provide you with a quote for a similar project.
Please let me know your requirements and any specific details you have in mind, so I can give you an accurate quote.
Looking forward to hearing from you soon.
Best regards,
Marcus Finn`,
attachments: [
{
id: 'ATT-001',
name: 'working-sketch.png',
size: '128.5 KB',
type: 'image',
url: '/assets/image-abstract-1.png',
},
{ id: 'ATT-002', name: 'summer-customers.pdf', size: '782.3 KB', type: 'file', url: '#' },
{
id: 'ATT-003',
name: 'desktop-coffee.png',
size: '568.2 KB',
type: 'image',
url: '/assets/image-minimal-1.png',
},
],
folder: 'inbox',
labels: ['work', 'business'],
isImportant: true,
isStarred: false,
isUnread: true,
createdAt: dayjs().subtract(3, 'hour').toDate(),
},
{
id: 'TRD-003',
to: [{ name: 'Sofia Rivers', avatar: '/assets/avatar.png', email: 'sofia@devias.io' }],
from: { name: 'Miron Vitold', avatar: '/assets/avatar-1.png', email: 'miron.vitold@domain.com' },
subject: 'Amazing work',
message: `Hey, nice projects! I really liked the one in react. What's your quote on kinda similar project?`,
folder: 'spam',
labels: [],
isImportant: false,
isStarred: true,
isUnread: false,
createdAt: dayjs().subtract(1, 'day').toDate(),
},
{
id: 'TRD-002',
from: { name: 'Penjani Inyene', avatar: '/assets/avatar-4.png', email: 'penjani.inyene@domain.com' },
to: [{ name: 'Sofia Rivers', avatar: '/assets/avatar.png', email: 'sofia@devias.io' }],
subject: 'Flight reminder',
message: `Dear Sofia,
Your flight is coming up soon. Please don't forget to check in for your scheduled flight.`,
folder: 'inbox',
labels: ['business'],
isImportant: false,
isStarred: false,
isUnread: false,
createdAt: dayjs().subtract(2, 'day').toDate(),
},
{
id: 'TRD-001',
from: { name: 'Carson Darrin', avatar: '/assets/avatar-3.png', email: 'carson.darrin@domain.com' },
to: [{ name: 'Sofia Rivers', avatar: '/assets/avatar.png', email: 'sofia@devias.io' }],
subject: 'Possible candidates for the position',
message: `My market leading client has another fantastic opportunity for an experienced Software Developer to join them on a heavily remote basis`,
folder: 'trash',
labels: ['personal'],
isImportant: false,
isStarred: false,
isUnread: true,
createdAt: dayjs().subtract(2, 'day').toDate(),
},
] satisfies Thread[];
interface LayoutProps {
children: React.ReactNode;
params: { labelId: string };
}
export default function Layout({ children, params }: LayoutProps): React.JSX.Element {
const { labelId } = params;
const filteredThreads = filterThreads(threads, labelId);
return (
<MailProvider
currentLabelId={labelId}
labels={labels}
threads={filteredThreads}
>
<MailView>{children}</MailView>
</MailProvider>
);
}

View File

@@ -0,0 +1,11 @@
import * as React from 'react';
import type { Metadata } from 'next';
import { config } from '@/config';
import { ThreadsView } from '@/components/dashboard/mail/threads-view';
export const metadata = { title: `Mail | Dashboard | ${config.site.name}` } satisfies Metadata;
export default function Page(): React.JSX.Element {
return <ThreadsView />;
}

View File

@@ -0,0 +1,11 @@
import * as React from 'react';
import type { Metadata } from 'next';
import { config } from '@/config';
import { ThreadsView } from '@/components/dashboard/mail/threads-view';
export const metadata = { title: `Mail | Dashboard | ${config.site.name}` } satisfies Metadata;
export default function Page(): React.JSX.Element {
return <ThreadsView />;
}