🎉 chore(init): project initialization
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
'use client';
|
||||
|
||||
import { ProList } from '@/components/pro-list';
|
||||
import { queryDocumentDetail, queryDocumentList } from '@/services/user/document';
|
||||
import { Markdown } from '@repo/ui/markdown';
|
||||
import { formatDate } from '@repo/ui/utils';
|
||||
import { Button } from '@shadcn/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@shadcn/ui/card';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Fragment, useState } from 'react';
|
||||
|
||||
export default function Page() {
|
||||
const t = useTranslations('document');
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
const [selected, setSelected] = useState<number>();
|
||||
|
||||
const { data } = useQuery({
|
||||
enabled: !!selected,
|
||||
queryKey: ['queryDocumentDetail', selected],
|
||||
queryFn: async () => {
|
||||
const { data } = await queryDocumentDetail({
|
||||
id: selected!,
|
||||
});
|
||||
return data.data;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{selected ? (
|
||||
<Card>
|
||||
<CardHeader className='pb-2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Button variant='outline' onClick={() => setSelected(undefined)}>
|
||||
<ChevronLeft className='size-4' />
|
||||
{t('back')}
|
||||
</Button>
|
||||
<CardTitle className='font-medium'>{data?.title}</CardTitle>
|
||||
<CardDescription>{formatDate(data?.updated_at)}</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Markdown>{data?.content || ''}</Markdown>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<ProList<API.DocumentItem, { tag: string }>
|
||||
params={[
|
||||
{
|
||||
key: 'tag',
|
||||
placeholder: t('category'),
|
||||
options: tags.map((item) => ({
|
||||
label: item,
|
||||
value: item,
|
||||
})),
|
||||
},
|
||||
]}
|
||||
request={async (_, filter) => {
|
||||
const response = await queryDocumentList();
|
||||
const list = response.data.data?.list || [];
|
||||
setTags(
|
||||
Array.from(new Set(list.reduce((acc: string[], item) => acc.concat(item.tags), []))),
|
||||
);
|
||||
const filterList = list.filter((item) =>
|
||||
filter.tag ? item.tags.includes(filter.tag) : true,
|
||||
);
|
||||
return {
|
||||
list: filterList,
|
||||
total: filterList.length || 0,
|
||||
};
|
||||
}}
|
||||
renderItem={(item) => {
|
||||
return (
|
||||
<Card className='overflow-hidden'>
|
||||
<CardHeader className='bg-muted/50 flex flex-row items-center justify-between gap-2 space-y-0 p-3'>
|
||||
<CardTitle>{item.title}</CardTitle>
|
||||
<CardDescription>
|
||||
<Button
|
||||
size='sm'
|
||||
onClick={() => {
|
||||
setSelected(item.id);
|
||||
}}
|
||||
>
|
||||
{t('read')}
|
||||
</Button>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className='p-3 text-sm'>
|
||||
<ul className='grid gap-3 *:flex *:flex-col lg:grid-cols-2'>
|
||||
<li>
|
||||
<span className='text-muted-foreground'>{t('tags')}</span>
|
||||
<span>{item.tags.join(', ')}</span>
|
||||
</li>
|
||||
<li className='font-semibold'>
|
||||
<span className='text-muted-foreground'>{t('updatedAt')}</span>
|
||||
<time>{formatDate(item.updated_at)}</time>
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user