===== STRUCTURE ===== . ├── ActionRequestController.php ├── AppointmentController.php ├── AuthController.php ├── CityController.php ├── CycleController.php ├── DashboardController.php ├── DeplacementController.php ├── ExpenseController.php ├── ExpenseDashboardController.php ├── ListingController.php ├── LocationController.php ├── NotificationController.php ├── ObjectiveController.php ├── output.txt ├── PermissionController.php ├── ProductController.php ├── UserController.php ├── VisitController.php ├── VisitInProgressController.php ├── VisitProductController.php ├── VisitTypeController.php └── WeeklyProgramController.php 1 directory, 22 files ===== CONTENU DES FICHIERS ===== ----- ./ProductController.php ----- json(Product::all()); } } ----- ./ExpenseController.php ----- json( Expense::where('user_id', $request->user()->id)->get() ); } public function store(Request $request) { $validated = $request->validate([ 'category' => 'required|string', 'amount' => 'required|numeric', 'description' => 'nullable|string', ]); $expense = Expense::create([ 'user_id' => $request->user()->id, ...$validated ]); return response()->json(['message' => 'Dépense enregistrée', 'data' => $expense]); } } ----- ./AuthController.php ----- all(), [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response()->json(['errors' => $validator->errors()], 422); } $user = User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => bcrypt($request->password), ]); return response()->json(['message' => 'User registered successfully', 'user' => $user], 201); } // Login user and return JWT token public function login(Request $request) { $credentials = $request->only('email', 'password'); if (!$token = auth('api')->attempt($credentials)) { return response()->json(['error' => 'Unauthorized'], 401); } return $this->respondWithToken($token); } // Get user profile public function profile() { return response()->json(auth('api')->user()); } // Logout user (invalidate token) public function logout() { auth('api')->logout(); return response()->json(['message' => 'Successfully logged out']); } // Refresh JWT token public function refresh() { return $this->respondWithToken(auth('api')->refresh()); } // Return token response structure protected function respondWithToken($token) { return response()->json([ 'access_token' => $token, 'token_type' => 'bearer', 'expires_in' => auth('api')->factory()->getTTL() * 60, ]); } } ----- ./PermissionController.php ----- id()) ->orderByDesc('created_at') ->get(); return response()->json([ 'status' => 'success', 'data' => $permissions ]); } /** * ✅ Créer une nouvelle permission */ public function store(Request $request) { $validated = $request->validate([ 'start_date' => 'required|date', 'end_date' => 'nullable|date|after_or_equal:start_date', 'half_day' => 'boolean', 'period' => 'nullable|in:matin,apres_midi', 'reason' => 'required|in:trajet,reunion,congres,formation,maladie,affaire_personnel', 'notes' => 'nullable|string', ]); $permission = Permission::create([ 'user_id' => auth()->id(), 'start_date' => $validated['start_date'], 'end_date' => $validated['end_date'] ?? null, 'half_day' => $validated['half_day'] ?? false, 'period' => $validated['period'] ?? null, 'reason' => $validated['reason'], 'notes' => $validated['notes'] ?? null, 'status' => 'en_cours', ]); return response()->json([ 'status' => 'success', 'message' => 'Demande envoyée avec succès', 'data' => $permission ], 201); } } ----- ./output.txt ----- ----- ./VisitProductController.php ----- user()->id) ->orderByDesc('created_at') ->get(); } // 🔴 Nombre non lues public function unreadCount(Request $request) { return response()->json([ 'count' => Notification::where('user_id', $request->user()->id) ->where('is_read', false) ->count() ]); } // ✅ Marquer une notification comme lue public function markAsRead($id, Request $request) { Notification::where('id', $id) ->where('user_id', $request->user()->id) ->update(['is_read' => true]); return response()->json(['success' => true]); } // ✅ Marquer toutes comme lues public function markAllAsRead(Request $request) { Notification::where('user_id', $request->user()->id) ->where('is_read', false) ->update(['is_read' => true]); return response()->json(['success' => true]); } } ----- ./DashboardController.php ----- user(); if (!$user) { return response()->json([ 'error' => 'Utilisateur non authentifié', 'code' => 401 ], 401); } $now = Carbon::now(); // 🔁 Cycle actuel $cycle = Cycle::whereDate('start_date', '<=', $now) ->whereDate('end_date', '>=', $now) ->first(); if (!$cycle) { return response()->json([ 'error' => 'Aucun cycle actif trouvé', 'code' => 404 ], 404); } $cycleId = $cycle->id; // 🎯 Objectif = nombre de listings $cities = $user->cities()->pluck('city_id'); if ($cities->isEmpty()) { return response()->json([ 'error' => 'Aucune ville associée à cet utilisateur', 'code' => 422 ], 422); } $objectif = Listing::whereIn('city_id', $cities)->count(); if ($objectif === 0) { return response()->json([ 'error' => 'Aucun listing trouvé pour les villes de l’utilisateur', 'code' => 404 ], 404); } // ✅ Visites du cycle actuel $visitsInCycle = Visit::where('user_id', $user->id) ->where('cycle_id', $cycleId) ->count(); // 💰 Gains (visites validées × 10 DH) $validatedVisits = Visit::where('user_id', $user->id) ->where('cycle_id', $cycleId) ->where('status', 'valide') ->count(); $gains = $validatedVisits * self::PRIME_PER_VISIT; // 📊 Statistiques (tous les types) $listingTypes = [ 'PRIVE','PUBLIC','EMI-PUBLIC','MILITAIRE', 'PHARMACIE','GROSSISTERIE','MATERNITE','URGENCE' ]; $stats = collect($listingTypes)->mapWithKeys(function ($type) use ($user, $cycleId) { $count = Visit::where('user_id', $user->id) ->where('cycle_id', $cycleId) ->whereHas('listing', fn($q) => $q->where('type', $type)) ->count(); return [$type => $count]; }); // 🕒 Activité récente (visites + déplacements) $recentVisits = Visit::with('listing') ->where('user_id', $user->id) ->select('id', 'listing_id', 'status', 'created_at') ->latest() ->take(5) ->get() ->map(fn($v) => [ 'type' => 'visit', 'title' => $v->listing?->name ?? 'Listing supprimé', 'subtitle' => match ($v->status) { 'valide' => 'Visite validée', 'invalide' => 'Visite refusée', default => 'Visite en attente', }, 'status' => $v->status === 'valide' ? 'success' : 'warning', 'date' => $v->created_at, ]); $recentTrips = Deplacement::where('user_id', $user->id) ->select('id', 'from_city', 'to_city', 'status', 'created_at') ->latest() ->take(5) ->get() ->map(fn($d) => [ 'type' => 'deplacement', 'title' => "{$d->from_city} → {$d->to_city}", 'subtitle' => 'Déplacement ' . str_replace('_', ' ', $d->status), 'status' => $d->status === 'realise' ? 'success' : 'warning', 'date' => $d->created_at, ]); $recentActivity = $recentVisits ->merge($recentTrips) ->sortByDesc('date') ->take(5) ->values(); // ✅ Response return response()->json([ 'user' => [ 'name' => $user->name, 'date' => $now->translatedFormat('d F Y'), 'cities' => $cities, ], 'kpis' => [ 'visits' => $visitsInCycle, 'objectif' => $objectif, 'gains' => $gains . ' DH', ], 'statistics' => $stats, 'recent_activity' => $recentActivity, ]); } catch (Exception $e) { return response()->json([ 'error' => 'Une erreur est survenue : ' . $e->getMessage(), 'code' => 500 ], 500); } } } ----- ./ListingController.php ----- user()->cities->pluck('id'); // Récupérer le cycle actuel $today = Carbon::today(); $currentCycle = Cycle::where('start_date', '<=', $today) ->where('end_date', '>=', $today) ->first(); $listings = Listing::with('city') ->whereIn('city_id', $cityIds) ->get(); // Ajouter hasExist + trier $listings = $listings->map(function ($listing) use ($currentCycle) { $listing->cycle_name = $currentCycle?->name; $listing->hasExist = false; if ($currentCycle) { $listing->hasExist = Visit::where('listing_id', $listing->id) ->where('cycle_id', $currentCycle->id) ->exists(); } // Forcer false pour certains types if ($listing->type === 'MATERNITE' || $listing->type === 'URGENCE') { $listing->hasExist = false; } return $listing; }) ->sortBy([ ['hasExist', 'asc'], // false (0) avant true (1) ['created_at', 'desc'], // plus récent en premier ]) ->values(); return response()->json($listings); } public function show($id) { return response()->json(Listing::findOrFail($id)); } public function updateCoordinates(Request $request, $id) { $listing = Listing::findOrFail($id); // Valider les données $request->validate([ 'lat' => 'nullable|numeric|between:-90,90', 'lng' => 'nullable|numeric|between:-180,180', ]); // Mettre à jour uniquement lat et lng $listing->lat = $request->lat; $listing->lng = $request->lng; $listing->save(); return response()->json([ 'message' => 'Coordinates updated successfully', 'listing' => $listing ]); } public function store(Request $request) { $user = $request->user(); $validated = $request->validate([ 'name' => 'required|string|max:255', 'address' => 'required|string|max:255', 'city_id' => 'required|exists:cities,id', 'type' => 'nullable|string|max:100', 'specialty' => 'nullable|string|max:150', ]); // ✅ Vérifier que la ville appartient à l'utilisateur if (! $user->cities()->where('cities.id', $validated['city_id'])->exists()) { return response()->json([ 'message' => 'Ville non autorisée' ], 403); } $listing = Listing::create($validated); return response()->json([ 'message' => 'Listing créé avec succès', 'listing' => $listing ], 201); } public function update(Request $request, $id) { $listing = Listing::findOrFail($id); $validated = $request->validate([ 'name' => 'required|string|max:255', 'address' => 'required|string|max:255', 'type' => 'nullable|string|max:100', 'specialty' => 'nullable|string|max:150', ]); $listing->update($validated); return response()->json([ 'message' => 'Listing modifié avec succès', 'listing' => $listing ]); } } ----- ./DeplacementController.php ----- id()) ->orderBy('date_depart', 'desc') ->get(); return response()->json([ 'status' => 'success', 'data' => $deplacements ]); } /** * ✅ Mise à jour du statut * - non_realise → en_cours * - en_cours → realise * - en_cours → non_realise (annuler) */ public function updateStatus(Request $request, $id) { $request->validate([ 'status' => 'required|in:non_realise,en_cours,realise', ]); $deplacement = Deplacement::where('id', $id) ->where('user_id', auth()->id()) ->first(); if (!$deplacement) { return response()->json([ 'status' => 'error', 'message' => 'Déplacement non trouvé ou accès refusé' ], 404); } // ✅ Mise à jour $deplacement->status = $request->status; $deplacement->save(); return response()->json([ 'status' => 'success', 'message' => 'Statut mis à jour avec succès', 'data' => $deplacement ]); } } ----- ./VisitTypeController.php ----- user()->cities; return response()->json($cities); } // 🔹 Liste globale des villes (admin) public function index() { return response()->json(City::all()); } } ----- ./VisitController.php ----- user()->cities->pluck('id'); $visits = Visit::with(['user', 'listing', 'cycle']) ->whereHas('listing', function ($query) use ($cityIds) { $query->whereIn('city_id', $cityIds); }) ->latest() ->get(); return response()->json([ 'status' => 'success', 'data' => $visits ]); } public function valideVisits(Request $request) { $cityIds = $request->user()->cities->pluck('id'); $visits = Visit::with(['user', 'listing', 'cycle']) ->where('status', 'valide') ->whereHas('listing', function ($query) use ($cityIds) { $query->whereIn('city_id', $cityIds); }) ->latest() ->get(); return response()->json([ 'status' => 'success', 'data' => $visits ]); } public function invalideVisits() { $visits = Visit::with(['user', 'listing', 'cycle']) ->where('status', 'invalide') ->latest() ->get(); return response()->json([ 'status' => 'success', 'data' => $visits ]); } public function doubleVisits() { $visits = Visit::with(['user', 'listing', 'cycle']) ->where('status', 'double') ->latest() ->get(); return response()->json([ 'status' => 'success', 'data' => $visits ]); } public function nonRealiseeVisits() { $visits = Visit::with(['user', 'listing', 'cycle']) ->where('status', 'non_realisee') ->latest() ->get(); return response()->json([ 'status' => 'success', 'data' => $visits ]); } /** * Enregistrer une nouvelle visite */ public function store(Request $request) { // 1️⃣ Validation $validator = Validator::make($request->all(), [ 'listing_id' => 'required|integer|exists:listings,id', 'visite_type' => 'required|string|in:Face à face,Double,Accompagné', 'user_latitude' => 'nullable|numeric', 'user_longitude' => 'nullable|numeric', 'distance_meters' => 'nullable|numeric', 'admin_pending' => 'nullable|boolean', 'interlocuteur' => 'nullable|string|max:255', 'role' => 'nullable|string|max:255', 'rupture_stock' => 'nullable|boolean', 'marche_direct' => 'nullable|boolean', 'objection' => 'nullable|boolean', 'important' => 'nullable|boolean', 'elapsed_time' => 'nullable|integer|min:0', 'proposed_meds' => 'nullable|array', 'requested_meds' => 'nullable|array', 'results' => 'nullable|array', 'notes' => 'nullable|string', 'status' => 'nullable|in:valide,invalide,double,non_realisee,distante', 'user_accompagnant_id' => 'nullable|integer|exists:users,id', 'poslat' => 'nullable|numeric', 'poslong' => 'nullable|numeric', 'has_ticket' => 'nullable|boolean', 'is_far' => 'nullable|boolean', 'far_reason' => 'nullable|string|max:255', 'far_explanation' => 'nullable|string', // 'image' => 'nullable|string', ]); if ($validator->fails()) { return response()->json([ 'status' => 'error', 'message' => 'Validation failed', 'errors' => $validator->errors(), ], 422); } $data = $validator->validated(); $today = Carbon::today(); $thirtyMinutesAgo = Carbon::now()->subMinutes(30); $alreadyExists = Visit::where('user_id', auth()->id()) ->where('listing_id', $data['listing_id']) ->where('date_visit', '>=', $thirtyMinutesAgo) ->exists(); if ($alreadyExists) { return response()->json([ 'status' => 'error', 'message' => 'Une visite a déjà été enregistrée il y a quelque minutes' ], 422); } // 3️⃣ Assigner les valeurs calculées $data['status'] = 'valide'; if (!empty($request->user_position) && is_array($request->user_position)) { $data['poslat'] = $request->user_position['latitude'] ?? null; $data['poslong'] = $request->user_position['longitude'] ?? null; } // 4️⃣ Récupérer le cycle du jour $cycle = Cycle::where('start_date', '<=', $today) ->where('end_date', '>=', $today) ->first(); if (!$cycle) { return response()->json([ 'status' => 'error', 'message' => 'Aucun cycle trouvé pour aujourd’hui', ], 422); } $data['cycle_id'] = $cycle->id; $data['user_id'] = auth()->id(); $data['date_visit'] = now(); // 5️⃣ Gestion de l'image if (!empty($data['image']) && Str::startsWith($data['image'], 'data:image')) { $imageData = explode(',', $data['image'])[1]; $imageName = Str::uuid() . '.png'; Storage::disk('public')->put('visits/' . $imageName, base64_decode($imageData)); $data['image'] = 'visits/' . $imageName; } // 6️⃣ Création de la visite $visit = Visit::create($data); return response()->json([ 'status' => 'success', 'message' => 'Visite enregistrée avec succès', 'data' => $visit, ], 201); } // ✅ GET d'une visite avec nouvelles colonnes public function show($id) { $visit = Visit::with(['user', 'listing', 'cycle'])->find($id); if (!$visit) { return response()->json([ 'status' => 'error', 'message' => 'Visite non trouvée' ], 404); } return response()->json([ 'status' => 'success', 'data' => $visit ]); } public function update(Request $request, $id) { $visit = Visit::find($id); if (!$visit) { return response()->json([ 'status' => 'error', 'message' => 'Visite non trouvée' ], 404); } $validator = Validator::make($request->all(), [ // mêmes règles que pour store 'listing_id' => 'integer|exists:listings,id', 'visite_type' => 'string|in:Face à face,Double,Accompagné', 'user_latitude' => 'nullable|numeric', 'user_longitude' => 'nullable|numeric', 'distance_meters' => 'nullable|numeric', 'admin_pending' => 'boolean', 'interlocuteur' => 'nullable|string|max:255', 'role' => 'nullable|string|max:255', 'rupture_stock' => 'boolean', 'marche_direct' => 'boolean', 'objection' => 'boolean', 'important' => 'boolean', 'elapsed_time' => 'integer|min:0', 'proposed_meds' => 'nullable|array', 'requested_meds' => 'nullable|array', 'results' => 'nullable|array', 'notes' => 'nullable|string', 'status' => 'nullable|in:valide,invalide,double,non_realisee,distante', 'user_accompagnant_id' => 'nullable|integer|exists:users,id', 'poslat' => 'nullable|numeric', 'poslong' => 'nullable|numeric', 'has_ticket' => 'boolean', 'is_far' => 'boolean', 'far_reason' => 'nullable|string|max:255', 'far_explanation' => 'nullable|string', // 'image' => 'nullable|string', ]); if ($validator->fails()) { return response()->json([ 'status' => 'error', 'message' => 'Validation failed', 'errors' => $validator->errors(), ], 422); } $data = $validator->validated(); // ✅ gérer l'image si base64 if (!empty($data['image']) && Str::startsWith($data['image'], 'data:image')) { $imageData = explode(',', $data['image'])[1]; $imageName = Str::uuid() . '.png'; Storage::disk('public')->put('visits/' . $imageName, base64_decode($imageData)); $data['image'] = 'visits/' . $imageName; } $visit->update($data); return response()->json([ 'status' => 'success', 'message' => 'Visite mise à jour', 'data' => $visit ]); } public function destroy($id) { $visit = Visit::find($id); if (!$visit) { return response()->json([ 'status' => 'error', 'message' => 'Visite non trouvée' ], 404); } $visit->delete(); return response()->json([ 'status' => 'success', 'message' => 'Visite supprimée' ]); } public function today(Request $request) { try { $user = $request->user(); if (!$user) { return response()->json([ 'status' => 'error', 'message' => 'Utilisateur non authentifié' ], 401); } // ⚡ Préciser la table pour éviter l'ambiguïté $cityIds = $user->cities()->pluck('cities.id'); if ($cityIds->isEmpty()) { return response()->json([ 'status' => 'error', 'message' => 'Aucune ville associée à cet utilisateur' ], 422); } $today = Carbon::today(); $stats = Visit::join('listings', 'visits.listing_id', '=', 'listings.id') ->whereDate('visits.date_visit', $today) ->whereIn('listings.city_id', $cityIds) ->selectRaw('listings.type, COUNT(*) as total') ->groupBy('listings.type') ->get(); return response()->json([ 'status' => 'success', 'data' => $stats ]); } catch (\Exception $e) { return response()->json([ 'status' => 'error', 'message' => 'Une erreur est survenue : ' . $e->getMessage() ], 500); } } } ----- ./UserController.php ----- id(); $users = User::select('id', 'name', 'email') ->where('id', '!=', $connectedUserId) ->get(); return response()->json([ 'status' => 'success', 'users' => $users ]); } /** * Show the form for creating a new resource. */ public function create() { // } /** * Store a newly created resource in storage. */ public function store(Request $request) { // } /** * Display the specified resource. */ public function show(string $id) { // } /** * Show the form for editing the specified resource. */ public function edit(string $id) { // } public function updateProfile(Request $request) { $user = auth()->user(); $validated = $request->validate([ 'name' => ['sometimes', 'string', 'max:255'], 'email' => [ 'sometimes', 'email', Rule::unique('users')->ignore($user->id) ], 'phone' => ['nullable', 'string', 'max:20'], 'mobile' => ['nullable', 'string', 'max:20'], 'address' => ['nullable', 'string'], 'birth_date' => ['nullable', 'date'], 'national_id' => ['nullable', 'string', 'max:50'], 'car_registration' => ['nullable', 'string', 'max:20'], 'car_model' => ['nullable', 'string', 'max:50'], 'password' => ['nullable', 'confirmed', 'min:8'], // inclure password_confirmation ]); if (!empty($validated['password'])) { $validated['password'] = Hash::make($validated['password']); } else { unset($validated['password']); } $user->update($validated); if (array_key_exists('password', $validated)) { $user->update([ 'password_changed_at' => now(), 'must_change_password' => false ]); } return response()->json([ 'status' => 'success', 'message' => 'Profil mis à jour avec succès.', 'user' => $user, ]); } public function update(Request $request, string $id) { // } /** * Remove the specified resource from storage. */ public function destroy(string $id) { // } } ----- ./ExpenseDashboardController.php ----- user(); /* ================= CYCLE ACTUEL ================= */ $cycle = Cycle::whereDate('start_date', '<=', now()) ->whereDate('end_date', '>=', now()) ->first(); $cycleId = optional($cycle)->id; /* ================= PRIME PAR VISITE ================= */ $primePerVisit = SalarySetting::first()?->prime_per_visit ?? 0; /* ================= VISITES ================= */ $visitsCount = Visit::where('user_id', $user->id) ->where('cycle_id', $cycleId) ->where('status', 'valide') ->count(); $visitGain = $visitsCount * $primePerVisit; /* ================= DÉPLACEMENTS ================= */ $deplacementGain = Expense::where('user_id', $user->id) ->where('category', 'transport') ->where('status', 'approuvee') ->sum('amount'); /* ================= HORS TERRAIN ================= */ $horsTerrainGain = Expense::where('user_id', $user->id) ->whereIn('category', ['repas', 'hotel']) ->where('status', 'approuvee') ->sum('amount'); /* ================= AUTRES ================= */ $autresGain = Expense::where('user_id', $user->id) ->where('category', 'autre') ->where('status', 'approuvee') ->sum('amount'); /* ================= TOTAL ================= */ $total = $visitGain + $deplacementGain + $horsTerrainGain + $autresGain; /* ================= STATS ================= */ $stats = [ [ 'label' => 'Visite', 'gain' => round($visitGain), 'percent' => $total > 0 ? round(($visitGain / $total) * 100) : 0, ], [ 'label' => 'Déplacement', 'gain' => round($deplacementGain), 'percent' => $total > 0 ? round(($deplacementGain / $total) * 100) : 0, ], [ 'label' => 'Hors terrain', 'gain' => round($horsTerrainGain), 'percent' => $total > 0 ? round(($horsTerrainGain / $total) * 100) : 0, ], [ 'label' => 'Autres', 'gain' => round($autresGain), 'percent' => $total > 0 ? round(($autresGain / $total) * 100) : 0, ], ]; /* ================= OBJECTIFS ================= */ $objectifs = Objective::where('user_id', $user->id) ->where('cycle_id', $cycleId) ->get() ->map(function ($obj) use ($primePerVisit) { $done = $obj->target_visits > 0 ? round(($obj->achieved / $obj->target_visits) * 100) : 0; return [ 'type' => ucfirst($obj->category), 'objectif' => $obj->target_visits, 'realise' => $obj->achieved, 'done' => $done, 'gain' => round($obj->achieved * $primePerVisit), ]; }); /* ================= RESPONSE ================= */ return response()->json([ 'total' => round($total), 'stats' => $stats, 'objectifs' => $objectifs->values(), ]); } } ----- ./ObjectiveController.php ----- validate([ 'listing_id' => 'required|exists:listings,id', 'start_lat' => 'required|numeric', 'start_lng' => 'required|numeric', ]); // éviter plusieurs visites en cours $existing = VisitInProgress::where('user_id', auth()->id()) ->where('status', 'in_progress') ->first(); if ($existing) { return response()->json([ 'status' => 'error', 'message' => 'Une visite est déjà en cours' ], 400); } $visit = VisitInProgress::create([ 'user_id' => auth()->id(), 'listing_id' => $data['listing_id'], 'start_time' => now(), 'start_lat' => $data['start_lat'], 'start_lng' => $data['start_lng'], 'status' => 'in_progress', ]); return response()->json([ 'status' => 'success', 'visit' => $visit ]); } // 🧨 FINISH VISIT public function finish(Request $request) { $data = $request->validate([ 'end_lat' => 'required|numeric', 'end_lng' => 'required|numeric', ]); $visit = VisitInProgress::where('user_id', auth()->id()) ->where('status', 'in_progress') ->latest() ->first(); if (!$visit) { return response()->json([ 'status' => 'error', 'message' => 'Aucune visite en cours' ], 404); } $visit->update([ 'end_time' => now(), 'end_lat' => $data['end_lat'], 'end_lng' => $data['end_lng'], 'status' => 'finished', ]); return response()->json([ 'status' => 'success', 'visit' => $visit ]); } } ----- ./WeeklyProgramController.php ----- json(Appointment::where('user_id', $request->user()->id)->get()); } public function store(Request $request) { $validated = $request->validate([ 'listing_id' => 'required|integer', 'date_appointment' => 'required|date' ]); $appt = Appointment::create([ 'user_id' => $request->user()->id, ...$validated ]); return response()->json(['message' => 'Rendez-vous ajouté', 'data' => $appt]); } public function destroy($id) { Appointment::findOrFail($id)->delete(); return response()->json(['message' => 'Rendez-vous supprimé']); } } ----- ./CycleController.php ----- json(Cycle::all()); } } class ObjectiveController extends Controller { public function index() { return response()->json(Objective::with('cycle')->get()); } } class WeeklyProgramController extends Controller { public function userProgram() { return response()->json(WeeklyProgram::with('cycle','city')->get()); } } class DashboardController extends Controller { public function index() { return response()->json([ 'total_visits' => 38, 'goal' => 50, 'medications_presented' => 13, 'earnings' => 2500 ]); } } ----- ./LocationController.php ----- validate([ 'latitude' => 'required|numeric', 'longitude' => 'required|numeric', ]); $location = Location::create([ 'user_id' => $request->user()->id, 'latitude' => $request->latitude, 'longitude' => $request->longitude, 'recorded_at' => now(), ]); \Log::info("User {$request->user()->id} location saved: {$request->latitude}, {$request->longitude}"); return response()->json([ 'status' => 'success', 'location' => $location ]); } public function bulk(Request $request) { $locations = $request->input('locations', []); foreach ($locations as $loc) { Location::create([ 'user_id' => $request->user()->id, 'latitude' => $loc['latitude'], 'longitude' => $loc['longitude'], 'recorded_at' => $loc['recorded_at'] ?? now(), ]); } return response()->json(['status' => 'success', 'count' => count($locations)]); } } ----- ./ActionRequestController.php ----- where('user_id', auth()->id()) ->when($request->search, function ($q) use ($request) { $q->where('produit', 'like', "%{$request->search}%") ->orWhere('type', 'like', "%{$request->search}%"); }) ->orderByDesc('created_at') ->get(); return response()->json([ 'data' => $actions ]); } }