chore: init
This commit is contained in:
BIN
.cursor/.DS_Store
vendored
Normal file
BIN
.cursor/.DS_Store
vendored
Normal file
Binary file not shown.
32
.cursor/rules/api-routes.mdc
Normal file
32
.cursor/rules/api-routes.mdc
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
globs: app/api/**/*.ts
|
||||
---
|
||||
|
||||
# API Routes Rules
|
||||
|
||||
1. Routes MUST only use services for data access
|
||||
2. Routes MUST handle input validation
|
||||
3. Routes MUST return typed responses
|
||||
4. Routes MUST use proper error handling
|
||||
|
||||
Example of correct API route:
|
||||
|
||||
```typescript
|
||||
import { MyService } from "@/services/my-service";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const service = new MyService(pool);
|
||||
const data = await service.getData();
|
||||
return Response.json(data);
|
||||
} catch (error) {
|
||||
return Response.error();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
❌ FORBIDDEN:
|
||||
|
||||
- Direct database queries
|
||||
- Business logic implementation
|
||||
- Untyped responses
|
||||
167
.cursor/rules/business-logic-separation.mdc
Normal file
167
.cursor/rules/business-logic-separation.mdc
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
description: Enforce business logic separation between frontend and backend
|
||||
---
|
||||
|
||||
# Business Logic Separation Rules
|
||||
|
||||
## Core Principle: NO Business Logic in Frontend
|
||||
|
||||
All business logic, data processing, and domain rules MUST be implemented in the backend services layer. The frontend is purely for presentation and user interaction.
|
||||
|
||||
## ✅ ALLOWED in Frontend ([src/components/](mdc:src/components/), [src/hooks/](mdc:src/hooks/), [src/clients/](mdc:src/clients/))
|
||||
|
||||
### Components
|
||||
- UI rendering and presentation logic
|
||||
- Form validation (UI-level only, not business rules)
|
||||
- User interaction handling (clicks, inputs, navigation)
|
||||
- Visual state management (loading, errors, UI states)
|
||||
- Data formatting for display purposes only
|
||||
|
||||
### Hooks
|
||||
- React state management
|
||||
- API call orchestration (using clients)
|
||||
- UI-specific logic (modals, forms, animations)
|
||||
- Data fetching and caching coordination
|
||||
|
||||
### Clients
|
||||
- HTTP requests to API routes
|
||||
- Request/response transformation (serialization only)
|
||||
- Error handling and retry logic
|
||||
- Authentication token management
|
||||
|
||||
## ❌ FORBIDDEN in Frontend
|
||||
|
||||
### Business Rules
|
||||
```typescript
|
||||
// ❌ BAD: Business logic in component
|
||||
const TaskCard = ({ task }) => {
|
||||
const canEdit = task.status === 'open' && task.assignee === currentUser.id;
|
||||
const priority = task.dueDate < new Date() ? 'high' : 'normal';
|
||||
// This is business logic!
|
||||
}
|
||||
|
||||
// ✅ GOOD: Get computed values from backend
|
||||
const TaskCard = ({ task }) => {
|
||||
const { canEdit, priority } = task; // Computed by backend service
|
||||
}
|
||||
```
|
||||
|
||||
### Data Processing
|
||||
```typescript
|
||||
// ❌ BAD: Data transformation in frontend
|
||||
const processJiraTasks = (tasks) => {
|
||||
return tasks.map(task => ({
|
||||
...task,
|
||||
normalizedStatus: mapJiraStatus(task.status),
|
||||
estimatedHours: calculateEstimate(task.storyPoints)
|
||||
}));
|
||||
}
|
||||
|
||||
// ✅ GOOD: Data already processed by backend service
|
||||
const { processedTasks } = await tasksClient.getTasks();
|
||||
```
|
||||
|
||||
### Domain Logic
|
||||
```typescript
|
||||
// ❌ BAD: Domain calculations in frontend
|
||||
const calculateTeamVelocity = (sprints) => {
|
||||
// Complex business calculation
|
||||
}
|
||||
|
||||
// ✅ GOOD: Domain logic in service
|
||||
// This belongs in services/team-analytics.ts
|
||||
```
|
||||
|
||||
## ✅ REQUIRED in Backend ([src/services/](mdc:src/services/), [src/app/api/](mdc:src/app/api/))
|
||||
|
||||
### Services Layer
|
||||
- All business rules and domain logic
|
||||
- Data validation and processing
|
||||
- Integration with external APIs (Jira, macOS Reminders)
|
||||
- Complex calculations and algorithms
|
||||
- Data aggregation and analytics
|
||||
- Permission and authorization logic
|
||||
|
||||
### API Routes
|
||||
- Input validation and sanitization
|
||||
- Service orchestration
|
||||
- Response formatting
|
||||
- Error handling and logging
|
||||
- Authentication and authorization
|
||||
|
||||
## Implementation Pattern
|
||||
|
||||
### ✅ Correct Flow
|
||||
```
|
||||
User Action → Component → Client → API Route → Service → Database
|
||||
↑ ↓
|
||||
Pure UI Logic Business Logic
|
||||
```
|
||||
|
||||
### ❌ Incorrect Flow
|
||||
```
|
||||
User Action → Component with Business Logic → Database
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Task Status Management
|
||||
```typescript
|
||||
// ❌ BAD: In component
|
||||
const updateTaskStatus = (taskId, newStatus) => {
|
||||
if (newStatus === 'done' && !task.hasAllSubtasks) {
|
||||
throw new Error('Cannot complete task with pending subtasks');
|
||||
}
|
||||
// Business rule in frontend!
|
||||
}
|
||||
|
||||
// ✅ GOOD: In services/task-processor.ts
|
||||
export const updateTaskStatus = async (taskId: string, newStatus: TaskStatus) => {
|
||||
const task = await getTask(taskId);
|
||||
|
||||
// Business rules in service
|
||||
if (newStatus === 'done' && !await hasAllSubtasksCompleted(taskId)) {
|
||||
throw new BusinessError('Cannot complete task with pending subtasks');
|
||||
}
|
||||
|
||||
return await updateTask(taskId, { status: newStatus });
|
||||
}
|
||||
```
|
||||
|
||||
### Team Analytics
|
||||
```typescript
|
||||
// ❌ BAD: In component
|
||||
const TeamDashboard = () => {
|
||||
const calculateBurndown = (tasks) => {
|
||||
// Complex business calculation in component
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ GOOD: In services/team-analytics.ts
|
||||
export const getTeamBurndown = async (teamId: string, sprintId: string) => {
|
||||
// All calculation logic in service
|
||||
const tasks = await getSprintTasks(sprintId);
|
||||
return calculateBurndownMetrics(tasks);
|
||||
}
|
||||
```
|
||||
|
||||
## Enforcement
|
||||
|
||||
When reviewing code:
|
||||
1. **Components**: Should only contain JSX, event handlers, and UI state
|
||||
2. **Hooks**: Should only orchestrate API calls and manage React state
|
||||
3. **Clients**: Should only make HTTP requests and handle responses
|
||||
4. **Services**: Should contain ALL business logic and data processing
|
||||
5. **API Routes**: Should validate input and call appropriate services
|
||||
|
||||
## Red Flags
|
||||
|
||||
Watch for these patterns that indicate business logic in frontend:
|
||||
- Complex calculations in components/hooks
|
||||
- Business rule validation in forms
|
||||
- Data transformation beyond display formatting
|
||||
- Domain-specific constants and rules
|
||||
- Integration logic with external systems
|
||||
|
||||
Remember: **The frontend is a thin presentation layer. All intelligence lives in the backend.**
|
||||
31
.cursor/rules/clients.mdc
Normal file
31
.cursor/rules/clients.mdc
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
globs: clients/**/*.ts
|
||||
---
|
||||
|
||||
# HTTP Clients Rules
|
||||
|
||||
1. All HTTP calls MUST be in clients/domains/
|
||||
2. Each domain MUST have its own client
|
||||
3. Clients MUST use the base HTTP client
|
||||
4. Clients MUST type their responses
|
||||
|
||||
Example of correct client:
|
||||
|
||||
```typescript
|
||||
import { HttpClient } from "@/clients/base/http-client";
|
||||
import { MyData } from "@/lib/types";
|
||||
|
||||
export class MyClient {
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
async getData(): Promise<MyData[]> {
|
||||
return this.httpClient.get("/api/data");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
❌ FORBIDDEN:
|
||||
|
||||
- Direct fetch calls
|
||||
- Business logic in clients
|
||||
- Untyped responses
|
||||
28
.cursor/rules/components.mdc
Normal file
28
.cursor/rules/components.mdc
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
globs: src/components/**/*.tsx
|
||||
---
|
||||
|
||||
# Components Rules
|
||||
|
||||
1. UI components MUST be in src/components/ui/
|
||||
2. Feature components MUST be in their feature folder
|
||||
3. Components MUST use clients for data fetching
|
||||
4. Components MUST be properly typed
|
||||
|
||||
Example of correct component:
|
||||
|
||||
```typescript
|
||||
import { useMyClient } from '@/hooks/use-my-client';
|
||||
|
||||
export const MyComponent = () => {
|
||||
const { data } = useMyClient();
|
||||
return <div>{data.map(item => <Item key={item.id} {...item} />)}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
❌ FORBIDDEN:
|
||||
|
||||
- Direct service usage
|
||||
- Direct database queries
|
||||
- Direct fetch calls
|
||||
- Untyped props
|
||||
167
.cursor/rules/css-variables-theme.mdc
Normal file
167
.cursor/rules/css-variables-theme.mdc
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
description: CSS Variables theme system best practices
|
||||
---
|
||||
|
||||
# CSS Variables Theme System
|
||||
|
||||
## Core Principle: Pure CSS Variables for Theming
|
||||
|
||||
This project uses **CSS Variables exclusively** for theming. No Tailwind `dark:` classes or conditional CSS classes.
|
||||
|
||||
## ✅ Architecture Pattern
|
||||
|
||||
### CSS Structure
|
||||
```css
|
||||
:root {
|
||||
/* Light theme (default values) */
|
||||
--background: #f1f5f9;
|
||||
--foreground: #0f172a;
|
||||
--primary: #0891b2;
|
||||
--success: #059669;
|
||||
--destructive: #dc2626;
|
||||
--accent: #d97706;
|
||||
--purple: #8b5cf6;
|
||||
--yellow: #eab308;
|
||||
--green: #059669;
|
||||
--blue: #2563eb;
|
||||
--gray: #6b7280;
|
||||
--gray-light: #e5e7eb;
|
||||
}
|
||||
|
||||
.dark {
|
||||
/* Dark theme (override values) */
|
||||
--background: #1e293b;
|
||||
--foreground: #f1f5f9;
|
||||
--primary: #06b6d4;
|
||||
--success: #10b981;
|
||||
--destructive: #ef4444;
|
||||
--accent: #f59e0b;
|
||||
--purple: #8b5cf6;
|
||||
--yellow: #eab308;
|
||||
--green: #10b981;
|
||||
--blue: #3b82f6;
|
||||
--gray: #9ca3af;
|
||||
--gray-light: #374151;
|
||||
}
|
||||
```
|
||||
|
||||
### Theme Application
|
||||
- **Single source of truth**: [ThemeContext.tsx](mdc:src/contexts/ThemeContext.tsx) applies theme class to `document.documentElement`
|
||||
- **No duplication**: Theme is applied only once, not in multiple places
|
||||
- **SSR safe**: Initial theme from server-side preferences
|
||||
|
||||
## ✅ Component Usage Patterns
|
||||
|
||||
### Correct: Using CSS Variables
|
||||
```tsx
|
||||
// ✅ GOOD: CSS Variables in className
|
||||
<div className="bg-[var(--card)] text-[var(--foreground)] border-[var(--border)]">
|
||||
|
||||
// ✅ GOOD: CSS Variables in style prop
|
||||
<div style={{ color: 'var(--primary)', backgroundColor: 'var(--card)' }}>
|
||||
|
||||
// ✅ GOOD: CSS Variables with color-mix for transparency
|
||||
<div style={{
|
||||
backgroundColor: 'color-mix(in srgb, var(--primary) 10%, transparent)',
|
||||
borderColor: 'color-mix(in srgb, var(--primary) 20%, var(--border))'
|
||||
}}>
|
||||
```
|
||||
|
||||
### ❌ Forbidden: Tailwind Dark Mode Classes
|
||||
```tsx
|
||||
// ❌ BAD: Tailwind dark: classes
|
||||
<div className="bg-white dark:bg-gray-800 text-black dark:text-white">
|
||||
|
||||
// ❌ BAD: Conditional classes
|
||||
<div className={theme === 'dark' ? 'bg-gray-800' : 'bg-white'}>
|
||||
|
||||
// ❌ BAD: Hardcoded colors
|
||||
<div className="bg-red-500 text-blue-600">
|
||||
```
|
||||
|
||||
## ✅ Color System
|
||||
|
||||
### Semantic Color Tokens
|
||||
- `--background`: Main background color
|
||||
- `--foreground`: Main text color
|
||||
- `--card`: Card/panel background
|
||||
- `--card-hover`: Card hover state
|
||||
- `--card-column`: Column background (darker than cards)
|
||||
- `--border`: Border color
|
||||
- `--input`: Input field background
|
||||
- `--primary`: Primary brand color
|
||||
- `--primary-foreground`: Text on primary background
|
||||
- `--muted`: Muted text color
|
||||
- `--muted-foreground`: Secondary text color
|
||||
- `--accent`: Accent color (orange/amber)
|
||||
- `--destructive`: Error/danger color (red)
|
||||
- `--success`: Success color (green)
|
||||
- `--purple`: Purple accent
|
||||
- `--yellow`: Yellow accent
|
||||
- `--green`: Green accent
|
||||
- `--blue`: Blue accent
|
||||
- `--gray`: Gray color
|
||||
- `--gray-light`: Light gray background
|
||||
|
||||
### Color Mixing Patterns
|
||||
```css
|
||||
/* Background with transparency */
|
||||
background-color: color-mix(in srgb, var(--primary) 10%, transparent);
|
||||
|
||||
/* Border with transparency */
|
||||
border-color: color-mix(in srgb, var(--primary) 20%, var(--border));
|
||||
|
||||
/* Text with opacity */
|
||||
color: color-mix(in srgb, var(--destructive) 80%, transparent);
|
||||
```
|
||||
|
||||
## ✅ Theme Context Usage
|
||||
|
||||
### ThemeProvider Setup
|
||||
```tsx
|
||||
// In layout.tsx
|
||||
<ThemeProvider initialTheme={initialPreferences.viewPreferences.theme}>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
```
|
||||
|
||||
### Component Usage
|
||||
```tsx
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
|
||||
function MyComponent() {
|
||||
const { theme, toggleTheme, setTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<button onClick={toggleTheme}>
|
||||
Switch to {theme === 'dark' ? 'light' : 'dark'} theme
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## ✅ Future Extensibility
|
||||
|
||||
This system is designed to support:
|
||||
- **Custom color themes**: Easy to add new color variables
|
||||
- **User preferences**: Colors can be dynamically changed
|
||||
- **Theme presets**: Multiple predefined themes
|
||||
- **Accessibility**: High contrast modes
|
||||
|
||||
## 🚨 Anti-patterns to Avoid
|
||||
|
||||
1. **Don't mix approaches**: Never use both CSS variables and Tailwind dark: classes
|
||||
2. **Don't duplicate theme application**: Theme should be applied only in ThemeContext
|
||||
3. **Don't hardcode colors**: Always use semantic color tokens
|
||||
4. **Don't use conditional classes**: Use CSS variables instead
|
||||
5. **Don't forget transparency**: Use `color-mix()` for semi-transparent colors
|
||||
|
||||
## 📁 Key Files
|
||||
|
||||
- [globals.css](mdc:src/app/globals.css) - CSS Variables definitions
|
||||
- [ThemeContext.tsx](mdc:src/contexts/ThemeContext.tsx) - Theme management
|
||||
- [UserPreferencesContext.tsx](mdc:src/contexts/UserPreferencesContext.tsx) - Preferences sync
|
||||
- [layout.tsx](mdc:src/app/layout.tsx) - Theme provider setup
|
||||
|
||||
Remember: **CSS Variables are the single source of truth for theming. Keep it pure and consistent.**
|
||||
30
.cursor/rules/project-structure.mdc
Normal file
30
.cursor/rules/project-structure.mdc
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Project Structure Rules
|
||||
|
||||
1. Backend:
|
||||
- [src/services/](mdc:src/services/) - ALL database access
|
||||
- [src/app/api/](mdc:src/app/api/) - API routes using services
|
||||
|
||||
2. Frontend:
|
||||
- [src/clients/](mdc:src/clients/) - HTTP clients
|
||||
- [src/components/](mdc:src/components/) - React components (organized by domain)
|
||||
- [src/hooks/](mdc:src/hooks/) - React hooks
|
||||
|
||||
3. Shared:
|
||||
- [src/lib/](mdc:src/lib/) - Types and utilities
|
||||
- [scripts/](mdc:scripts/) - Utility scripts
|
||||
|
||||
Key Files:
|
||||
|
||||
- [src/services/database.ts](mdc:src/services/database.ts) - Database pool
|
||||
- [src/clients/base/http-client.ts](mdc:src/clients/base/http-client.ts) - Base HTTP client
|
||||
- [src/lib/types.ts](mdc:src/lib/types.ts) - Shared types
|
||||
|
||||
❌ FORBIDDEN:
|
||||
|
||||
- Database access outside src/services/
|
||||
- HTTP calls outside src/clients/
|
||||
- Business logic in src/components/
|
||||
113
.cursor/rules/server-actions.mdc
Normal file
113
.cursor/rules/server-actions.mdc
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
description: Guide for when to use Server Actions vs API Routes in Next.js App Router
|
||||
---
|
||||
|
||||
# Server Actions vs API Routes - Decision Guide
|
||||
|
||||
## ✅ USE SERVER ACTIONS for:
|
||||
|
||||
### Quick Actions & Mutations
|
||||
- **TaskCard actions**: `updateTaskStatus()`, `updateTaskTitle()`, `deleteTask()`
|
||||
- **Daily checkboxes**: `toggleCheckbox()`, `addCheckbox()`, `updateCheckbox()`
|
||||
- **User preferences**: `updateTheme()`, `updateViewPreferences()`, `updateFilters()`
|
||||
- **Simple CRUD**: `createTag()`, `updateTag()`, `deleteTag()`
|
||||
|
||||
### Characteristics of Server Action candidates:
|
||||
- Simple, frequent mutations
|
||||
- No complex business logic
|
||||
- Used in interactive components (forms, buttons, toggles)
|
||||
- Need immediate UI feedback with `useTransition`
|
||||
- Benefit from automatic cache revalidation
|
||||
|
||||
## ❌ KEEP API ROUTES for:
|
||||
|
||||
### Complex Endpoints
|
||||
- **Initial data fetching**: `GET /api/tasks` with complex filters
|
||||
- **External integrations**: `POST /api/jira/sync` with complex logic
|
||||
- **Analytics & reports**: Complex data aggregation
|
||||
- **Public API**: Endpoints that might be called from mobile/external
|
||||
|
||||
### Characteristics that require API Routes:
|
||||
- Complex business logic or data processing
|
||||
- Multiple service orchestration
|
||||
- Need for HTTP monitoring/logging
|
||||
- External consumption (mobile apps, webhooks)
|
||||
- Real-time features (WebSockets, SSE)
|
||||
- File uploads or special content types
|
||||
|
||||
## 🔄 Implementation Pattern
|
||||
|
||||
### Server Actions Structure
|
||||
```typescript
|
||||
// actions/tasks.ts
|
||||
'use server'
|
||||
|
||||
import { tasksService } from '@/services/tasks';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export async function updateTaskStatus(taskId: string, status: TaskStatus) {
|
||||
try {
|
||||
const task = await tasksService.updateTask(taskId, { status });
|
||||
revalidatePath('/'); // Auto cache invalidation
|
||||
return { success: true, data: task };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Component Usage with useTransition
|
||||
```typescript
|
||||
// components/TaskCard.tsx
|
||||
'use client';
|
||||
|
||||
import { updateTaskStatus } from '@/actions/tasks';
|
||||
import { useTransition } from 'react';
|
||||
|
||||
export function TaskCard({ task }) {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const handleStatusChange = (status) => {
|
||||
startTransition(async () => {
|
||||
const result = await updateTaskStatus(task.id, status);
|
||||
if (!result.success) {
|
||||
// Handle error
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={isPending ? 'opacity-50' : ''}>
|
||||
{/* UI with loading state */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 🏗️ Migration Strategy
|
||||
|
||||
When migrating from API Routes to Server Actions:
|
||||
|
||||
1. **Create server action** in `actions/` directory
|
||||
2. **Update component** to use server action directly
|
||||
3. **Remove API route** (PATCH, POST, DELETE for mutations)
|
||||
4. **Simplify client** (remove mutation methods, keep GET only)
|
||||
5. **Update hooks** to use server actions instead of HTTP calls
|
||||
|
||||
## 🎯 Benefits of Server Actions
|
||||
|
||||
- **🚀 Performance**: No HTTP serialization overhead
|
||||
- **🔄 Cache intelligence**: Automatic revalidation with `revalidatePath()`
|
||||
- **📦 Bundle reduction**: Less client-side HTTP code
|
||||
- **⚡ UX**: Native loading states with `useTransition`
|
||||
- **🎯 Simplicity**: Direct service calls, less boilerplate
|
||||
|
||||
## 🚨 Anti-patterns to Avoid
|
||||
|
||||
- Don't use server actions for complex data fetching
|
||||
- Don't use server actions for endpoints that need HTTP monitoring
|
||||
- Don't use server actions for public API endpoints
|
||||
- Don't mix server actions with client-side state management for the same data
|
||||
|
||||
Remember: Server Actions are for **direct mutations**, API Routes are for **complex operations** and **public interfaces**.
|
||||
42
.cursor/rules/services.mdc
Normal file
42
.cursor/rules/services.mdc
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
globs: src/services/*.ts
|
||||
---
|
||||
|
||||
# Services Rules
|
||||
|
||||
1. Services MUST contain ALL PostgreSQL queries
|
||||
2. Services are the ONLY layer allowed to communicate with the database
|
||||
3. Each service MUST:
|
||||
- Use the pool from [src/services/database.ts](mdc:src/services/database.ts)
|
||||
- Implement proper transaction management
|
||||
- Handle errors and logging
|
||||
- Validate data before insertion
|
||||
- Have a clear interface
|
||||
|
||||
Example of correct service implementation:
|
||||
|
||||
```typescript
|
||||
export class MyService {
|
||||
constructor(private pool: Pool) {}
|
||||
|
||||
async myMethod(): Promise<Result> {
|
||||
const client = await this.pool.connect();
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
// ... queries
|
||||
await client.query("COMMIT");
|
||||
} catch (error) {
|
||||
await client.query("ROLLBACK");
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
❌ FORBIDDEN:
|
||||
|
||||
- Direct database queries outside src/services
|
||||
- Raw SQL in API routes
|
||||
- Database logic in components
|
||||
54
.cursor/rules/todo-tracking.mdc
Normal file
54
.cursor/rules/todo-tracking.mdc
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
description: Automatic TODO tracking and task completion management
|
||||
---
|
||||
|
||||
# TODO Task Tracking Rules
|
||||
|
||||
## Automatic Task Completion
|
||||
|
||||
Whenever you complete a task or implement a feature mentioned in [TODO.md](mdc:TODO.md), you MUST:
|
||||
|
||||
1. **Immediately update the TODO.md** by changing `- [ ]` to `- [x]` for the completed task
|
||||
2. **Use the exact task description** from the TODO - don't modify the text
|
||||
3. **Update related sub-tasks** if completing a parent task affects them
|
||||
4. **Add completion timestamp** in a comment if the task was significant
|
||||
|
||||
## Task Completion Examples
|
||||
|
||||
### ✅ Correct completion marking:
|
||||
```markdown
|
||||
- [x] Initialiser Next.js avec TypeScript
|
||||
- [x] Configurer ESLint, Prettier
|
||||
- [x] Setup structure de dossiers selon les règles du workspace
|
||||
```
|
||||
|
||||
### ✅ With timestamp for major milestones:
|
||||
```markdown
|
||||
- [x] Créer `services/database.ts` - Pool de connexion DB <!-- Completed 2025-01-15 -->
|
||||
```
|
||||
|
||||
## When to Update TODO.md
|
||||
|
||||
Update the TODO immediately after:
|
||||
- Creating/modifying files mentioned in tasks
|
||||
- Implementing features described in tasks
|
||||
- Completing configuration steps
|
||||
- Finishing any work item listed in the TODO
|
||||
|
||||
## Task Dependencies
|
||||
|
||||
When completing tasks, consider:
|
||||
- **Parent tasks**: Mark parent complete only when ALL sub-tasks are done
|
||||
- **Blocking tasks**: Some tasks may unblock others - mention this in updates
|
||||
- **Phase completion**: Note when entire phases are completed
|
||||
|
||||
## Progress Tracking
|
||||
|
||||
Always maintain visibility of:
|
||||
- Current phase progress
|
||||
- Next logical task to tackle
|
||||
- Any blockers or issues encountered
|
||||
- Completed vs remaining work ratio
|
||||
|
||||
This ensures the TODO.md remains an accurate reflection of project progress and helps maintain momentum.
|
||||
313
devbook.md
Normal file
313
devbook.md
Normal file
@@ -0,0 +1,313 @@
|
||||
# SWOT Manager - Development Book
|
||||
|
||||
Application de gestion d'ateliers SWOT pour entretiens managériaux.
|
||||
|
||||
## Stack Technique
|
||||
|
||||
- **Framework**: Next.js 15 (App Router)
|
||||
- **Styling**: Tailwind CSS + CSS Variables theming
|
||||
- **Auth**: NextAuth.js v5
|
||||
- **Database**: PostgreSQL + Prisma
|
||||
- **Drag & Drop**: @hello-pangea/dnd (fork maintenu de react-beautiful-dnd)
|
||||
- **State**: React Context + Server Actions
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 : Setup Initial
|
||||
|
||||
- [ ] Initialiser Next.js 15 avec TypeScript
|
||||
- [ ] Configurer Tailwind CSS avec le système de CSS Variables
|
||||
- [ ] Setup ESLint + Prettier
|
||||
- [ ] Créer la structure de dossiers
|
||||
```
|
||||
src/
|
||||
├── app/
|
||||
│ ├── api/
|
||||
│ ├── (auth)/
|
||||
│ │ ├── login/
|
||||
│ │ └── register/
|
||||
│ ├── sessions/
|
||||
│ │ ├── [id]/
|
||||
│ │ └── new/
|
||||
│ └── layout.tsx
|
||||
├── components/
|
||||
│ ├── ui/ # Composants réutilisables
|
||||
│ ├── swot/ # Composants SWOT
|
||||
│ └── layout/ # Header, Sidebar, etc.
|
||||
├── services/ # Logique métier + DB
|
||||
├── lib/ # Types, utils
|
||||
├── contexts/ # React Contexts
|
||||
└── actions/ # Server Actions
|
||||
```
|
||||
- [ ] Configurer les CSS Variables pour le theming (light/dark)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 : Base de Données
|
||||
|
||||
- [ ] Installer et configurer Prisma
|
||||
- [ ] Créer le schéma de base de données :
|
||||
```prisma
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
email String @unique
|
||||
name String?
|
||||
password String
|
||||
sessions Session[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
title String
|
||||
collaborator String # Nom du collaborateur évalué
|
||||
date DateTime @default(now())
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
items SwotItem[]
|
||||
actions Action[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
enum SwotCategory {
|
||||
STRENGTH
|
||||
WEAKNESS
|
||||
OPPORTUNITY
|
||||
THREAT
|
||||
}
|
||||
|
||||
model SwotItem {
|
||||
id String @id @default(cuid())
|
||||
content String
|
||||
category SwotCategory
|
||||
order Int @default(0)
|
||||
sessionId String
|
||||
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
actionLinks ActionLink[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Action {
|
||||
id String @id @default(cuid())
|
||||
title String
|
||||
description String?
|
||||
priority Int @default(0) # 0=low, 1=medium, 2=high
|
||||
status String @default("todo") # todo, in_progress, done
|
||||
dueDate DateTime?
|
||||
sessionId String
|
||||
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
links ActionLink[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model ActionLink {
|
||||
id String @id @default(cuid())
|
||||
actionId String
|
||||
action Action @relation(fields: [actionId], references: [id], onDelete: Cascade)
|
||||
swotItemId String
|
||||
swotItem SwotItem @relation(fields: [swotItemId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([actionId, swotItemId])
|
||||
}
|
||||
```
|
||||
- [ ] Générer le client Prisma
|
||||
- [ ] Créer les migrations initiales
|
||||
- [ ] Créer le service database.ts (pool de connexion)
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 : Authentification
|
||||
|
||||
- [ ] Installer NextAuth.js v5
|
||||
- [ ] Configurer le provider Credentials (email/password)
|
||||
- [ ] Créer les pages :
|
||||
- [ ] `/login` - Page de connexion
|
||||
- [ ] `/register` - Page d'inscription
|
||||
- [ ] Créer le service auth.ts
|
||||
- [ ] Protéger les routes avec middleware
|
||||
- [ ] Créer le composant AuthProvider
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 : Layout & Navigation
|
||||
|
||||
- [ ] Créer le layout principal avec :
|
||||
- [ ] Header avec navigation et user menu
|
||||
- [ ] Theme toggle (dark/light)
|
||||
- [ ] Créer les composants UI de base :
|
||||
- [ ] Button
|
||||
- [ ] Card
|
||||
- [ ] Input
|
||||
- [ ] Modal
|
||||
- [ ] Badge
|
||||
- [ ] Créer la page d'accueil `/` - Dashboard avec liste des sessions
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 : Gestion des Sessions SWOT
|
||||
|
||||
- [ ] Créer le service `sessions.ts`
|
||||
- [ ] Créer les Server Actions pour les sessions :
|
||||
- [ ] `createSession`
|
||||
- [ ] `updateSession`
|
||||
- [ ] `deleteSession`
|
||||
- [ ] `getSession`
|
||||
- [ ] `getUserSessions`
|
||||
- [ ] Créer les pages :
|
||||
- [ ] `/sessions` - Liste des sessions
|
||||
- [ ] `/sessions/new` - Création de session
|
||||
- [ ] `/sessions/[id]` - Vue détaillée de la session SWOT
|
||||
- [ ] Créer les composants :
|
||||
- [ ] `SessionCard` - Carte de session dans la liste
|
||||
- [ ] `SessionForm` - Formulaire création/édition
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 : Matrice SWOT Interactive
|
||||
|
||||
- [ ] Installer @hello-pangea/dnd
|
||||
- [ ] Créer les composants SWOT :
|
||||
- [ ] `SwotBoard` - Container principal de la matrice
|
||||
- [ ] `SwotQuadrant` - Un quadrant (S, W, O, T)
|
||||
- [ ] `SwotCard` - Une carte dans un quadrant
|
||||
- [ ] `SwotCardForm` - Formulaire ajout/édition de carte
|
||||
- [ ] Implémenter le drag & drop :
|
||||
- [ ] Réorganisation dans un même quadrant
|
||||
- [ ] Déplacement entre quadrants
|
||||
- [ ] Créer les Server Actions pour les items :
|
||||
- [ ] `createSwotItem`
|
||||
- [ ] `updateSwotItem`
|
||||
- [ ] `deleteSwotItem`
|
||||
- [ ] `reorderSwotItems`
|
||||
- [ ] Édition inline des cartes
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 : Système de Liaison & Actions
|
||||
|
||||
- [ ] Créer le mode "liaison" :
|
||||
- [ ] Bouton pour activer le mode liaison
|
||||
- [ ] Sélection multiple d'items SWOT
|
||||
- [ ] Visualisation des items sélectionnés (highlight)
|
||||
- [ ] Créer les composants Actions :
|
||||
- [ ] `ActionPanel` - Panneau des actions croisées
|
||||
- [ ] `ActionCard` - Une action avec ses liens
|
||||
- [ ] `ActionForm` - Formulaire création/édition d'action
|
||||
- [ ] `LinkedItemsBadges` - Badges des items liés
|
||||
- [ ] Créer les Server Actions pour les actions :
|
||||
- [ ] `createAction`
|
||||
- [ ] `updateAction`
|
||||
- [ ] `deleteAction`
|
||||
- [ ] `linkItemToAction`
|
||||
- [ ] `unlinkItemFromAction`
|
||||
- [ ] Visualisation des liens sur la matrice (highlight on hover)
|
||||
|
||||
---
|
||||
|
||||
## Phase 8 : Roadmap & Priorisation
|
||||
|
||||
- [ ] Créer la vue Roadmap :
|
||||
- [ ] `RoadmapView` - Vue timeline des actions
|
||||
- [ ] Filtres par statut, priorité
|
||||
- [ ] Tri par date d'échéance
|
||||
- [ ] Drag & drop pour réordonner les priorités
|
||||
- [ ] Mise à jour du statut des actions (todo → in_progress → done)
|
||||
- [ ] Vue Kanban alternative des actions
|
||||
|
||||
---
|
||||
|
||||
## Phase 9 : Export & Partage
|
||||
|
||||
- [ ] Export PDF de la matrice SWOT
|
||||
- [ ] Export PDF de la roadmap
|
||||
- [ ] Génération de lien de partage en lecture seule
|
||||
- [ ] Copie au format texte pour coller dans un email
|
||||
|
||||
---
|
||||
|
||||
## Phase 10 : Polish & UX
|
||||
|
||||
- [ ] Animations et transitions fluides
|
||||
- [ ] États de chargement (skeletons)
|
||||
- [ ] Messages de confirmation et toasts
|
||||
- [ ] Raccourcis clavier :
|
||||
- [ ] `N` - Nouvelle carte
|
||||
- [ ] `Escape` - Annuler
|
||||
- [ ] `Enter` - Valider
|
||||
- [ ] Responsive design (tablette minimum)
|
||||
- [ ] Mode présentation (masquer les contrôles)
|
||||
|
||||
---
|
||||
|
||||
## Phase 11 : Bonus (Optionnel)
|
||||
|
||||
- [ ] Templates de sessions SWOT prédéfinis
|
||||
- [ ] Collaboration temps réel (plusieurs personnes sur la même session)
|
||||
- [ ] Historique des modifications
|
||||
- [ ] Import depuis Miro/Trello
|
||||
- [ ] Intégration calendrier pour les échéances
|
||||
- [ ] Notifications par email pour les deadlines
|
||||
|
||||
---
|
||||
|
||||
## Notes Techniques
|
||||
|
||||
### Structure des Server Actions
|
||||
|
||||
```typescript
|
||||
// actions/swot-items.ts
|
||||
'use server'
|
||||
|
||||
import { swotService } from '@/services/swot';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export async function createSwotItem(sessionId: string, data: CreateSwotItemInput) {
|
||||
const item = await swotService.createItem(sessionId, data);
|
||||
revalidatePath(`/sessions/${sessionId}`);
|
||||
return { success: true, data: item };
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern de Liaison
|
||||
|
||||
```typescript
|
||||
// Mode liaison : state dans le composant parent
|
||||
const [linkMode, setLinkMode] = useState(false);
|
||||
const [selectedItems, setSelectedItems] = useState<string[]>([]);
|
||||
|
||||
// Quand 2+ items sélectionnés, proposer création d'action
|
||||
if (selectedItems.length >= 2) {
|
||||
// Ouvrir modal de création d'action avec items pré-sélectionnés
|
||||
}
|
||||
```
|
||||
|
||||
### Visualisation des Liens
|
||||
|
||||
- Au hover d'une action : highlight des items liés
|
||||
- Au hover d'un item : highlight des actions liées
|
||||
- Couleurs par type de croisement (SO, ST, WO, WT)
|
||||
|
||||
---
|
||||
|
||||
## Commandes Utiles
|
||||
|
||||
```bash
|
||||
# Lancer le dev server
|
||||
npm run dev
|
||||
|
||||
# Prisma
|
||||
npx prisma migrate dev --name <name>
|
||||
npx prisma generate
|
||||
npx prisma studio
|
||||
|
||||
# Build
|
||||
npm run build
|
||||
|
||||
# Lint
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user