Skip to content

Commit 82f079c

Browse files
author
赵汝波
committed
feat: Refactor task query parsing and enhance project detail UI
- Introduced a new `parse-tasks-url` module to centralize task query parsing logic for both `/tasks` and `/projects/[id]` pages. - Updated `ProjectDetailPage` and `TasksPage` to utilize the new parsing functions, improving code maintainability. - Enhanced UI in `ProjectDetailPage` to include a link to all tasks and improved layout for better user experience. - Adjusted `TasksView` to support project-specific task filtering and improved navigation handling.
1 parent 1f69d1e commit 82f079c

4 files changed

Lines changed: 186 additions & 118 deletions

File tree

‎src/app/projects/[id]/page.tsx‎

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,33 @@ import { auth } from '@/auth'
22
import { CreateTaskSheet } from '@/components/create-task-sheet'
33
import { TasksView } from '@/components/tasks-view'
44
import { can } from '@/lib/acl'
5+
import {
6+
parseTasksTaskQuery,
7+
parseViewMode,
8+
pickFirst,
9+
} from '@/lib/parse-tasks-url'
510
import { prisma } from '@/lib/db'
11+
import { getTasksForUser } from '@/lib/tasks-data'
612
import type { TaskListItem } from '@/types/task'
13+
import Link from 'next/link'
714
import { notFound, redirect } from 'next/navigation'
815

916
interface ProjectDetailPageProps {
1017
params: Promise<{ id: string }>
18+
searchParams: Promise<Record<string, string | string[] | undefined>>
1119
}
1220

