feat: initialisation complète du projet CESIZen

- Backend Laravel 11 (API REST, JWT auth, PostgreSQL)
- Frontend Next.js 14 (React, TypeScript, Tailwind CSS)
- Docker Compose (PostgreSQL, backend, frontend)
- Modules : auth, profil, tracker émotions, informations, admin
This commit is contained in:
Azmog
2026-03-11 16:27:01 +01:00
commit e3da625927
158 changed files with 25139 additions and 0 deletions
@@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Emotion;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class AdminEmotionController extends Controller
{
public function index(): JsonResponse
{
$emotions = Emotion::niveau(1)
->with(['enfants' => fn($q) => $q->orderBy('nom')])
->orderBy('nom')
->get();
return response()->json($emotions);
}
public function store(Request $request): JsonResponse
{
$request->validate([
'nom' => 'required|string|max:255',
'couleur' => 'required|string|max:7',
'icone' => 'nullable|string|max:10',
'niveau' => 'required|integer|in:1,2',
'parent_id' => 'nullable|required_if:niveau,2|exists:emotions,id',
]);
$emotion = Emotion::create($request->only('nom', 'couleur', 'icone', 'niveau', 'parent_id'));
return response()->json([
'message' => 'Émotion créée',
'emotion' => $emotion,
], 201);
}
public function update(Request $request, int $id): JsonResponse
{
$emotion = Emotion::findOrFail($id);
$request->validate([
'nom' => 'sometimes|string|max:255',
'couleur' => 'sometimes|string|max:7',
'icone' => 'nullable|string|max:10',
'est_actif' => 'sometimes|boolean',
]);
$emotion->update($request->only('nom', 'couleur', 'icone', 'est_actif'));
return response()->json([
'message' => 'Émotion modifiée',
'emotion' => $emotion,
]);
}
public function destroy(int $id): JsonResponse
{
$emotion = Emotion::findOrFail($id);
$emotion->update(['est_actif' => false]);
$emotion->enfants()->update(['est_actif' => false]);
return response()->json([
'message' => 'Émotion désactivée',
]);
}
}
@@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreFeedRequest;
use App\Models\Feed;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Str;
class AdminFeedController extends Controller
{
public function index(): JsonResponse
{
$feeds = Feed::with('auteur:id,nom,prenom')
->orderBy('ordre')
->orderBy('created_at', 'desc')
->get();
return response()->json($feeds);
}
public function store(StoreFeedRequest $request): JsonResponse
{
$data = $request->validated();
$data['auteur_id'] = auth()->id();
$data['slug'] = $data['slug'] ?? Str::slug($data['titre']);
$feed = Feed::create($data);
return response()->json([
'message' => 'Contenu créé',
'feed' => $feed,
], 201);
}
public function update(StoreFeedRequest $request, int $id): JsonResponse
{
$feed = Feed::findOrFail($id);
$data = $request->validated();
if (isset($data['titre']) && !isset($data['slug'])) {
$data['slug'] = Str::slug($data['titre']);
}
$feed->update($data);
return response()->json([
'message' => 'Contenu modifié',
'feed' => $feed,
]);
}
public function destroy(int $id): JsonResponse
{
Feed::findOrFail($id)->delete();
return response()->json([
'message' => 'Contenu supprimé',
]);
}
}
@@ -0,0 +1,108 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Utilisateur;
use App\Models\Role;
use App\Models\Tracker;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class AdminUtilisateurController extends Controller
{
public function index(Request $request): JsonResponse
{
$query = Utilisateur::with('role');
if ($request->filled('search')) {
$search = $request->search;
$query->where(function ($q) use ($search) {
$q->where('nom', 'ilike', "%{$search}%")
->orWhere('prenom', 'ilike', "%{$search}%")
->orWhere('email', 'ilike', "%{$search}%");
});
}
if ($request->filled('role')) {
$query->whereHas('role', fn($q) => $q->where('nom', $request->role));
}
$utilisateurs = $query->orderBy('created_at', 'desc')->paginate(15);
return response()->json($utilisateurs);
}
public function show(int $id): JsonResponse
{
$utilisateur = Utilisateur::with('role')->findOrFail($id);
return response()->json($utilisateur);
}
public function store(Request $request): JsonResponse
{
$request->validate([
'nom' => 'required|string|max:255',
'prenom' => 'required|string|max:255',
'email' => 'required|email|unique:utilisateurs,email',
'password' => 'required|string|min:8',
'role_id' => 'required|exists:roles,id',
]);
$utilisateur = Utilisateur::create($request->only('nom', 'prenom', 'email', 'password', 'role_id'));
Tracker::create([
'utilisateur_id' => $utilisateur->id,
'nom' => 'Mon journal',
]);
$utilisateur->load('role');
return response()->json([
'message' => 'Utilisateur créé',
'utilisateur' => $utilisateur,
], 201);
}
public function update(Request $request, int $id): JsonResponse
{
$utilisateur = Utilisateur::findOrFail($id);
$request->validate([
'nom' => 'sometimes|string|max:255',
'prenom' => 'sometimes|string|max:255',
'email' => "sometimes|email|unique:utilisateurs,email,{$id}",
'role_id' => 'sometimes|exists:roles,id',
]);
$utilisateur->update($request->only('nom', 'prenom', 'email', 'role_id'));
$utilisateur->load('role');
return response()->json([
'message' => 'Utilisateur modifié',
'utilisateur' => $utilisateur,
]);
}
public function toggleActive(int $id): JsonResponse
{
$utilisateur = Utilisateur::findOrFail($id);
$utilisateur->update(['est_actif' => !$utilisateur->est_actif]);
return response()->json([
'message' => $utilisateur->est_actif ? 'Compte activé' : 'Compte désactivé',
'utilisateur' => $utilisateur,
]);
}
public function destroy(int $id): JsonResponse
{
$utilisateur = Utilisateur::findOrFail($id);
$utilisateur->delete();
return response()->json([
'message' => 'Utilisateur supprimé',
]);
}
}
@@ -0,0 +1,98 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\RegisterRequest;
use App\Http\Requests\LoginRequest;
use App\Models\Utilisateur;
use App\Models\Role;
use App\Models\Tracker;
use Illuminate\Http\JsonResponse;
use Tymon\JWTAuth\Facades\JWTAuth;
class AuthController extends Controller
{
public function register(RegisterRequest $request): JsonResponse
{
$role = Role::where('nom', 'membre')->first();
$utilisateur = Utilisateur::create([
'nom' => $request->nom,
'prenom' => $request->prenom,
'email' => $request->email,
'password' => $request->password,
'role_id' => $role->id,
'consentement_rgpd' => true,
]);
Tracker::create([
'utilisateur_id' => $utilisateur->id,
'nom' => 'Mon journal',
]);
$token = JWTAuth::fromUser($utilisateur);
$utilisateur->load('role');
return response()->json([
'message' => 'Inscription réussie',
'utilisateur' => $utilisateur,
'token' => $token,
], 201);
}
public function login(LoginRequest $request): JsonResponse
{
$credentials = $request->only('email', 'password');
if (!$token = auth()->attempt($credentials)) {
return response()->json([
'message' => 'Identifiants incorrects',
], 401);
}
$utilisateur = auth()->user();
if (!$utilisateur->est_actif) {
auth()->logout();
return response()->json([
'message' => 'Votre compte a été désactivé',
], 403);
}
$utilisateur->load('role');
return response()->json([
'utilisateur' => $utilisateur,
'token' => $token,
]);
}
public function logout(): JsonResponse
{
auth()->logout();
return response()->json([
'message' => 'Déconnexion réussie',
]);
}
public function me(): JsonResponse
{
$utilisateur = auth()->user();
$utilisateur->load('role');
return response()->json($utilisateur);
}
public function refresh(): JsonResponse
{
$token = auth()->refresh();
$utilisateur = auth()->user();
$utilisateur->load('role');
return response()->json([
'utilisateur' => $utilisateur,
'token' => $token,
]);
}
}
@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers;
use App\Models\Emotion;
use Illuminate\Http\JsonResponse;
class EmotionController extends Controller
{
public function index(): JsonResponse
{
$emotions = Emotion::actives()
->niveau(1)
->with(['enfants' => function ($query) {
$query->actives()->orderBy('nom');
}])
->orderBy('nom')
->get();
return response()->json($emotions);
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers;
use App\Models\Feed;
use Illuminate\Http\JsonResponse;
class FeedController extends Controller
{
public function index(): JsonResponse
{
$feeds = Feed::publie()
->orderBy('ordre')
->orderBy('created_at', 'desc')
->get(['id', 'titre', 'slug', 'contenu', 'image_url', 'ordre', 'created_at']);
return response()->json($feeds);
}
public function show(string $slug): JsonResponse
{
$feed = Feed::publie()->where('slug', $slug)->with('auteur:id,nom,prenom')->firstOrFail();
return response()->json($feed);
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\UpdateProfilRequest;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class ProfilController extends Controller
{
public function show(): JsonResponse
{
$utilisateur = auth()->user();
$utilisateur->load('role');
return response()->json($utilisateur);
}
public function update(UpdateProfilRequest $request): JsonResponse
{
$utilisateur = auth()->user();
$utilisateur->update($request->validated());
$utilisateur->load('role');
return response()->json([
'message' => 'Profil mis à jour',
'utilisateur' => $utilisateur,
]);
}
public function updatePassword(Request $request): JsonResponse
{
$request->validate([
'ancien_mot_de_passe' => 'required|string',
'password' => 'required|string|min:8|confirmed',
]);
$utilisateur = auth()->user();
if (!Hash::check($request->ancien_mot_de_passe, $utilisateur->password)) {
return response()->json([
'message' => 'L\'ancien mot de passe est incorrect',
], 422);
}
$utilisateur->update(['password' => $request->password]);
return response()->json([
'message' => 'Mot de passe modifié avec succès',
]);
}
public function destroy(): JsonResponse
{
$utilisateur = auth()->user();
$utilisateur->delete();
auth()->logout();
return response()->json([
'message' => 'Compte supprimé avec succès',
]);
}
}
@@ -0,0 +1,88 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreSaisieRequest;
use App\Models\SaisieTracker;
use App\Services\RapportService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class SaisieTrackerController extends Controller
{
public function index(Request $request): JsonResponse
{
$tracker = auth()->user()->tracker;
if (!$tracker) {
return response()->json([]);
}
$query = $tracker->saisies()->with('emotion')->orderBy('date_saisie', 'desc');
if ($request->filled('date_debut')) {
$query->where('date_saisie', '>=', $request->date_debut);
}
if ($request->filled('date_fin')) {
$query->where('date_saisie', '<=', $request->date_fin);
}
if ($request->filled('emotion_id')) {
$query->where('emotion_id', $request->emotion_id);
}
$saisies = $query->paginate(20);
return response()->json($saisies);
}
public function store(StoreSaisieRequest $request): JsonResponse
{
$tracker = auth()->user()->tracker;
if (!$tracker) {
$tracker = auth()->user()->tracker()->create(['nom' => 'Mon journal']);
}
$saisie = $tracker->saisies()->create($request->validated());
$saisie->load('emotion');
return response()->json([
'message' => 'Saisie enregistrée',
'saisie' => $saisie,
], 201);
}
public function update(StoreSaisieRequest $request, int $id): JsonResponse
{
$saisie = auth()->user()->tracker->saisies()->findOrFail($id);
$saisie->update($request->validated());
$saisie->load('emotion');
return response()->json([
'message' => 'Saisie modifiée',
'saisie' => $saisie,
]);
}
public function destroy(int $id): JsonResponse
{
$saisie = auth()->user()->tracker->saisies()->findOrFail($id);
$saisie->delete();
return response()->json([
'message' => 'Saisie supprimée',
]);
}
public function rapports(Request $request, RapportService $rapportService): JsonResponse
{
$request->validate([
'period' => 'sometimes|in:week,month,quarter,year',
]);
$period = $request->get('period', 'month');
$rapport = $rapportService->genererRapport(auth()->user(), $period);
return response()->json($rapport);
}
}