feat: Setup projet Classeo avec infrastructure Docker et architecture DDD
Configure l'environnement de développement complet avec Docker Compose, structure DDD 4 Bounded Contexts, et pipeline CI/CD GitHub Actions. Corrections compatibilité CI: - Symfony 8 nécessite monolog-bundle ^4.0 (la v3.x ne supporte que jusqu'à Symfony 7) - ESLint v9 nécessite flat config (eslint.config.js) - le format .eslintrc.cjs est obsolète
This commit is contained in:
62
frontend/src/app.css
Normal file
62
frontend/src/app.css
Normal file
@@ -0,0 +1,62 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--primary: 221.2 83.2% 53.3%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--ring: 221.2 83.2% 53.3%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--primary: 217.2 91.2% 59.8%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--ring: 224.3 76.3% 48%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-feature-settings:
|
||||
'rlig' 1,
|
||||
'calt' 1;
|
||||
}
|
||||
}
|
||||
16
frontend/src/app.html
Normal file
16
frontend/src/app.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#3b82f6" />
|
||||
<meta name="description" content="Classeo - Application de gestion scolaire" />
|
||||
<link rel="manifest" href="%sveltekit.assets%/manifest.webmanifest" />
|
||||
<link rel="apple-touch-icon" href="%sveltekit.assets%/apple-touch-icon.png" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
2
frontend/src/lib/index.ts
Normal file
2
frontend/src/lib/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
export * from './types';
|
||||
35
frontend/src/lib/types/api.ts
Normal file
35
frontend/src/lib/types/api.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// API response types
|
||||
export interface ApiError {
|
||||
code: string;
|
||||
message: string;
|
||||
violations?: Array<{
|
||||
propertyPath: string;
|
||||
message: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
data: T[];
|
||||
meta: {
|
||||
total: number;
|
||||
page: number;
|
||||
itemsPerPage: number;
|
||||
lastPage: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface HydraCollection<T> {
|
||||
'@context': string;
|
||||
'@id': string;
|
||||
'@type': string;
|
||||
'hydra:totalItems': number;
|
||||
'hydra:member': T[];
|
||||
'hydra:view'?: {
|
||||
'@id': string;
|
||||
'@type': string;
|
||||
'hydra:first'?: string;
|
||||
'hydra:last'?: string;
|
||||
'hydra:next'?: string;
|
||||
'hydra:previous'?: string;
|
||||
};
|
||||
}
|
||||
2
frontend/src/lib/types/index.ts
Normal file
2
frontend/src/lib/types/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './shared';
|
||||
export * from './api';
|
||||
27
frontend/src/lib/types/shared.ts
Normal file
27
frontend/src/lib/types/shared.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
// Branded types for type safety
|
||||
export type TenantId = string & { readonly brand: unique symbol };
|
||||
export type UserId = string & { readonly brand: unique symbol };
|
||||
export type NoteId = string & { readonly brand: unique symbol };
|
||||
export type ClasseId = string & { readonly brand: unique symbol };
|
||||
export type EleveId = string & { readonly brand: unique symbol };
|
||||
|
||||
// Helper functions for branded types
|
||||
export function createTenantId(id: string): TenantId {
|
||||
return id as TenantId;
|
||||
}
|
||||
|
||||
export function createUserId(id: string): UserId {
|
||||
return id as UserId;
|
||||
}
|
||||
|
||||
export function createNoteId(id: string): NoteId {
|
||||
return id as NoteId;
|
||||
}
|
||||
|
||||
export function createClasseId(id: string): ClasseId {
|
||||
return id as ClasseId;
|
||||
}
|
||||
|
||||
export function createEleveId(id: string): EleveId {
|
||||
return id as EleveId;
|
||||
}
|
||||
23
frontend/src/routes/+layout.svelte
Normal file
23
frontend/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { browser } from '$app/environment';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
const queryClient = $state(
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
enabled: browser,
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
retry: 1
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
</script>
|
||||
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{@render children()}
|
||||
</QueryClientProvider>
|
||||
28
frontend/src/routes/+page.svelte
Normal file
28
frontend/src/routes/+page.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
let count = $state(0);
|
||||
|
||||
function increment() {
|
||||
count++;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Classeo</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="flex min-h-screen flex-col items-center justify-center bg-gray-50">
|
||||
<div class="text-center">
|
||||
<h1 class="mb-4 text-4xl font-bold text-primary">Bienvenue sur Classeo</h1>
|
||||
<p class="mb-8 text-gray-600">Application de gestion scolaire</p>
|
||||
|
||||
<div class="rounded-lg bg-white p-8 shadow-md">
|
||||
<p class="mb-4 text-2xl font-semibold text-gray-800">Compteur: {count}</p>
|
||||
<button
|
||||
onclick={increment}
|
||||
class="rounded-md bg-primary px-6 py-2 text-primary-foreground transition-colors hover:bg-primary/90"
|
||||
>
|
||||
Incrementer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
Reference in New Issue
Block a user