You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
969 lines
27 KiB
Markdown
969 lines
27 KiB
Markdown
|
2 months ago
|
# 📅 Semaine 5 - Setup & Structure
|
||
|
|
|
||
|
|
**Durée** : 20 heures
|
||
|
|
**Objectif** : Poser les fondations techniques et créer la documentation architecturale
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 Objectifs de la Semaine
|
||
|
|
|
||
|
|
1. ✅ Initialiser le projet Next.js avec configuration optimale
|
||
|
|
2. ✅ Mettre en place la structure microservices
|
||
|
|
3. ✅ Configurer Supabase (base de données, auth, storage)
|
||
|
|
4. ✅ Créer les 8 documents livrables (cahier des charges)
|
||
|
|
5. ✅ Setup CI/CD basique
|
||
|
|
6. ✅ Installer et configurer tous les outils (ESLint, Prettier, Testing)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 Tâches Détaillées
|
||
|
|
|
||
|
|
### Jour 1 : Initialisation Next.js & Configuration (4h)
|
||
|
|
|
||
|
|
#### Tâche 1.1 : Créer le projet Next.js (30 min)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Créer le projet
|
||
|
|
cd apps/
|
||
|
|
npx create-next-app@latest modern-app \
|
||
|
|
--typescript \
|
||
|
|
--tailwind \
|
||
|
|
--app \
|
||
|
|
--src-dir \
|
||
|
|
--import-alias "@/*" \
|
||
|
|
--no-eslint
|
||
|
|
|
||
|
|
cd modern-app
|
||
|
|
|
||
|
|
# Installer dépendances supplémentaires
|
||
|
|
pnpm add \
|
||
|
|
@supabase/supabase-js \
|
||
|
|
@supabase/auth-helpers-nextjs \
|
||
|
|
zod \
|
||
|
|
react-hook-form \
|
||
|
|
@hookform/resolvers \
|
||
|
|
zustand \
|
||
|
|
@tanstack/react-query \
|
||
|
|
date-fns \
|
||
|
|
clsx \
|
||
|
|
tailwind-merge
|
||
|
|
|
||
|
|
# Dépendances dev
|
||
|
|
pnpm add -D \
|
||
|
|
@types/node \
|
||
|
|
@types/react \
|
||
|
|
@types/react-dom \
|
||
|
|
jest \
|
||
|
|
@testing-library/react \
|
||
|
|
@testing-library/jest-dom \
|
||
|
|
@playwright/test \
|
||
|
|
eslint-config-prettier \
|
||
|
|
prettier \
|
||
|
|
husky \
|
||
|
|
lint-staged
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Projet Next.js 14 créé avec App Router
|
||
|
|
- ✅ TypeScript configuré en mode strict
|
||
|
|
- ✅ Tailwind CSS fonctionnel
|
||
|
|
- ✅ Toutes les dépendances installées
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 1.2 : Configuration TypeScript strict (30 min)
|
||
|
|
|
||
|
|
**Fichier** : `tsconfig.json`
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"compilerOptions": {
|
||
|
|
"target": "ES2020",
|
||
|
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||
|
|
"allowJs": false,
|
||
|
|
"skipLibCheck": true,
|
||
|
|
"strict": true,
|
||
|
|
"noEmit": true,
|
||
|
|
"esModuleInterop": true,
|
||
|
|
"module": "esnext",
|
||
|
|
"moduleResolution": "bundler",
|
||
|
|
"resolveJsonModule": true,
|
||
|
|
"isolatedModules": true,
|
||
|
|
"jsx": "preserve",
|
||
|
|
"incremental": true,
|
||
|
|
"plugins": [
|
||
|
|
{
|
||
|
|
"name": "next"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"paths": {
|
||
|
|
"@/*": ["./src/*"],
|
||
|
|
"@/services/*": ["./src/services/*"],
|
||
|
|
"@/components/*": ["./src/components/*"],
|
||
|
|
"@/shared/*": ["./src/shared/*"]
|
||
|
|
},
|
||
|
|
// Options strictes supplémentaires
|
||
|
|
"noUnusedLocals": true,
|
||
|
|
"noUnusedParameters": true,
|
||
|
|
"noFallthroughCasesInSwitch": true,
|
||
|
|
"noImplicitReturns": true,
|
||
|
|
"forceConsistentCasingInFileNames": true
|
||
|
|
},
|
||
|
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||
|
|
"exclude": ["node_modules"]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ TypeScript strict mode activé
|
||
|
|
- ✅ Path aliases configurés
|
||
|
|
- ✅ `pnpm tsc --noEmit` passe sans erreur
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 1.3 : Configuration ESLint & Prettier (30 min)
|
||
|
|
|
||
|
|
**Fichier** : `.eslintrc.json`
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"extends": [
|
||
|
|
"next/core-web-vitals",
|
||
|
|
"plugin:@typescript-eslint/recommended",
|
||
|
|
"prettier"
|
||
|
|
],
|
||
|
|
"rules": {
|
||
|
|
"@typescript-eslint/no-unused-vars": "error",
|
||
|
|
"@typescript-eslint/no-explicit-any": "error",
|
||
|
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||
|
|
"prefer-const": "error",
|
||
|
|
"no-console": ["warn", { "allow": ["warn", "error"] }]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichier** : `.prettierrc`
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"semi": false,
|
||
|
|
"singleQuote": true,
|
||
|
|
"tabWidth": 2,
|
||
|
|
"trailingComma": "es5",
|
||
|
|
"printWidth": 80,
|
||
|
|
"arrowParens": "always"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichier** : `package.json` (scripts)
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"scripts": {
|
||
|
|
"dev": "next dev",
|
||
|
|
"build": "next build",
|
||
|
|
"start": "next start",
|
||
|
|
"lint": "next lint",
|
||
|
|
"lint:fix": "next lint --fix",
|
||
|
|
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
|
||
|
|
"type-check": "tsc --noEmit",
|
||
|
|
"test": "jest",
|
||
|
|
"test:watch": "jest --watch",
|
||
|
|
"test:coverage": "jest --coverage",
|
||
|
|
"e2e": "playwright test",
|
||
|
|
"e2e:ui": "playwright test --ui"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ ESLint configuré avec règles strictes
|
||
|
|
- ✅ Prettier configuré
|
||
|
|
- ✅ `pnpm lint` passe sans erreur
|
||
|
|
- ✅ `pnpm format` fonctionne
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 1.4 : Configuration shadcn/ui (1h)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Initialiser shadcn/ui
|
||
|
|
pnpm dlx shadcn-ui@latest init
|
||
|
|
|
||
|
|
# Installer les composants de base
|
||
|
|
pnpm dlx shadcn-ui@latest add button
|
||
|
|
pnpm dlx shadcn-ui@latest add input
|
||
|
|
pnpm dlx shadcn-ui@latest add card
|
||
|
|
pnpm dlx shadcn-ui@latest add dialog
|
||
|
|
pnpm dlx shadcn-ui@latest add form
|
||
|
|
pnpm dlx shadcn-ui@latest add toast
|
||
|
|
pnpm dlx shadcn-ui@latest add table
|
||
|
|
pnpm dlx shadcn-ui@latest add badge
|
||
|
|
pnpm dlx shadcn-ui@latest add avatar
|
||
|
|
pnpm dlx shadcn-ui@latest add select
|
||
|
|
pnpm dlx shadcn-ui@latest add calendar
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichier** : `src/lib/utils.ts` (créé par shadcn)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { type ClassValue, clsx } from 'clsx'
|
||
|
|
import { twMerge } from 'tailwind-merge'
|
||
|
|
|
||
|
|
export function cn(...inputs: ClassValue[]) {
|
||
|
|
return twMerge(clsx(inputs))
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ shadcn/ui initialisé
|
||
|
|
- ✅ Composants de base installés dans `src/components/ui/`
|
||
|
|
- ✅ Tailwind config mise à jour avec les tokens shadcn
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 1.5 : Configuration des tests (Jest) (1h 30min)
|
||
|
|
|
||
|
|
**Fichier** : `jest.config.js`
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const nextJest = require('next/jest')
|
||
|
|
|
||
|
|
const createJestConfig = nextJest({
|
||
|
|
dir: './',
|
||
|
|
})
|
||
|
|
|
||
|
|
const customJestConfig = {
|
||
|
|
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||
|
|
testEnvironment: 'jest-environment-jsdom',
|
||
|
|
moduleNameMapper: {
|
||
|
|
'^@/(.*)$': '<rootDir>/src/$1',
|
||
|
|
},
|
||
|
|
collectCoverageFrom: [
|
||
|
|
'src/**/*.{ts,tsx}',
|
||
|
|
'!src/**/*.d.ts',
|
||
|
|
'!src/**/*.stories.tsx',
|
||
|
|
'!src/app/**', // Exclure les pages Next.js
|
||
|
|
],
|
||
|
|
coverageThreshold: {
|
||
|
|
global: {
|
||
|
|
branches: 80,
|
||
|
|
functions: 80,
|
||
|
|
lines: 80,
|
||
|
|
statements: 80,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = createJestConfig(customJestConfig)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichier** : `jest.setup.js`
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
import '@testing-library/jest-dom'
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichier** : `playwright.config.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { defineConfig, devices } from '@playwright/test'
|
||
|
|
|
||
|
|
export default defineConfig({
|
||
|
|
testDir: './tests/e2e',
|
||
|
|
fullyParallel: true,
|
||
|
|
forbidOnly: !!process.env.CI,
|
||
|
|
retries: process.env.CI ? 2 : 0,
|
||
|
|
workers: process.env.CI ? 1 : undefined,
|
||
|
|
reporter: 'html',
|
||
|
|
use: {
|
||
|
|
baseURL: 'http://localhost:3000',
|
||
|
|
trace: 'on-first-retry',
|
||
|
|
},
|
||
|
|
projects: [
|
||
|
|
{
|
||
|
|
name: 'chromium',
|
||
|
|
use: { ...devices['Desktop Chrome'] },
|
||
|
|
},
|
||
|
|
],
|
||
|
|
webServer: {
|
||
|
|
command: 'pnpm dev',
|
||
|
|
url: 'http://localhost:3000',
|
||
|
|
reuseExistingServer: !process.env.CI,
|
||
|
|
},
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Jest configuré avec React Testing Library
|
||
|
|
- ✅ Playwright configuré pour E2E
|
||
|
|
- ✅ `pnpm test` exécute les tests unitaires
|
||
|
|
- ✅ `pnpm e2e` exécute les tests E2E
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Jour 2 : Structure Microservices & Architecture (4h)
|
||
|
|
|
||
|
|
#### Tâche 2.1 : Créer la structure des microservices (2h)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Créer la structure complète
|
||
|
|
mkdir -p src/services/{auth,catalogue,reservation,inventory,payment,notification}/{domain/{entities,repositories,value-objects},application/{use-cases,services},infrastructure/{repositories,external}}
|
||
|
|
|
||
|
|
mkdir -p src/shared/{domain/{entities,value-objects},infrastructure/{di,events,supabase},utils}
|
||
|
|
|
||
|
|
mkdir -p src/components/{ui,features,layouts}
|
||
|
|
mkdir -p src/hooks
|
||
|
|
mkdir -p src/lib
|
||
|
|
mkdir -p src/types
|
||
|
|
|
||
|
|
mkdir -p tests/{unit,integration,e2e}
|
||
|
|
mkdir -p docs
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Structure des 6 microservices créée
|
||
|
|
- ✅ Chaque service suit Clean Architecture (3 couches)
|
||
|
|
- ✅ Dossiers shared/ créés
|
||
|
|
- ✅ Dossiers tests/ créés
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 2.2 : Créer les interfaces de base (Repository Pattern) (2h)
|
||
|
|
|
||
|
|
**Fichier** : `src/shared/domain/repositories/base.repository.interface.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
export interface IBaseRepository<T, ID = string> {
|
||
|
|
findById(id: ID): Promise<T | null>
|
||
|
|
findAll(filters?: Record<string, unknown>): Promise<T[]>
|
||
|
|
create(data: Omit<T, 'id'>): Promise<T>
|
||
|
|
update(id: ID, data: Partial<T>): Promise<T>
|
||
|
|
delete(id: ID): Promise<void>
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichier** : `src/shared/domain/entities/base.entity.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
export abstract class BaseEntity {
|
||
|
|
id: string
|
||
|
|
createdAt: Date
|
||
|
|
updatedAt: Date
|
||
|
|
|
||
|
|
constructor(id: string, createdAt?: Date, updatedAt?: Date) {
|
||
|
|
this.id = id
|
||
|
|
this.createdAt = createdAt || new Date()
|
||
|
|
this.updatedAt = updatedAt || new Date()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichier** : `src/shared/domain/value-objects/result.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Result Pattern (pour gérer succès/erreur)
|
||
|
|
export class Result<T> {
|
||
|
|
public isSuccess: boolean
|
||
|
|
public isFailure: boolean
|
||
|
|
public error?: string
|
||
|
|
private _value?: T
|
||
|
|
|
||
|
|
private constructor(isSuccess: boolean, error?: string, value?: T) {
|
||
|
|
this.isSuccess = isSuccess
|
||
|
|
this.isFailure = !isSuccess
|
||
|
|
this.error = error
|
||
|
|
this._value = value
|
||
|
|
}
|
||
|
|
|
||
|
|
public get value(): T {
|
||
|
|
if (this.isFailure) {
|
||
|
|
throw new Error('Cannot get value from failed result')
|
||
|
|
}
|
||
|
|
return this._value as T
|
||
|
|
}
|
||
|
|
|
||
|
|
public static ok<U>(value?: U): Result<U> {
|
||
|
|
return new Result<U>(true, undefined, value)
|
||
|
|
}
|
||
|
|
|
||
|
|
public static fail<U>(error: string): Result<U> {
|
||
|
|
return new Result<U>(false, error)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Interfaces de base créées
|
||
|
|
- ✅ Result Pattern implémenté
|
||
|
|
- ✅ BaseEntity créé
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Jour 3 : Configuration Supabase (4h)
|
||
|
|
|
||
|
|
#### Tâche 3.1 : Créer le projet Supabase (30 min)
|
||
|
|
|
||
|
|
1. Aller sur https://supabase.com
|
||
|
|
2. Créer un nouveau projet : `bricoloc-moderne`
|
||
|
|
3. Choisir la région : Europe (eu-central-1)
|
||
|
|
4. Noter les credentials :
|
||
|
|
- Project URL
|
||
|
|
- Anon Key
|
||
|
|
- Service Role Key
|
||
|
|
|
||
|
|
**Fichier** : `.env.local`
|
||
|
|
|
||
|
|
```bash
|
||
|
|
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
|
||
|
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||
|
|
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Projet Supabase créé
|
||
|
|
- ✅ Variables d'environnement configurées
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 3.2 : Créer le schéma de base de données (2h)
|
||
|
|
|
||
|
|
**Fichier** : `supabase/migrations/001_initial_schema.sql`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- Enable UUID extension
|
||
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||
|
|
|
||
|
|
-- Table users (étendue de auth.users)
|
||
|
|
CREATE TABLE public.profiles (
|
||
|
|
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
|
||
|
|
email TEXT NOT NULL,
|
||
|
|
nom TEXT,
|
||
|
|
prenom TEXT,
|
||
|
|
telephone TEXT,
|
||
|
|
adresse TEXT,
|
||
|
|
ville TEXT,
|
||
|
|
code_postal TEXT,
|
||
|
|
is_admin BOOLEAN DEFAULT FALSE,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Table categories
|
||
|
|
CREATE TABLE public.categories (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
nom TEXT NOT NULL,
|
||
|
|
description TEXT,
|
||
|
|
slug TEXT UNIQUE NOT NULL,
|
||
|
|
icone TEXT,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Table entrepots
|
||
|
|
CREATE TABLE public.entrepots (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
nom TEXT NOT NULL,
|
||
|
|
ville TEXT NOT NULL,
|
||
|
|
adresse TEXT NOT NULL,
|
||
|
|
code_postal TEXT NOT NULL,
|
||
|
|
telephone TEXT,
|
||
|
|
email TEXT,
|
||
|
|
horaires_ouverture TEXT,
|
||
|
|
latitude DECIMAL(10, 8),
|
||
|
|
longitude DECIMAL(11, 8),
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Table outils
|
||
|
|
CREATE TABLE public.outils (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
nom TEXT NOT NULL,
|
||
|
|
description TEXT,
|
||
|
|
marque TEXT,
|
||
|
|
modele TEXT,
|
||
|
|
categorie_id UUID REFERENCES categories(id) ON DELETE SET NULL,
|
||
|
|
prix_jour DECIMAL(10, 2) NOT NULL,
|
||
|
|
caution DECIMAL(10, 2),
|
||
|
|
puissance TEXT,
|
||
|
|
poids TEXT,
|
||
|
|
dimensions TEXT,
|
||
|
|
specifications JSONB,
|
||
|
|
image_principale TEXT,
|
||
|
|
images TEXT[],
|
||
|
|
reservable_professionnels_seulement BOOLEAN DEFAULT FALSE,
|
||
|
|
actif BOOLEAN DEFAULT TRUE,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Index pour recherche
|
||
|
|
CREATE INDEX idx_outils_nom ON outils USING gin(to_tsvector('french', nom));
|
||
|
|
CREATE INDEX idx_outils_description ON outils USING gin(to_tsvector('french', description));
|
||
|
|
CREATE INDEX idx_outils_categorie ON outils(categorie_id);
|
||
|
|
|
||
|
|
-- Table stocks
|
||
|
|
CREATE TABLE public.stocks (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
outil_id UUID NOT NULL REFERENCES outils(id) ON DELETE CASCADE,
|
||
|
|
entrepot_id UUID NOT NULL REFERENCES entrepots(id) ON DELETE CASCADE,
|
||
|
|
quantite_totale INTEGER NOT NULL DEFAULT 0,
|
||
|
|
quantite_disponible INTEGER NOT NULL DEFAULT 0,
|
||
|
|
quantite_reservee INTEGER NOT NULL DEFAULT 0,
|
||
|
|
seuil_alerte INTEGER DEFAULT 2,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
UNIQUE(outil_id, entrepot_id)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Table reservations
|
||
|
|
CREATE TABLE public.reservations (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
utilisateur_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
|
||
|
|
outil_id UUID NOT NULL REFERENCES outils(id) ON DELETE RESTRICT,
|
||
|
|
entrepot_id UUID NOT NULL REFERENCES entrepots(id) ON DELETE RESTRICT,
|
||
|
|
date_debut DATE NOT NULL,
|
||
|
|
date_fin DATE NOT NULL,
|
||
|
|
nombre_jours INTEGER NOT NULL,
|
||
|
|
prix_jour DECIMAL(10, 2) NOT NULL,
|
||
|
|
prix_total DECIMAL(10, 2) NOT NULL,
|
||
|
|
caution DECIMAL(10, 2),
|
||
|
|
statut TEXT NOT NULL CHECK (statut IN ('en_attente_paiement', 'confirmee', 'en_cours', 'terminee', 'annulee')),
|
||
|
|
date_retrait TIMESTAMPTZ,
|
||
|
|
date_retour TIMESTAMPTZ,
|
||
|
|
notes TEXT,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_reservations_utilisateur ON reservations(utilisateur_id);
|
||
|
|
CREATE INDEX idx_reservations_outil ON reservations(outil_id);
|
||
|
|
CREATE INDEX idx_reservations_dates ON reservations(date_debut, date_fin);
|
||
|
|
CREATE INDEX idx_reservations_statut ON reservations(statut);
|
||
|
|
|
||
|
|
-- Table transactions (paiements)
|
||
|
|
CREATE TABLE public.transactions (
|
||
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||
|
|
reservation_id UUID NOT NULL REFERENCES reservations(id) ON DELETE CASCADE,
|
||
|
|
montant DECIMAL(10, 2) NOT NULL,
|
||
|
|
statut TEXT NOT NULL CHECK (statut IN ('en_attente', 'reussie', 'echouee', 'remboursee')),
|
||
|
|
methode_paiement TEXT,
|
||
|
|
stripe_payment_intent_id TEXT,
|
||
|
|
stripe_charge_id TEXT,
|
||
|
|
metadata JSONB,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Fonction pour mettre à jour updated_at
|
||
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||
|
|
RETURNS TRIGGER AS $$
|
||
|
|
BEGIN
|
||
|
|
NEW.updated_at = NOW();
|
||
|
|
RETURN NEW;
|
||
|
|
END;
|
||
|
|
$$ LANGUAGE plpgsql;
|
||
|
|
|
||
|
|
-- Triggers pour updated_at
|
||
|
|
CREATE TRIGGER update_profiles_updated_at BEFORE UPDATE ON profiles FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
CREATE TRIGGER update_categories_updated_at BEFORE UPDATE ON categories FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
CREATE TRIGGER update_entrepots_updated_at BEFORE UPDATE ON entrepots FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
CREATE TRIGGER update_outils_updated_at BEFORE UPDATE ON outils FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
CREATE TRIGGER update_stocks_updated_at BEFORE UPDATE ON stocks FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
CREATE TRIGGER update_reservations_updated_at BEFORE UPDATE ON reservations FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
CREATE TRIGGER update_transactions_updated_at BEFORE UPDATE ON transactions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||
|
|
```
|
||
|
|
|
||
|
|
**Exécuter la migration** :
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Via Supabase CLI (optionnel)
|
||
|
|
supabase migration new initial_schema
|
||
|
|
# Copier le SQL ci-dessus dans le fichier généré
|
||
|
|
supabase db push
|
||
|
|
|
||
|
|
# OU via l'interface Supabase
|
||
|
|
# SQL Editor > Nouvelle requête > Coller le SQL > Exécuter
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Schéma de base de données créé
|
||
|
|
- ✅ 7 tables créées (profiles, categories, entrepots, outils, stocks, reservations, transactions)
|
||
|
|
- ✅ Index de recherche créés
|
||
|
|
- ✅ Triggers updated_at fonctionnels
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 3.3 : Configuration Row Level Security (RLS) (1h)
|
||
|
|
|
||
|
|
**Fichier** : `supabase/migrations/002_rls_policies.sql`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- Enable RLS on all tables
|
||
|
|
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
|
||
|
|
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
|
||
|
|
ALTER TABLE entrepots ENABLE ROW LEVEL SECURITY;
|
||
|
|
ALTER TABLE outils ENABLE ROW LEVEL SECURITY;
|
||
|
|
ALTER TABLE stocks ENABLE ROW LEVEL SECURITY;
|
||
|
|
ALTER TABLE reservations ENABLE ROW LEVEL SECURITY;
|
||
|
|
ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;
|
||
|
|
|
||
|
|
-- Profiles policies
|
||
|
|
CREATE POLICY "Users can view their own profile" ON profiles FOR SELECT USING (auth.uid() = id);
|
||
|
|
CREATE POLICY "Users can update their own profile" ON profiles FOR UPDATE USING (auth.uid() = id);
|
||
|
|
CREATE POLICY "Admins can view all profiles" ON profiles FOR SELECT USING ((SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
|
||
|
|
-- Categories policies (lecture publique)
|
||
|
|
CREATE POLICY "Categories are viewable by everyone" ON categories FOR SELECT USING (true);
|
||
|
|
CREATE POLICY "Only admins can insert categories" ON categories FOR INSERT WITH CHECK ((SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
CREATE POLICY "Only admins can update categories" ON categories FOR UPDATE USING ((SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
|
||
|
|
-- Entrepots policies (lecture publique)
|
||
|
|
CREATE POLICY "Entrepots are viewable by everyone" ON entrepots FOR SELECT USING (true);
|
||
|
|
CREATE POLICY "Only admins can manage entrepots" ON entrepots FOR ALL USING ((SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
|
||
|
|
-- Outils policies (lecture publique)
|
||
|
|
CREATE POLICY "Outils are viewable by everyone" ON outils FOR SELECT USING (actif = true OR (SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
CREATE POLICY "Only admins can manage outils" ON outils FOR ALL USING ((SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
|
||
|
|
-- Stocks policies (lecture publique)
|
||
|
|
CREATE POLICY "Stocks are viewable by everyone" ON stocks FOR SELECT USING (true);
|
||
|
|
CREATE POLICY "Only admins can manage stocks" ON stocks FOR ALL USING ((SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
|
||
|
|
-- Reservations policies
|
||
|
|
CREATE POLICY "Users can view their own reservations" ON reservations FOR SELECT USING (utilisateur_id = auth.uid() OR (SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
CREATE POLICY "Users can create reservations" ON reservations FOR INSERT WITH CHECK (utilisateur_id = auth.uid());
|
||
|
|
CREATE POLICY "Users can update their own reservations" ON reservations FOR UPDATE USING (utilisateur_id = auth.uid() AND statut = 'en_attente_paiement');
|
||
|
|
CREATE POLICY "Admins can manage all reservations" ON reservations FOR ALL USING ((SELECT is_admin FROM profiles WHERE id = auth.uid()));
|
||
|
|
|
||
|
|
-- Transactions policies
|
||
|
|
CREATE POLICY "Users can view their own transactions" ON transactions FOR SELECT USING (
|
||
|
|
(SELECT utilisateur_id FROM reservations WHERE id = reservation_id) = auth.uid()
|
||
|
|
OR (SELECT is_admin FROM profiles WHERE id = auth.uid())
|
||
|
|
);
|
||
|
|
CREATE POLICY "Only system can insert transactions" ON transactions FOR INSERT WITH CHECK (false); -- Via API seulement
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ RLS activé sur toutes les tables
|
||
|
|
- ✅ Policies créées pour sécuriser l'accès
|
||
|
|
- ✅ Utilisateurs ne peuvent voir que leurs données
|
||
|
|
- ✅ Admins ont accès complet
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 3.4 : Seed data de test (30 min)
|
||
|
|
|
||
|
|
**Fichier** : `supabase/seed.sql`
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- Insérer des catégories
|
||
|
|
INSERT INTO categories (nom, description, slug) VALUES
|
||
|
|
('Perceuses', 'Perceuses électriques et sans fil', 'perceuses'),
|
||
|
|
('Scies', 'Scies circulaires et sauteuses', 'scies'),
|
||
|
|
('Ponceuses', 'Ponceuses orbitales et excentriques', 'ponceuses'),
|
||
|
|
('Meuleuses', 'Meuleuses d''angle', 'meuleuses'),
|
||
|
|
('Échelles', 'Échelles et échafaudages', 'echelles');
|
||
|
|
|
||
|
|
-- Insérer des entrepôts
|
||
|
|
INSERT INTO entrepots (nom, ville, adresse, code_postal, telephone, email) VALUES
|
||
|
|
('BricoLoc Toulouse', 'Toulouse', '15 Avenue de la Gare', '31000', '05 61 00 00 00', 'toulouse@bricoloc.fr'),
|
||
|
|
('BricoLoc Paris', 'Paris', '42 Rue de Rivoli', '75004', '01 40 00 00 00', 'paris@bricoloc.fr');
|
||
|
|
|
||
|
|
-- Insérer quelques outils (seront complétés en Semaine 7)
|
||
|
|
INSERT INTO outils (nom, description, marque, categorie_id, prix_jour, caution, image_principale) VALUES
|
||
|
|
('Perceuse Bosch Professional', 'Perceuse à percussion 18V', 'Bosch', (SELECT id FROM categories WHERE slug = 'perceuses'), 12.00, 150.00, '/images/outils/perceuse-bosch.jpg'),
|
||
|
|
('Scie Circulaire Makita', 'Scie circulaire 1200W', 'Makita', (SELECT id FROM categories WHERE slug = 'scies'), 15.00, 200.00, '/images/outils/scie-makita.jpg');
|
||
|
|
|
||
|
|
-- Insérer des stocks
|
||
|
|
INSERT INTO stocks (outil_id, entrepot_id, quantite_totale, quantite_disponible) VALUES
|
||
|
|
((SELECT id FROM outils WHERE nom LIKE 'Perceuse%'), (SELECT id FROM entrepots WHERE ville = 'Toulouse'), 5, 5),
|
||
|
|
((SELECT id FROM outils WHERE nom LIKE 'Scie%'), (SELECT id FROM entrepots WHERE ville = 'Toulouse'), 3, 3);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Catégories de test insérées
|
||
|
|
- ✅ Entrepôts de test insérés
|
||
|
|
- ✅ Quelques outils de test insérés
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Jour 4 : Documentation Livrables (4h)
|
||
|
|
|
||
|
|
Créer les 6 documents requis par le cahier des charges.
|
||
|
|
|
||
|
|
#### Tâche 4.1 : Exigences Non Fonctionnelles (1h)
|
||
|
|
|
||
|
|
Créer `docs/NON_FUNCTIONAL_REQUIREMENTS.md` - Voir fichier séparé
|
||
|
|
|
||
|
|
#### Tâche 4.2 : Architecture Logique (1h)
|
||
|
|
|
||
|
|
Créer `docs/LOGICAL_ARCHITECTURE.md` - Voir fichier séparé
|
||
|
|
|
||
|
|
#### Tâche 4.3 : Comparaison Styles Architecturaux (1h)
|
||
|
|
|
||
|
|
Créer `docs/ARCHITECTURE_STYLES_COMPARISON.md` - Voir fichier séparé
|
||
|
|
|
||
|
|
#### Tâche 4.4 : Matrice de Choix Technologique (1h)
|
||
|
|
|
||
|
|
Créer `docs/TECHNOLOGY_DECISION_MATRIX.md` - Voir fichier séparé
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ 4 documents créés et complets
|
||
|
|
- ✅ Format Markdown professionnel
|
||
|
|
- ✅ Diagrammes Mermaid inclus
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Jour 5 : CI/CD & Finalisation (4h)
|
||
|
|
|
||
|
|
#### Tâche 5.1 : Configuration GitHub Actions (2h)
|
||
|
|
|
||
|
|
**Fichier** : `.github/workflows/ci.yml`
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
name: CI
|
||
|
|
|
||
|
|
on:
|
||
|
|
push:
|
||
|
|
branches: [main, develop]
|
||
|
|
pull_request:
|
||
|
|
branches: [main, develop]
|
||
|
|
|
||
|
|
jobs:
|
||
|
|
lint-and-type-check:
|
||
|
|
runs-on: ubuntu-latest
|
||
|
|
steps:
|
||
|
|
- uses: actions/checkout@v4
|
||
|
|
- uses: pnpm/action-setup@v2
|
||
|
|
with:
|
||
|
|
version: 8
|
||
|
|
- uses: actions/setup-node@v4
|
||
|
|
with:
|
||
|
|
node-version: '20'
|
||
|
|
cache: 'pnpm'
|
||
|
|
|
||
|
|
- name: Install dependencies
|
||
|
|
run: pnpm install
|
||
|
|
|
||
|
|
- name: Lint
|
||
|
|
run: pnpm lint
|
||
|
|
|
||
|
|
- name: Type check
|
||
|
|
run: pnpm type-check
|
||
|
|
|
||
|
|
test:
|
||
|
|
runs-on: ubuntu-latest
|
||
|
|
steps:
|
||
|
|
- uses: actions/checkout@v4
|
||
|
|
- uses: pnpm/action-setup@v2
|
||
|
|
with:
|
||
|
|
version: 8
|
||
|
|
- uses: actions/setup-node@v4
|
||
|
|
with:
|
||
|
|
node-version: '20'
|
||
|
|
cache: 'pnpm'
|
||
|
|
|
||
|
|
- name: Install dependencies
|
||
|
|
run: pnpm install
|
||
|
|
|
||
|
|
- name: Run tests
|
||
|
|
run: pnpm test:coverage
|
||
|
|
|
||
|
|
- name: Upload coverage
|
||
|
|
uses: codecov/codecov-action@v3
|
||
|
|
with:
|
||
|
|
files: ./coverage/lcov.info
|
||
|
|
|
||
|
|
build:
|
||
|
|
runs-on: ubuntu-latest
|
||
|
|
steps:
|
||
|
|
- uses: actions/checkout@v4
|
||
|
|
- uses: pnpm/action-setup@v2
|
||
|
|
with:
|
||
|
|
version: 8
|
||
|
|
- uses: actions/setup-node@v4
|
||
|
|
with:
|
||
|
|
node-version: '20'
|
||
|
|
cache: 'pnpm'
|
||
|
|
|
||
|
|
- name: Install dependencies
|
||
|
|
run: pnpm install
|
||
|
|
|
||
|
|
- name: Build
|
||
|
|
run: pnpm build
|
||
|
|
|
||
|
|
- name: Check bundle size
|
||
|
|
run: |
|
||
|
|
BUNDLE_SIZE=$(du -sh .next/static | cut -f1)
|
||
|
|
echo "Bundle size: $BUNDLE_SIZE"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ CI configuré sur GitHub Actions
|
||
|
|
- ✅ Lint, type-check, tests et build automatisés
|
||
|
|
- ✅ Pipeline s'exécute sur chaque push/PR
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 5.2 : Configuration Vercel (30 min)
|
||
|
|
|
||
|
|
1. Connecter le repo GitHub à Vercel
|
||
|
|
2. Configurer les variables d'environnement :
|
||
|
|
- `NEXT_PUBLIC_SUPABASE_URL`
|
||
|
|
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
|
||
|
|
3. Déploiement automatique sur push `main`
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Projet déployé sur Vercel
|
||
|
|
- ✅ URL de production accessible
|
||
|
|
- ✅ Variables d'environnement configurées
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 5.3 : Documentation README (1h)
|
||
|
|
|
||
|
|
**Fichier** : `README.md`
|
||
|
|
|
||
|
|
```markdown
|
||
|
|
# BricoLoc - Application Moderne
|
||
|
|
|
||
|
|
Application de location d'outils démontrant une architecture microservices moderne avec Clean Architecture.
|
||
|
|
|
||
|
|
## Stack Technique
|
||
|
|
|
||
|
|
- **Frontend** : Next.js 14, React 18, TypeScript
|
||
|
|
- **UI** : Tailwind CSS, shadcn/ui
|
||
|
|
- **Backend** : Supabase (PostgreSQL, Auth, Realtime, Storage)
|
||
|
|
- **Tests** : Jest, React Testing Library, Playwright
|
||
|
|
- **CI/CD** : GitHub Actions, Vercel
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
Cette application suit une architecture microservices hybride avec 6 services :
|
||
|
|
- Auth Service
|
||
|
|
- Catalogue Service
|
||
|
|
- Reservation Service
|
||
|
|
- Inventory Service
|
||
|
|
- Payment Service
|
||
|
|
- Notification Service
|
||
|
|
|
||
|
|
Chaque service implémente Clean Architecture avec 3 couches :
|
||
|
|
- Domain Layer (Entities, Business Rules)
|
||
|
|
- Application Layer (Use Cases, Services)
|
||
|
|
- Infrastructure Layer (Repositories, External APIs)
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Installer les dépendances
|
||
|
|
pnpm install
|
||
|
|
|
||
|
|
# Configurer les variables d'environnement
|
||
|
|
cp .env.example .env.local
|
||
|
|
# Éditer .env.local avec vos credentials Supabase
|
||
|
|
|
||
|
|
# Lancer en développement
|
||
|
|
pnpm dev
|
||
|
|
```
|
||
|
|
|
||
|
|
## Scripts Disponibles
|
||
|
|
|
||
|
|
- `pnpm dev` - Lancer le serveur de développement
|
||
|
|
- `pnpm build` - Build de production
|
||
|
|
- `pnpm start` - Lancer le build de production
|
||
|
|
- `pnpm lint` - Linter le code
|
||
|
|
- `pnpm type-check` - Vérifier les types TypeScript
|
||
|
|
- `pnpm test` - Tests unitaires
|
||
|
|
- `pnpm test:coverage` - Tests avec coverage
|
||
|
|
- `pnpm e2e` - Tests E2E avec Playwright
|
||
|
|
|
||
|
|
## Documentation
|
||
|
|
|
||
|
|
- [Roadmap](./docs/ROADMAP_MODERN.md)
|
||
|
|
- [Exigences Non Fonctionnelles](./docs/NON_FUNCTIONAL_REQUIREMENTS.md)
|
||
|
|
- [Architecture Logique](./docs/LOGICAL_ARCHITECTURE.md)
|
||
|
|
- [Comparaison Styles Architecturaux](./docs/ARCHITECTURE_STYLES_COMPARISON.md)
|
||
|
|
- [Matrice de Choix Technologique](./docs/TECHNOLOGY_DECISION_MATRIX.md)
|
||
|
|
|
||
|
|
## License
|
||
|
|
|
||
|
|
MIT
|
||
|
|
```
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ README complet et professionnel
|
||
|
|
- ✅ Instructions d'installation claires
|
||
|
|
- ✅ Liens vers documentation
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### Tâche 5.4 : Revue et Tests (30 min)
|
||
|
|
|
||
|
|
- Vérifier que toutes les commandes fonctionnent
|
||
|
|
- Vérifier que le CI passe
|
||
|
|
- Vérifier que le déploiement Vercel fonctionne
|
||
|
|
- Commit et push final
|
||
|
|
|
||
|
|
**Critères d'acceptation** :
|
||
|
|
- ✅ Toutes les commandes s'exécutent sans erreur
|
||
|
|
- ✅ CI GitHub Actions au vert
|
||
|
|
- ✅ Application déployée sur Vercel
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ✅ Checklist de Fin de Semaine 5
|
||
|
|
|
||
|
|
### Configuration Projet
|
||
|
|
- [x] Next.js 14 initialisé avec App Router ✅
|
||
|
|
- [x] TypeScript strict mode configuré ✅
|
||
|
|
- [x] ESLint + Prettier configurés ✅
|
||
|
|
- [x] shadcn/ui installé et configuré ✅
|
||
|
|
- [x] Jest + Playwright configurés ✅
|
||
|
|
|
||
|
|
### Structure
|
||
|
|
- [x] Structure microservices créée (6 services) ✅
|
||
|
|
- [x] Clean Architecture par service (3 couches) ✅
|
||
|
|
- [x] Shared/ avec DI, Event Bus, utils ✅
|
||
|
|
- [x] Interfaces de base (Repository, Entity, Result) ✅
|
||
|
|
|
||
|
|
### Supabase
|
||
|
|
- [x] Projet Supabase créé ✅
|
||
|
|
- [x] Schéma de base de données créé (7 tables) ✅
|
||
|
|
- [x] RLS policies configurées ✅
|
||
|
|
- [x] Types TypeScript générés ✅
|
||
|
|
|
||
|
|
### Documentation
|
||
|
|
- [x] NON_FUNCTIONAL_REQUIREMENTS.md ✅
|
||
|
|
- [x] LOGICAL_ARCHITECTURE.md ✅
|
||
|
|
- [x] ARCHITECTURE_STYLES_COMPARISON.md ✅
|
||
|
|
- [x] TECHNOLOGY_DECISION_MATRIX.md ✅
|
||
|
|
- [x] SUPABASE_SETUP.md ✅
|
||
|
|
|
||
|
|
### CI/CD
|
||
|
|
- [x] GitHub Actions configuré ✅
|
||
|
|
- [ ] Vercel connecté et déployé (à configurer manuellement)
|
||
|
|
- [x] README.md complet ✅
|
||
|
|
|
||
|
|
### Tests
|
||
|
|
- [x] `pnpm lint` passe ✅
|
||
|
|
- [x] `pnpm type-check` passe (src/) ✅
|
||
|
|
- [x] `pnpm build` réussit ✅
|
||
|
|
- [x] Tests unitaires (14 tests) ✅
|
||
|
|
- [ ] CI GitHub Actions au vert (nécessite push sur GitHub)
|
||
|
|
- [ ] Déploiement Vercel fonctionnel (nécessite connexion manuelle)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 Prochaine Étape
|
||
|
|
|
||
|
|
**Semaine 6** : Implémenter Auth Service avec Supabase Auth
|
||
|
|
|
||
|
|
Voir : [Semaine 6 - Auth Service](./WEEK_6_AUTH.md)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Maintenu par** : Équipe Architecture BricoLoc
|
||
|
|
**Dernière mise à jour** : 31 Octobre 2025
|