Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/(dashboard)/my-work/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export default async function MyWorkPage() {
])

const myTasks = allMyTasks
.filter((t) => t.status !== 'done')
// Only active plans are live work — draft plans haven't started, and
// completed/cancelled plans shouldn't nag with stale open tasks.
.filter((t) => t.status !== 'done' && t.planStatus === 'active')
.sort((a, b) => (a.endDate ?? '9999') < (b.endDate ?? '9999') ? -1 : 1)
const myPlans = plans.filter((p) => p.ownerId === user.id && p.status !== 'completed' && p.status !== 'cancelled')
const myItems = items.filter(
Expand Down
4 changes: 2 additions & 2 deletions docs/app-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ Provenance columns (`source` default `native`, `connectionId`, `externalId/Key/U
| `getProduct(slug, userId)` | `Product & { assets }` \| `null` | Org-aware; assets ordered by createdAt desc; `dependencies` always `[]` |
| `getCodePlans(userId, filters?)` | `CodePlan[]` | Org-aware; filters: `productId`, `status`, `type`; includes `taskCount`, `completedTaskCount`, `progress`, `productName` |
| `getCodePlan(id, userId)` | `CodePlanDetail` \| `null` | Org-scope guarded; includes full `tasks[]`, `assignees[]`, `targetAssets[]`, `planAssets[]` (per-asset branch/PR); `progress` = % done |
| `getTasks(userId, filters?)` | `TaskWithContext[]` | Org-aware; filters: `planId`, `assigneeId`, `status`; includes `planTitle`, `assetName`, `assigneeName` |
| `getTasks(userId, filters?)` | `TaskWithContext[]` | Org-aware; filters: `planId`, `assigneeId`, `status`; includes `planTitle`, `planStatus`, `assetName`, `assigneeName` |
| `getWorkItems(userId, filters?)` | `WorkItemWithContext[]` | Org-aware; filters: `productId`, `assetId`, `type`, `status`, `planId`; includes product/asset names + `linkedPlans[]` |
| `getWorkItem(id, userId)` | `WorkItemWithContext` \| `null` | Org-scope guarded |
| `getAssetOptions(userId)` | `{id,name,productId}[]` | Flat asset list across accessible products (dropdowns) |
Expand Down Expand Up @@ -266,7 +266,7 @@ All routes share `AppShell`: 64px top header + 256px sidebar. Sidebar contains:
---

#### `/my-work` — My Work
Personal execution view (the Tasks page remains the comprehensive review surface): open tasks assigned to me sorted by end date (overdue in red), plans I own with progress bars, and open work items I own — all respecting the product scope switcher.
Personal execution view (the Tasks page remains the comprehensive review surface): open tasks assigned to me sorted by end date (overdue in red), plans I own with progress bars, and open work items I own — all respecting the product scope switcher. "My Open Tasks" additionally filters to tasks on **active** plans only (via `TaskWithContext.planStatus`) — a task on a draft plan hasn't started, and one on a completed/cancelled plan is a stale artifact, so neither belongs in a personal to-do list.

#### `/products` — Products List
- Grid of product cards with name, description (truncated), tags (max 3 shown), asset count, active plan count
Expand Down
3 changes: 3 additions & 0 deletions lib/db/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ type TaskFilters = {

export type TaskWithContext = Task & {
planTitle: string
planStatus: CodePlanStatus
assetName: string | null
assigneeName: string | null
}
Expand Down Expand Up @@ -571,6 +572,7 @@ export async function getTasks(userId: string, filters: TaskFilters = {}): Promi
createdAt: tasks.createdAt,
updatedAt: tasks.updatedAt,
planTitle: codePlans.title,
planStatus: codePlans.status,
assetName: assets.name,
assigneeName: users.name,
})
Expand Down Expand Up @@ -602,6 +604,7 @@ export async function getTasks(userId: string, filters: TaskFilters = {}): Promi
createdAt: r.createdAt.toISOString(),
updatedAt: r.updatedAt.toISOString(),
planTitle: r.planTitle,
planStatus: r.planStatus,
assetName: r.assetName,
assigneeName: r.assigneeName,
}))
Expand Down
10 changes: 9 additions & 1 deletion tests/lib/db/queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,21 @@ describe('getTasks', () => {
expect(bobTasks[0].id).toBe(F.task1)
})

it('includes planTitle, assetName, and assigneeName', async () => {
it('includes planTitle, planStatus, assetName, and assigneeName', async () => {
const taskList = await getTasks(F.alice)
const t1 = taskList.find((t) => t.id === F.task1)!
expect(t1.planTitle).toBe('Active Plan')
expect(t1.planStatus).toBe('active')
expect(t1.assigneeName).toBe('Bob')
expect(t1.assetName).toBeNull() // task1 has no assetId
})

it('reports planStatus: completed for a task on the completed plan', async () => {
// task4 sits on planCompleted in the shared fixtures — the other status
// besides 'active' already present, so this needs no extra seed data.
const completedTasks = await getTasks(F.alice, { planId: F.planCompleted })
expect(completedTasks[0].planStatus).toBe('completed')
})
})

// ---------------------------------------------------------------------------
Expand Down
Loading