1321
export default async function ProjectDetailPage({
1422
params,
23+
searchParams,
1524
}: ProjectDetailPageProps) {
1625
const session = await auth()
1726
if (!session?.user?.id) {
1827
redirect('/login')
1928
}
2029

2130
const { id } = await params
31+
const sp = await searchParams
2232

2333
const project = await prisma.project.findUnique({
2434
where: { id },
@@ -37,10 +47,14 @@ export default async function ProjectDetailPage({
3747
notFound()
3848
}
3949

40-
const rows = await prisma.task.findMany({
41-
where: { projectId: id, userId: session.user.id },
42-
orderBy: { updatedAt: 'desc' },
43-
include: { project: { select: { id: true, name: true } } },
50+
const query = parseTasksTaskQuery(sp)
51+
const view = parseViewMode(sp)
52+
const rawTaskId = pickFirst(sp.taskId)?.trim()
53+
const openTaskId = rawTaskId && rawTaskId.length > 0 ? rawTaskId : undefined
54+
55+
const rows = await getTasksForUser(session.user.id, {
56+
...query,
57+
projectId: id,
4458
})
4559

4660
const tasks: TaskListItem[] = rows.map(t => ({
@@ -56,8 +70,8 @@ export default async function ProjectDetailPage({
5670
}))
5771

5872
return (
59-
<main className='mx-auto flex w-full max-w-4xl flex-1 flex-col gap-6 px-4 py-12'>
60-
<section className='flex items-start justify-between gap-4'>
73+
<main className='mx-auto flex w-full max-w-7xl flex-1 flex-col gap-6 px-4 py-10 sm:px-6'>
74+
<section className='flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between'>
6175
<div>
6276
<p className='font-mono text-xs uppercase tracking-wider text-zinc-400'>
6377
Project
@@ -66,7 +80,15 @@ export default async function ProjectDetailPage({
6680
{project.name}
6781
</h1>
6882
<p className='mt-2 text-sm text-zinc-500'>
69-
仅项目所有者可访问;任务列表同样按用户隔离。
83+
与「我的任务」相同的筛选与列表/看板视图;条件写入 URL,仅本项目数据。
84+
</p>
85+
<p className='mt-2 text-xs text-zinc-400'>
86+
<Link
87+
href='/tasks'
88+
className='font-medium text-violet-600 hover:text-violet-700'
89+
>
90+
← 全部任务
91+
</Link>
7092
</p>
7193
</div>
7294
<CreateTaskSheet
@@ -78,15 +100,16 @@ export default async function ProjectDetailPage({
78100
<TasksView
79101
tasks={tasks}
80102
projects={[{ id: project.id, name: project.name }]}
103+
openTaskId={openTaskId}
81104
currentQuery={{
82-
keyword: '',
83-
status: 'all',
84-
projectId: project.id,
85-
sort: 'updated_desc',
86-
dateFrom: '',
87-
dateTo: '',
88-
priority: 'all',
89-
view: 'list',
105+
keyword: query.keyword ?? '',
106+
status: (query.status as string) ?? 'all',
107+
projectId: id,
108+
sort: query.sort ?? 'updated_desc',
109+
dateFrom: query.dateFrom ?? '',
110+
dateTo: query.dateTo ?? '',
111+
priority: (query.priority as string) ?? 'all',
112+
view,
90113
}}
91114
/>
92115
</main>

‎src/app/tasks/page.tsx‎

Lines changed: 9 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,17 @@
11
import { auth } from '@/auth'
22
import { CreateTaskSheet } from '@/components/create-task-sheet'
33
import { TasksView } from '@/components/tasks-view'
4-
import { normalizeDateRange, parseYmdParam } from '@/lib/date-query'
5-
import { prisma } from '@/lib/db'
64
import {
7-
getTasksForUser,
8-
type TaskQuery,
9-
type TaskSort,
10-
} from '@/lib/tasks-data'
11-
import type { TaskListItem, TaskPriority, TaskStatus } from '@/types/task'
5+
parseTasksTaskQuery,
6+
parseViewMode,
7+
pickFirst,
8+
} from '@/lib/parse-tasks-url'
9+
import { prisma } from '@/lib/db'
10+
import { getTasksForUser } from '@/lib/tasks-data'
11+
import type { TaskListItem } from '@/types/task'
1212
import { redirect } from 'next/navigation'
1313
import { Suspense } from 'react'
1414

15-
const ALLOWED_STATUS: TaskStatus[] = ['todo', 'doing', 'done']
16-
const ALLOWED_SORT: TaskSort[] = [
17-
'updated_desc',
18-
'updated_asc',
19-
'created_desc',
20-
'created_asc',
21-
'due_asc',
22-
'due_desc',
23-
]
24-
25-
const ALLOWED_PRIORITY: (TaskPriority | 'all')[] = [
26-
'all',
27-
'none',
28-
'low',
29-
'medium',
30-
'high',
31-
'urgent',
32-
]
33-
34-
function pickFirst(value: string | string[] | undefined): string | undefined {
35-
if (Array.isArray(value)) return value[0]
36-
return value
37-
}
38-
39-
function parseTaskQuery(
40-
sp: Record<string, string | string[] | undefined>
41-
): TaskQuery {
42-
const keyword = pickFirst(sp.keyword)?.trim() || undefined
43-
44-
const rawStatus = pickFirst(sp.status)
45-
const status =
46-
rawStatus === 'all' ||
47-
(rawStatus && ALLOWED_STATUS.includes(rawStatus as TaskStatus))
48-
? (rawStatus as TaskQuery['status'])
49-
: undefined
50-
51-
const rawProject = pickFirst(sp.projectId)
52-
const projectId = rawProject ? rawProject : undefined
53-
54-
const rawSort = pickFirst(sp.sort)
55-
const sort =
56-
rawSort && ALLOWED_SORT.includes(rawSort as TaskSort)
57-
? (rawSort as TaskSort)
58-
: undefined
59-
60-
const rawFrom = parseYmdParam(pickFirst(sp.dateFrom))
61-
const rawTo = parseYmdParam(pickFirst(sp.dateTo))
62-
const { dateFrom, dateTo } = normalizeDateRange(rawFrom, rawTo)
63-
64-
const rawPriority = pickFirst(sp.priority)
65-
const priority =
66-
rawPriority &&
67-
ALLOWED_PRIORITY.includes(rawPriority as TaskPriority | 'all')
68-
? (rawPriority as TaskQuery['priority'])
69-
: undefined
70-
71-
return { keyword, status, projectId, sort, dateFrom, dateTo, priority }
72-
}
73-
7415
export default async function TasksPage({
7516
searchParams,
7617
}: {
@@ -82,12 +23,11 @@ export default async function TasksPage({
8223
}
8324

8425
const sp = await searchParams
85-
const query = parseTaskQuery(sp)
26+
const query = parseTasksTaskQuery(sp)
8627
const rawTaskId = pickFirst(sp.taskId)?.trim()
8728
const openTaskId = rawTaskId && rawTaskId.length > 0 ? rawTaskId : undefined
8829

89-
const rawView = pickFirst(sp.view)
90-
const view = rawView === 'board' ? ('board' as const) : ('list' as const)
30+
const view = parseViewMode(sp)
9131

9232
const [raw, projects] = await Promise.all([
9333
getTasksForUser(session.user.id, query),

0 commit comments

Comments
 (0)