Skip to content

Commit e877018

Browse files
updated dev containers and copilot-instructions
1 parent 82d368a commit e877018

38 files changed

+7437
-2
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
88
},
99
"postCreateCommand": "npm run setup",
10+
"postAttachCommand": "npm run dev",
1011
"forwardPorts": [
1112
3000,
1213
5432

.github/copilot-instructions.md

Lines changed: 360 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,362 @@
1-
See `package.json` for the available project scripts and dependencies.
1+
# Taskflow - GitHub Copilot Instructions
22

3-
Anytime you want to run a project script (e.g., linting, testing, building), use the corrisponding task defined in `.vscode/tasks.json`. Never use `npm run <script>` directly, unless there is no corresponding task defined.
3+
## Overview
44

5+
This document enables AI coding assistants to generate features aligned with the Taskflow project's architecture and style conventions. All patterns are **observed from the actual codebase**.
6+
7+
**Tech Stack**: Next.js 15 App Router, React 19, TypeScript, Prisma + PostgreSQL, Tailwind CSS 4
8+
9+
**Domain**: Team task management application with Kanban boards and collaboration features.
10+
11+
**Project Scripts**: Use tasks in `.vscode/tasks.json` to run scripts (not `npm run` directly).
12+
13+
---
14+
15+
## File Categories
16+
17+
### Next.js Pages (`app/`)
18+
- Route groups `(name)` for shared layouts without URL segments
19+
- Pages are async server components that fetch data directly
20+
- Auth checks in layout, fonts via `className` from `lib/fonts.ts`
21+
22+
### Server Actions (`app/*/actions.ts`)
23+
- `"use server"` directive at file level, PrismaClient per file
24+
- Receive `FormData`, return `{ error, success, message? }` (never throw)
25+
- Call `getCurrentUser()` first, use `revalidatePath()` after mutations
26+
- Prisma import: `@/app/generated/prisma/client`
27+
28+
### React Components - UI (`components/ui/`)
29+
- Build on Radix UI primitives with CVA for variants
30+
- Use `React.forwardRef` with `displayName`, support `asChild` via Radix Slot
31+
- Use `cn()` for all className composition
32+
33+
### React Components - Feature (`components/`)
34+
- Mark interactive components with `"use client"`
35+
- Receive server-fetched data via props (never fetch in component)
36+
- Poppins font for headings, Lucide React for icons
37+
- Charts client-only with `ResponsiveContainer`, DnD uses `@hello-pangea/dnd`
38+
39+
### Utility Functions (`lib/`)
40+
- `cn()` combines clsx + tailwind-merge
41+
- Date parsing returns `undefined` for invalid dates
42+
- Custom hooks for contexts with error checking
43+
44+
### Types (`lib/types.ts`)
45+
- Import Prisma types from `@/app/generated/prisma/client`
46+
- Extend Prisma types with relations using `&`
47+
48+
### Database (`prisma/`)
49+
- Generate client to `app/generated/prisma`
50+
- Binary targets: ARM64 Linux (Docker) + ARM64 macOS
51+
- Named relations for self-referencing (`"AssignedTasks"`, `"CreatedTasks"`)
52+
- Hash passwords with bcryptjs in seed data
53+
54+
### Tests
55+
- **Unit** (`tests/unit/*.test.tsx`): Jest + Testing Library, mock server actions with `jest.mock`
56+
- **E2E** (`tests/e2e/*.spec.ts`): Playwright with global setup/teardown for server
57+
58+
---
59+
60+
## Feature Scaffold Guide
61+
62+
### Adding a New Feature Component
63+
64+
**Example**: Create a "Priority Badge" component
65+
66+
1. **Determine file types needed**:
67+
- UI primitive: `components/ui/priority-badge.tsx`
68+
- Feature usage: Import in task components
69+
- Types (if needed): Add to `lib/types.ts`
70+
71+
2. **Create UI primitive**:
72+
```tsx
73+
// components/ui/priority-badge.tsx
74+
import * as React from "react"
75+
import { cva, type VariantProps } from "class-variance-authority"
76+
import { cn } from "@/lib/utils"
77+
78+
const badgeVariants = cva(
79+
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold",
80+
{
81+
variants: {
82+
priority: {
83+
low: "bg-green-100 text-green-800",
84+
medium: "bg-yellow-100 text-yellow-800",
85+
high: "bg-red-100 text-red-800",
86+
},
87+
},
88+
defaultVariants: {
89+
priority: "medium",
90+
},
91+
}
92+
)
93+
94+
export interface PriorityBadgeProps
95+
extends React.HTMLAttributes<HTMLSpanElement>,
96+
VariantProps<typeof badgeVariants> {}
97+
98+
const PriorityBadge = React.forwardRef<HTMLSpanElement, PriorityBadgeProps>(
99+
({ className, priority, ...props }, ref) => {
100+
return (
101+
<span
102+
ref={ref}
103+
className={cn(badgeVariants({ priority, className }))}
104+
{...props}
105+
/>
106+
)
107+
}
108+
)
109+
PriorityBadge.displayName = "PriorityBadge"
110+
111+
export { PriorityBadge, badgeVariants }
112+
```
113+
114+
3. **Use in feature component**:
115+
```tsx
116+
// components/task-list.tsx
117+
import { PriorityBadge } from "@/components/ui/priority-badge"
118+
119+
<PriorityBadge priority={task.priority as "low" | "medium" | "high"}>
120+
{task.priority}
121+
</PriorityBadge>
122+
```
123+
124+
### Adding a New Page with Data Fetching
125+
126+
**Example**: Create a "Reports" page
127+
128+
1. **Create route structure**:
129+
```
130+
app/(dashboard)/reports/
131+
├── page.tsx # Server component
132+
├── actions.ts # Server actions
133+
└── _components/ # Private components
134+
└── reports-client.tsx
135+
```
136+
137+
2. **Server page fetches data**:
138+
```tsx
139+
// app/(dashboard)/reports/page.tsx
140+
import { PrismaClient } from "@/app/generated/prisma/client"
141+
import { getCurrentUser } from "@/app/login/actions"
142+
import { redirect } from "next/navigation"
143+
import { ReportsClient } from "./_components/reports-client"
144+
145+
const prisma = new PrismaClient()
146+
147+
export default async function ReportsPage() {
148+
const user = await getCurrentUser()
149+
if (!user) redirect("/login")
150+
151+
const reports = await prisma.task.groupBy({
152+
by: ["status"],
153+
_count: { id: true },
154+
})
155+
156+
return <ReportsClient reports={reports} />
157+
}
158+
```
159+
160+
3. **Client component for interactivity**:
161+
```tsx
162+
// app/(dashboard)/reports/_components/reports-client.tsx
163+
"use client"
164+
165+
import { useState } from "react"
166+
import { BarChart, Bar, ResponsiveContainer } from "recharts"
167+
168+
export function ReportsClient({ reports }) {
169+
const [filter, setFilter] = useState("all")
170+
// ... interactive logic
171+
}
172+
```
173+
174+
4. **Server actions for mutations**:
175+
```tsx
176+
// app/(dashboard)/reports/actions.ts
177+
"use server"
178+
179+
import { PrismaClient } from "@/app/generated/prisma/client"
180+
import { getCurrentUser } from "@/app/login/actions"
181+
import { revalidatePath } from "next/cache"
182+
183+
const prisma = new PrismaClient()
184+
185+
export async function generateReport(formData: FormData) {
186+
const user = await getCurrentUser()
187+
if (!user) return { error: "Unauthorized" }
188+
189+
const type = formData.get("type") as string
190+
191+
// ... report generation logic
192+
193+
revalidatePath("/reports")
194+
return { success: "Report generated successfully" }
195+
}
196+
```
197+
198+
### Adding Database Model and CRUD
199+
200+
**Example**: Add "Project" model
201+
202+
1. **Update Prisma schema**:
203+
```prisma
204+
// prisma/schema.prisma
205+
model Project {
206+
id Int @id @default(autoincrement())
207+
name String
208+
description String?
209+
createdAt DateTime @default(now())
210+
ownerId Int
211+
owner User @relation(fields: [ownerId], references: [id])
212+
tasks Task[]
213+
}
214+
215+
// Update Task model
216+
model Task {
217+
// ... existing fields
218+
projectId Int?
219+
project Project? @relation(fields: [projectId], references: [id])
220+
}
221+
```
222+
223+
2. **Run migration**:
224+
```bash
225+
npm run db:setup
226+
```
227+
228+
3. **Create type definitions**:
229+
```tsx
230+
// lib/types.ts
231+
import { Project, User, Task } from "@/app/generated/prisma/client"
232+
233+
export type ProjectWithRelations = Project & {
234+
owner: User
235+
tasks: Task[]
236+
}
237+
```
238+
239+
4. **Create server actions**:
240+
```tsx
241+
// app/(dashboard)/projects/actions.ts
242+
"use server"
243+
244+
import { PrismaClient } from "@/app/generated/prisma/client"
245+
import { getCurrentUser } from "@/app/login/actions"
246+
import { revalidatePath } from "next/cache"
247+
248+
const prisma = new PrismaClient()
249+
250+
export async function createProject(formData: FormData) {
251+
const user = await getCurrentUser()
252+
if (!user) return { error: "Unauthorized" }
253+
254+
const name = formData.get("name") as string
255+
const description = formData.get("description") as string
256+
257+
await prisma.project.create({
258+
data: {
259+
name,
260+
description,
261+
ownerId: user.id,
262+
},
263+
})
264+
265+
revalidatePath("/projects")
266+
return { success: "Project created" }
267+
}
268+
269+
export async function deleteProject(projectId: number) {
270+
const user = await getCurrentUser()
271+
if (!user) return { error: "Unauthorized" }
272+
273+
const project = await prisma.project.findUnique({
274+
where: { id: projectId },
275+
})
276+
277+
if (project?.ownerId !== user.id) {
278+
return { error: "Not authorized to delete this project" }
279+
}
280+
281+
await prisma.project.delete({
282+
where: { id: projectId },
283+
})
284+
285+
revalidatePath("/projects")
286+
return { success: "Project deleted" }
287+
}
288+
```
289+
290+
---
291+
292+
## Critical Rules
293+
294+
### UI Components
295+
- All UI primitives use Radix UI + CVA + forwardRef with displayName
296+
- Use `cn()` for className composition, Lucide React for icons
297+
- Tailwind CSS 4 only (`@import "tailwindcss"`, `@theme` for tokens)
298+
299+
### Routing & Data
300+
- Routes in `app/`, route groups `(name)` for layouts
301+
- Server components default, `"use client"` for interactivity
302+
- No `/api` routes - use server actions in `actions.ts`
303+
- Import Prisma from `@/app/generated/prisma/client`
304+
- Server actions: return `{ error, success, message? }`, call `revalidatePath()` after mutations
305+
306+
### State & Auth
307+
- Server actions for mutations, props for data to client components
308+
- No Redux/Zustand - use useState/useTransition
309+
- `getCurrentUser()` required for protected actions
310+
- Session-based auth (database sessions, not JWT)
311+
312+
### Forms & Testing
313+
- FormData in server actions, controlled inputs with useState
314+
- Jest + Testing Library for unit tests (`.test.tsx`)
315+
- Playwright for E2E tests (`.spec.ts`), global setup/teardown for server---
316+
317+
## Quick Examples
318+
319+
### Status Filter Dropdown
320+
```tsx
321+
// components/ui/status-select.tsx - UI primitive with CVA
322+
// components/task-status-filter.tsx - "use client" with useState
323+
```
324+
325+
### Task Comments Feature
326+
1. Add Comment model to `prisma/schema.prisma`, run `npm run db:setup`
327+
2. Add CRUD actions to `app/(dashboard)/tasks/actions.ts`
328+
3. Create `components/ui/comment-card.tsx` + `components/comment-form.tsx`
329+
4. Add types to `lib/types.ts`, add tests
330+
331+
### Due Date Notifications
332+
```tsx
333+
// lib/date-utils.ts
334+
export function isDueSoon(dueDate: Date): boolean {
335+
const diff = dueDate.getTime() - new Date().getTime()
336+
const hours = diff / (1000 * 60 * 60)
337+
return hours > 0 && hours <= 24
338+
}
339+
340+
// components/ui/notification-badge.tsx - CVA badge primitive
341+
// components/task-due-indicator.tsx - "use client" wrapper
342+
```---
343+
344+
## Key Patterns
345+
346+
1. Prisma from `@/app/generated/prisma/client`, not `@prisma/client`
347+
2. Route group `(dashboard)` for authenticated routes
348+
3. `inter.className` for body, `poppins.className` for headings
349+
4. Return error objects in server actions, never throw
350+
5. `useTransition` for optimistic updates
351+
6. `getCurrentUser()` + `revalidatePath()` in protected server actions
352+
7. Mark interactive components with `"use client"`
353+
354+
## Common Mistakes
355+
356+
❌ CSS modules or styled-components → ✅ Tailwind only
357+
❌ Fetch in client components → ✅ Server fetches, pass via props
358+
❌ Throw in server actions → ✅ Return `{ error, success }`
359+
❌ Skip `revalidatePath` → ✅ Always call after mutations
360+
❌ Manual className concat → ✅ Use `cn()` utility
361+
❌ Missing forwardRef/displayName → ✅ Required for UI components
362+
❌ API routes `/api` → ✅ Server actions only

0 commit comments

Comments
 (0)