Procházet zdrojové kódy

(feat) version 0.01 avant le payement

Brin AMONKONAN před 1 rokem
rodič
revize
f1746fd304
63 změnil soubory, kde provedl 5118 přidání a 967 odebrání
  1. 86 44
      app_transport_front/src/components/EmployeeModal.vue
  2. 123 10
      app_transport_front/src/components/ManagerModal.vue
  3. 16 26
      app_transport_front/src/components/TripSearchForm.vue
  4. 6 0
      app_transport_front/src/router/index.ts
  5. 28 2
      app_transport_front/src/services/CompanyService.ts
  6. 153 0
      app_transport_front/src/services/ReservationService.ts
  7. 32 14
      app_transport_front/src/services/StationCompanyService.ts
  8. 27 17
      app_transport_front/src/services/StationService.ts
  9. 235 0
      app_transport_front/src/services/TripService.ts
  10. 19 0
      app_transport_front/src/services/UserService.ts
  11. 172 0
      app_transport_front/src/services/VehiculeService.ts
  12. 11 3
      app_transport_front/src/stores/useStationStore.ts
  13. 46 1
      app_transport_front/src/stores/useUserStore.ts
  14. 63 6
      app_transport_front/src/types/index.ts
  15. 45 32
      app_transport_front/src/views/CompanyView.vue
  16. 363 221
      app_transport_front/src/views/EmployeeView.vue
  17. 251 29
      app_transport_front/src/views/HomeView.vue
  18. 1 0
      app_transport_front/src/views/ProfileVerificationView.vue
  19. 1576 0
      app_transport_front/src/views/ReservationView.vue
  20. 171 50
      app_transport_front/src/views/StationCompanyView.vue
  21. 319 20
      app_transport_front/src/views/StationView.vue
  22. 428 329
      app_transport_front/src/views/TripView.vue
  23. 143 118
      app_transport_front/src/views/VehicleView.vue
  24. 8 0
      node_app/prisma/migrations/20250604172111_add_company_employee_relation/migration.sql
  25. 2 0
      node_app/prisma/migrations/20250605093215_add_price_per_seat_with_default/migration.sql
  26. 12 0
      node_app/prisma/migrations/20250605154937_add_company_relation_and_place_availability/migration.sql
  27. 12 0
      node_app/prisma/schema.prisma
  28. 48 1
      node_app/src/app.module.ts
  29. 1 1
      node_app/src/application/dto/create-station.dto.ts
  30. 19 1
      node_app/src/application/dto/create-trajet.dto.ts
  31. 10 0
      node_app/src/application/dto/create-user.dto.ts
  32. 6 0
      node_app/src/application/dto/responses/trajet-response.dto.ts
  33. 5 0
      node_app/src/application/dto/update-user.dto.ts
  34. 25 0
      node_app/src/application/use-cases/company/find-employees-by-company.use-case.ts
  35. 18 0
      node_app/src/application/use-cases/station-company/create-station-company.use-case.ts
  36. 15 0
      node_app/src/application/use-cases/station-company/delete-station-company.use-case.ts
  37. 21 0
      node_app/src/application/use-cases/station-company/find-all-station-companies.use-case.ts
  38. 15 0
      node_app/src/application/use-cases/station-company/find-station-companies-by-company.use-case.ts
  39. 15 0
      node_app/src/application/use-cases/station-company/find-station-companies-by-station.use-case.ts
  40. 15 0
      node_app/src/application/use-cases/station-company/find-station-company-by-id.use-case.ts
  41. 36 0
      node_app/src/application/use-cases/station/create-station.use-case.ts
  42. 26 0
      node_app/src/application/use-cases/station/delete-station.use-case.ts
  43. 26 0
      node_app/src/application/use-cases/station/find-all-stations.use-case.ts
  44. 20 0
      node_app/src/application/use-cases/station/find-station-by-id.use-case.ts
  45. 23 0
      node_app/src/application/use-cases/station/find-stations-by-city.use-case.ts
  46. 23 0
      node_app/src/application/use-cases/station/search-stations-by-name.use-case.ts
  47. 32 0
      node_app/src/application/use-cases/station/update-station.use-case.ts
  48. 45 0
      node_app/src/application/use-cases/trajet/find-trajets-by-company.use-case.ts
  49. 2 0
      node_app/src/domain/entities/company.entity.ts
  50. 4 0
      node_app/src/domain/entities/place.entity.ts
  51. 2 0
      node_app/src/domain/entities/trajet.entity.ts
  52. 2 0
      node_app/src/domain/entities/user.entity.ts
  53. 39 0
      node_app/src/infrastructure/database/repositories/company.repository.ts
  54. 66 2
      node_app/src/infrastructure/database/repositories/station-company.repository.ts
  55. 56 5
      node_app/src/infrastructure/database/repositories/trajet.repository.ts
  56. 4 0
      node_app/src/infrastructure/mappers/place.mapper.ts
  57. 13 1
      node_app/src/infrastructure/mappers/station-company.mapper.ts
  58. 6 0
      node_app/src/infrastructure/mappers/trajet.mapper.ts
  59. 2 0
      node_app/src/infrastructure/mappers/user.mapper.ts
  60. 33 1
      node_app/src/presentation/controllers/company.controller.ts
  61. 50 14
      node_app/src/presentation/controllers/station-company.controller.ts
  62. 25 17
      node_app/src/presentation/controllers/station.controller.ts
  63. 22 2
      node_app/src/presentation/controllers/trajet.controller.ts

+ 86 - 44
app_transport_front/src/components/EmployeeModal.vue

@@ -81,8 +81,8 @@
               :disabled="!canManageEmployees"
             >
               <option value="">Sélectionner un rôle</option>
-              <option v-for="(roleValue, roleKey) in Role" :key="roleKey" :value="roleValue">
-                {{ RoleLabel[roleKey as keyof typeof RoleLabel] }}
+              <option v-for="role in availableRoles" :key="role.key" :value="role.value">
+                {{ role.label }}
               </option>
             </select>
           </div>
@@ -189,12 +189,10 @@ import { useToast } from '@/composables/useToast'
 interface Props {
   show: boolean
   editingEmployee?: User | null
-  roles: Array<{ id: string | number, name: string }>
 }
 
 const props = withDefaults(defineProps<Props>(), {
-  editingEmployee: null,
-  roles: () => []
+  editingEmployee: null
 })
 
 // Emits
@@ -235,16 +233,23 @@ const canManageEmployees = computed(() => {
 const availableRoles = computed(() => {
   if (!canManageEmployees.value) return []
   
-  // Filtrer les rôles selon les permissions
-  return props.roles.filter(role => {
-    const roleName = role.name.toUpperCase()
+  // Obtenir tous les rôles disponibles depuis l'énumération
+  const allRoles = Object.entries(RoleLabel).map(([key, label]) => ({
+    value: Role[key as keyof typeof Role],
+    label: label,
+    key: key
+  }))
+  
+  // Filtrer les rôles selon les permissions de l'utilisateur connecté
+  return allRoles.filter(role => {
+    const roleKey = role.key.toUpperCase()
     
     if (userStore.userRole === Role.Admin) {
       // Les admins peuvent créer tous les rôles sauf ADMIN
-      return roleName !== 'ADMIN'
+      return roleKey !== 'ADMIN'
     } else if (userStore.userRole === Role.Manager) {
       // Les managers peuvent créer seulement EMPLOYEE et DRIVER
-      return roleName === 'EMPLOYEE' || roleName === 'DRIVER'
+      return roleKey === 'EMPLOYEE' || roleKey === 'DRIVER'
     }
     
     return false
@@ -305,6 +310,7 @@ const loadEmployeeData = () => {
 
 const saveEmployee = async () => {
   if (!canManageEmployees.value || !isFormValid.value) {
+    error('Veuillez remplir tous les champs obligatoires')
     return
   }
 
@@ -315,48 +321,77 @@ const saveEmployee = async () => {
     
     if (props.editingEmployee) {
       // Mode modification - utiliser updateUser
-      const updateData = {
-         name: formData.value.lastName,
-         otherName: formData.value.firstName,
-         email: formData.value.email,
-         telephone: formData.value.phone,
-         address: formData.value.address,
-         role: formData.value.role as Role,
-         password: formData.value.password.trim() || undefined
+      const updateData: any = {
+         name: formData.value.lastName.trim(),
+         otherName: formData.value.firstName.trim(),
+         email: formData.value.email.trim(),
+         telephone: formData.value.phone.trim(),
+         address: formData.value.address.trim(),
+         role: formData.value.role as Role
        }
       
-      // Si le mot de passe est vide, on l'enlève
-      if (!updateData.password) {
-        delete updateData.password
+      // Ajouter le mot de passe seulement s'il est fourni
+      if (formData.value.password.trim()) {
+        updateData.password = formData.value.password.trim()
       }
       
       savedEmployee = await UserService.updateUser(props.editingEmployee.id, updateData)
+      
+      if (savedEmployee) {
+        success('Employé modifié avec succès!')
+      }
     } else {
-      // Mode création - créer d'abord l'utilisateur sans rôle
+      // Mode création - validation du mot de passe
       if (!formData.value.password.trim()) {
         error('Le mot de passe est requis pour créer un nouvel employé')
         return
       }
       
-      const createData = {
-        name: formData.value.lastName,
-        otherName: formData.value.firstName,
-        email: formData.value.email,
-        telephone: formData.value.phone,
-        address: formData.value.address,
-        password: formData.value.password
+      // Validation de l'email unique
+      if (!formData.value.email.includes('@')) {
+        error('Veuillez saisir un email valide')
+        return
       }
       
-      // Créer l'utilisateur
-      const createdUser = await UserService.createUser(createData)
+      // Récupérer l'ID de l'entreprise depuis le token JWT décodé
+      const companyId = userStore.decodedCompanyId || userStore.companyId
       
-      if (createdUser) {
-        console.log('Utilisateur créé avec succès:', createdUser)
-         // Mettre à jour avec le rôle
-         savedEmployee = await UserService.updateUser(createdUser.id, {
-           role: formData.value.role as Role
-         })
-       }
+      if (!companyId) {
+        error('Aucune entreprise associée à votre compte')
+        return
+      }
+      
+      // Créer d'abord l'utilisateur sans rôle (comme dans ManagerModal)
+      const { role, ...createDataSansRole } = {
+        name: formData.value.lastName.trim(),
+        otherName: formData.value.firstName.trim(),
+        email: formData.value.email.trim(),
+        telephone: formData.value.phone.trim(),
+        address: formData.value.address.trim(),
+        password: formData.value.password.trim(),
+        role: formData.value.role as Role,
+        companyId: parseInt(companyId) // Ajouter l'ID de l'entreprise
+      }
+      
+      // Créer l'utilisateur sans le rôle
+      const newEmployee = await UserService.createUser(createDataSansRole as User)
+      
+      if (newEmployee && newEmployee.id) {
+        // Assigner le rôle après création
+        newEmployee.role = formData.value.role as Role
+        
+        // Vérifier que l'utilisateur existe puis mettre à jour avec le rôle
+        const response = await UserService.getUser(newEmployee.id)
+        const valide = response ? await UserService.updatedByPatchUser(newEmployee.id, newEmployee) : new Error('Erreur lors de la mise à jour de l\'employé')
+        
+        if (valide && !(valide instanceof Error)) {
+          savedEmployee = newEmployee
+          success('Employé créé avec succès!')
+        } else {
+          error('Erreur lors de l\'assignation du rôle à l\'employé')
+          return
+        }
+      }
     }
     
     if (savedEmployee) {
@@ -365,16 +400,23 @@ const saveEmployee = async () => {
       
       // Fermer le modal
       closeModal()
-      
-      // Afficher un message de succès
-      success(props.editingEmployee ? 'Employé modifié avec succès!' : 'Employé créé avec succès!')
     } else {
       error('Erreur lors de la sauvegarde de l\'employé')
     }
     
-  } catch (error) {
-    console.error('Erreur lors de la sauvegarde de l\'employé:', error)
-    error('Erreur lors de la sauvegarde de l\'employé')
+  } catch (err: any) {
+    console.error('Erreur lors de la sauvegarde de l\'employé:', err)
+    
+    // Gestion des erreurs spécifiques
+    if (err.response?.status === 409) {
+      error('Cet email est déjà utilisé par un autre utilisateur')
+    } else if (err.response?.status === 400) {
+      error('Données invalides. Veuillez vérifier les informations saisies')
+    } else if (err.response?.status === 403) {
+      error('Vous n\'avez pas les permissions pour effectuer cette action')
+    } else {
+      error('Erreur lors de la sauvegarde de l\'employé')
+    }
   } finally {
     isSaving.value = false
   }

+ 123 - 10
app_transport_front/src/components/ManagerModal.vue

@@ -1,5 +1,6 @@
 <template>
-  <div v-if="show" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" style="z-index: 9999;">
+  <!-- Mode modal normal -->
+  <div v-if="show && !inline" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" style="z-index: 9999;">
     <div class="bg-white rounded-lg p-5 w-full max-w-md mx-auto" style="z-index: 10000;">
       <div class="modal-header flex justify-between items-center mb-5">
         <h3 class="text-lg font-semibold text-gray-800">Créer un nouveau gestionnaire</h3>
@@ -105,6 +106,112 @@
       </div>
     </div>
   </div>
+
+  <!-- Mode inline -->
+  <div v-if="show && inline" class="w-full h-full">
+    <div class="modal-header flex justify-between items-center mb-5">
+      <h3 class="text-lg font-semibold text-gray-800">Créer un nouveau gestionnaire</h3>
+      <button class="bg-transparent border-0 text-xl text-gray-400 cursor-pointer" @click="closeModal">
+        <i class="fas fa-times"></i>
+      </button>
+    </div>
+
+    <!-- Message d'erreur si l'utilisateur n'a pas les permissions -->
+    <div v-if="!canCreateManager" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
+      <strong>Accès refusé :</strong> Seuls les administrateurs peuvent créer des gestionnaires.
+    </div>
+
+    <div v-else class="modal-body">
+      <div class="form-group mb-2.5">
+        <label class="block text-sm font-medium text-gray-700 mb-1">Nom *</label>
+        <input 
+          type="text" 
+          v-model="formData.name" 
+          required 
+          class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm"
+          :disabled="!canCreateManager"
+        >
+      </div>
+
+      <div class="form-group mb-2.5">
+        <label class="block text-sm font-medium text-gray-700 mb-1">Prénom</label>
+        <input 
+          type="text" 
+          v-model="formData.otherName" 
+          class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm"
+          :disabled="!canCreateManager"
+        >
+      </div>
+
+      <div class="form-group mb-2.5">
+        <label class="block text-sm font-medium text-gray-700 mb-1">Email *</label>
+        <input 
+          type="email" 
+          v-model="formData.email" 
+          required 
+          class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm"
+          :disabled="!canCreateManager"
+        >
+      </div>
+
+      <div class="form-group mb-2.5">
+        <label class="block text-sm font-medium text-gray-700 mb-1">Mot de passe *</label>
+        <input 
+          type="password" 
+          v-model="formData.password" 
+          required 
+          class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm"
+          :disabled="!canCreateManager"
+        >
+      </div>
+
+      <div class="form-group mb-2.5">
+        <label class="block text-sm font-medium text-gray-700 mb-1">Adresse</label>
+        <input 
+          type="text" 
+          v-model="formData.address" 
+          class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm"
+          :disabled="!canCreateManager"
+        >
+      </div>
+
+      <div class="form-group mb-2.5">
+        <label class="block text-sm font-medium text-gray-700 mb-1">Téléphone</label>
+        <input 
+          type="tel" 
+          v-model="formData.telephone" 
+          class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm"
+          :disabled="!canCreateManager"
+        >
+      </div>
+
+      <!-- Information sur le rôle assigné -->
+      <div class="bg-blue-50 border border-blue-200 text-blue-700 px-4 py-3 rounded mb-4">
+        <p class="text-sm">
+          <i class="fas fa-info-circle mr-2"></i>
+          <strong>Rôle assigné :</strong> MANAGER (Gestionnaire)
+        </p>
+      </div>
+    </div>
+
+    <div class="modal-footer flex justify-end gap-2.5 mt-5">
+      <button 
+        class="bg-gray-100 text-gray-600 border-0 rounded py-2.5 px-4 cursor-pointer" 
+        @click="closeModal"
+      >
+        Annuler
+      </button>
+      <button 
+        v-if="canCreateManager"
+        class="bg-blue-600 text-white border-0 rounded py-2.5 px-4 cursor-pointer disabled:bg-gray-400 disabled:cursor-not-allowed" 
+        @click="createManager" 
+        :disabled="!isFormValid || isCreating"
+      >
+        <i v-if="isCreating" class="fas fa-spinner fa-spin mr-2"></i>
+        {{ isCreating ? 'Création...' : 'Créer le gestionnaire' }}
+      </button>
+    </div>
+  </div>
 </template>
 
 <script setup lang="ts">
@@ -116,7 +223,8 @@ import type { User } from '@/types/index'
 
 // Props
 interface Props {
-  show: boolean
+  show: boolean,
+  inline?: boolean // Nouvelle prop pour le mode inline
 }
 
 const props = defineProps<Props>()
@@ -182,15 +290,20 @@ const createManager = async () => {
 
   try {
     isCreating.value = true
+
+    const { role, ...formDataSansRole } = formData.value   
+    const newManager = await UserService.createUser(formDataSansRole as User)
     
-    // Créer le gestionnaire avec le rôle MANAGER
-    const newManager = await UserService.createUser(formData.value)
-    
-    // Émettre l'événement avec le nouveau gestionnaire
-    emit('managerCreated', newManager)
-    
-    // Fermer le modal
-    closeModal()
+    if (newManager && newManager.id) {
+      newManager.role = Role.Manager
+
+      const response = await UserService.getUser(newManager.id)
+      const valide = response ?  await UserService.updatedByPatchUser(newManager.id, newManager) : new Error('Erreur lors de la mise à jour du gestionnaire')
+      emit('managerCreated', newManager)
+      console.log('valide', valide) 
+      // Fermer le modal
+      closeModal()
+    }
     
   } catch (error) {
     console.error('Erreur lors de la création du gestionnaire:', error)

+ 16 - 26
app_transport_front/src/components/TripSearchForm.vue

@@ -4,20 +4,20 @@
     <form @submit.prevent="handleSearch" class="search-form">
       <div class="form-row">
         <div class="form-group">
-          <label for="departure">Ville de départ</label>
+          <label for="departure">Station de départ</label>
           <select id="departure" class="flex w-full" v-model="searchCriteria.departure" required>
-            <option value="" disabled>Sélectionner une ville</option>
-            <option v-for="city in cities" :key="city.name" :value="city.name">
-              {{ city.name }}
+            <option value="" disabled>Sélectionner une station</option>
+            <option v-for="station in cities" :key="station.id" :value="station.name">
+              {{ station.name }}
             </option>
           </select>
         </div>
         <div class="form-group">
-          <label for="arrival">Ville d'arrivée</label>
+          <label for="arrival">Station d'arrivée</label>
           <select id="arrival" class="flex w-full" v-model="searchCriteria.arrival" required>
-            <option value="" disabled>Sélectionner une ville</option>
-            <option v-for="city in cities" :key="city.name" :value="city.name">
-              {{ city.name }}
+            <option value="" disabled>Sélectionner une station</option>
+            <option v-for="station in cities" :key="station.id" :value="station.name">
+              {{ station.name }}
             </option>
           </select>
         </div>
@@ -50,14 +50,10 @@
 
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
+import StationService from '@/services/StationService';
+import type { Station } from '@/types';
 
-interface City {
-  name: string;
-  // Vous pouvez ajouter d'autres propriétés ici si elles existent dans votre JSON
-  // par exemple: id: number;
-}
-
-const cities = ref<City[]>([]);
+const cities = ref<Station[]>([]);
 
 const searchCriteria = ref({
   departure: '',
@@ -76,18 +72,12 @@ const handleSearch = () => {
 
 onMounted(async () => {
   try {
-    // Assurez-vous que civ.cities.json est dans le dossier public/datas/
-    const response = await fetch('/datas/civ.cities.json');
-    if (!response.ok) {
-      throw new Error(`HTTP error! status: ${response.status}`);
-    }
-    const data = await response.json();
-    // Si votre JSON est un tableau d'objets ayant une propriété "name"
-    // Si la structure est [{"nom": "Abidjan"}, ...] utilisez data.map(c => ({ name: c.nom }))
-    // Si c'est un tableau de chaînes ["Abidjan", ...], utilisez data.map(name => ({ name }))
-    cities.value = data; 
+    // Utiliser StationService pour récupérer les stations depuis l'API
+    const result = await StationService.getStations(1, 1000); // Récupérer toutes les stations
+    console.log("Stations récupérées:", result); // Ajout de ce log pour débogage potentie
+    cities.value = result.stations;
   } catch (error) {
-    console.error("Impossible de charger les villes:", error);
+    console.error("Impossible de charger les stations:", error);
     // Gérer l'erreur, par exemple, afficher un message à l'utilisateur
   }
 });

+ 6 - 0
app_transport_front/src/router/index.ts

@@ -97,6 +97,12 @@ const routes: Array<RouteRecordRaw> = [
     component: () => import('../views/CompanySetupView.vue'),
     meta: { requiresAuth: true, roles: [Role.Admin, Role.Manager, Role.User] }
   },
+  {
+    path: '/reservation/:id',
+    name: 'reservation',
+    component: () => import('../views/ReservationView.vue'),
+    meta: { requiresAuth: false }
+  },
   // {
   //   path: '/driver-setup',
   //   name: 'driver-setup',

+ 28 - 2
app_transport_front/src/services/CompanyService.ts

@@ -30,7 +30,7 @@ export default {
     }
   },
 
-  async getCompany(id: number): Promise<Company | null> {
+  async getCompany(id: number): Promise<any | null> {
     try {
       const response = await authenticatedFetch(`${API_URL}/companies/${id}`)
       if (!response.ok) {
@@ -113,7 +113,7 @@ export default {
     }
   },
 
-  async getCompanyTrips(id: number): Promise<any[]> {
+  async getCompanyTrips(id: number): Promise<Company[]> {
     try {
       const response = await authenticatedFetch(`${API_URL}/companies/${id}/trips`)
       if (!response.ok) {
@@ -124,5 +124,31 @@ export default {
       console.error('Erreur:', error)
       return []
     }
+  },
+
+  async getCompanyEmployees(id: number, page: number = 1, limit: number = 10): Promise<{ employees: any[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/companies/${id}/employees?page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des employés de l\'entreprise')
+      }
+      const result = await response.json()
+      // Adapter la structure de réponse
+      return {
+        employees: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        employees: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
   }
 }

+ 153 - 0
app_transport_front/src/services/ReservationService.ts

@@ -0,0 +1,153 @@
+import type { Reservation } from '@/types'
+
+const API_URL = 'http://localhost:3001/api'
+
+export default {
+  async getReservations(): Promise<Reservation[]> {
+    try {
+      const response = await fetch(`${API_URL}/reservations`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des réservations')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return []
+    }
+  },
+
+  async getReservation(id: number): Promise<Reservation | null> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/${id}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération de la réservation')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async getReservationsByUser(userId: number): Promise<Reservation[]> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/user/${userId}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des réservations utilisateur')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return []
+    }
+  },
+
+  async getReservationsByTrip(trajetId: number): Promise<Reservation[]> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/trajet/${trajetId}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des réservations du trajet')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return []
+    }
+  },
+
+  async getReservationsByPlace(placeId: number): Promise<Reservation[]> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/place/${placeId}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des réservations de la place')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return []
+    }
+  },
+
+  async createReservation(reservation: Omit<Reservation, 'id'>): Promise<Reservation | null> {
+    try {
+      const response = await fetch(`${API_URL}/reservations`, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json'
+        },
+        body: JSON.stringify(reservation)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la création de la réservation')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async updateReservation(id: number, reservation: Partial<Reservation>): Promise<Reservation | null> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/${id}`, {
+        method: 'PUT',
+        headers: {
+          'Content-Type': 'application/json'
+        },
+        body: JSON.stringify(reservation)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la mise à jour de la réservation')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async deleteReservation(id: number): Promise<boolean> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/${id}`, {
+        method: 'DELETE'
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la suppression de la réservation')
+      }
+      return true
+    } catch (error) {
+      console.error('Erreur:', error)
+      return false
+    }
+  },
+
+  async cancelReservation(id: number): Promise<Reservation | null> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/${id}/cancel`, {
+        method: 'PATCH'
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de l\'annulation de la réservation')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async confirmReservation(id: number): Promise<Reservation | null> {
+    try {
+      const response = await fetch(`${API_URL}/reservations/${id}/confirm`, {
+        method: 'PATCH'
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la confirmation de la réservation')
+      }
+      return await response.json()
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  }
+}

+ 32 - 14
app_transport_front/src/services/StationCompanyService.ts

@@ -1,24 +1,48 @@
 import type { StationCompany } from '@/types'
+import { authenticatedFetch } from '@/services/authService'
 
 const API_URL = 'http://localhost:3001/api'
 
 export default {
-  async getStationCompanies(): Promise<StationCompany[]> {
+  async getStationCompanies(page: number = 1, limit: number = 10, search?: string, stationId?: number): Promise<{ stationCompanies: StationCompany[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
     try {
-      const response = await fetch(`${API_URL}/station-companies`)
+      let url = `${API_URL}/station-companies?page=${page}&limit=${limit}`
+      
+      if (search) {
+        url += `&search=${encodeURIComponent(search)}`
+      }
+      
+      if (stationId) {
+        url += `&stationId=${stationId}`
+      }
+      
+      const response = await authenticatedFetch(url)
       if (!response.ok) {
         throw new Error('Erreur lors de la récupération des relations station-entreprise')
       }
-      return await response.json()
+      const response_data = await response.json()
+      const apiData = response_data.data
+      return {
+        stationCompanies: apiData.data || [],
+        pagination: {
+          total: apiData.total || 0,
+          page: apiData.page || 1,
+          limit: apiData.limit || 10,
+          totalPages: apiData.totalPages || 1
+        }
+      }
     } catch (error) {
       console.error('Erreur:', error)
-      return []
+      return {
+        stationCompanies: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
     }
   },
 
   async getStationCompany(id: number): Promise<StationCompany | null> {
     try {
-      const response = await fetch(`${API_URL}/station-companies/${id}`)
+      const response = await authenticatedFetch(`${API_URL}/station-companies/${id}`)
       if (!response.ok) {
         throw new Error('Erreur lors de la récupération de la relation station-entreprise')
       }
@@ -31,11 +55,8 @@ export default {
 
   async createStationCompany(stationCompany: Omit<StationCompany, 'id'>): Promise<StationCompany | null> {
     try {
-      const response = await fetch(`${API_URL}/station-companies`, {
+      const response = await authenticatedFetch(`${API_URL}/station-companies`, {
         method: 'POST',
-        headers: {
-          'Content-Type': 'application/json'
-        },
         body: JSON.stringify(stationCompany)
       })
       if (!response.ok) {
@@ -50,11 +71,8 @@ export default {
 
   async updateStationCompany(id: number, stationCompany: Partial<StationCompany>): Promise<StationCompany | null> {
     try {
-      const response = await fetch(`${API_URL}/station-companies/${id}`, {
+      const response = await authenticatedFetch(`${API_URL}/station-companies/${id}`, {
         method: 'PUT',
-        headers: {
-          'Content-Type': 'application/json'
-        },
         body: JSON.stringify(stationCompany)
       })
       if (!response.ok) {
@@ -69,7 +87,7 @@ export default {
 
   async deleteStationCompany(id: number): Promise<boolean> {
     try {
-      const response = await fetch(`${API_URL}/station-companies/${id}`, {
+      const response = await authenticatedFetch(`${API_URL}/station-companies/${id}`, {
         method: 'DELETE'
       })
       return response.ok

+ 27 - 17
app_transport_front/src/services/StationService.ts

@@ -1,28 +1,42 @@
 import type { Station } from '@/types'
+import { authenticatedFetch } from '@/services/authService'
 
 const API_URL = 'http://localhost:3001/api'
 
 export default {
-  async getStations(): Promise<Station[]> {
+  async getStations(page: number = 1, limit: number = 100): Promise<{ stations: Station[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
     try {
-      const response = await fetch(`${API_URL}/stations`)
+      const response = await authenticatedFetch(`${API_URL}/stations?page=${page}&limit=${limit}`)
       if (!response.ok) {
         throw new Error('Erreur lors de la récupération des stations')
       }
-      return await response.json()
+      const result = await response.json()
+      return {
+        stations: result.data?.data || result.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 100,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
     } catch (error) {
       console.error('Erreur:', error)
-      return []
+      return {
+        stations: [],
+        pagination: { total: 0, page: 1, limit: 100, totalPages: 1 }
+      }
     }
   },
 
   async getStation(id: number): Promise<Station | null> {
     try {
-      const response = await fetch(`${API_URL}/stations/${id}`)
+      const response = await authenticatedFetch(`${API_URL}/stations/${id}`)
       if (!response.ok) {
         throw new Error('Erreur lors de la récupération de la station')
       }
-      return await response.json()
+      const result = await response.json()
+      return result.data || result
     } catch (error) {
       console.error('Erreur:', error)
       return null
@@ -31,17 +45,15 @@ export default {
 
   async createStation(station: Omit<Station, 'id'>): Promise<Station | null> {
     try {
-      const response = await fetch(`${API_URL}/stations`, {
+      const response = await authenticatedFetch(`${API_URL}/stations`, {
         method: 'POST',
-        headers: {
-          'Content-Type': 'application/json'
-        },
         body: JSON.stringify(station)
       })
       if (!response.ok) {
         throw new Error('Erreur lors de la création de la station')
       }
-      return await response.json()
+      const result = await response.json()
+      return result.data || result
     } catch (error) {
       console.error('Erreur:', error)
       return null
@@ -50,17 +62,15 @@ export default {
 
   async updateStation(id: number, station: Partial<Station>): Promise<Station | null> {
     try {
-      const response = await fetch(`${API_URL}/stations/${id}`, {
+      const response = await authenticatedFetch(`${API_URL}/stations/${id}`, {
         method: 'PUT',
-        headers: {
-          'Content-Type': 'application/json'
-        },
         body: JSON.stringify(station)
       })
       if (!response.ok) {
         throw new Error('Erreur lors de la mise à jour de la station')
       }
-      return await response.json()
+      const result = await response.json()
+      return result.data || result
     } catch (error) {
       console.error('Erreur:', error)
       return null
@@ -69,7 +79,7 @@ export default {
 
   async deleteStation(id: number): Promise<boolean> {
     try {
-      const response = await fetch(`${API_URL}/stations/${id}`, {
+      const response = await authenticatedFetch(`${API_URL}/stations/${id}`, {
         method: 'DELETE'
       })
       return response.ok

+ 235 - 0
app_transport_front/src/services/TripService.ts

@@ -0,0 +1,235 @@
+import type { Trip } from '@/types'
+import { authenticatedFetch } from '@/services/authService'
+
+const API_URL = 'http://localhost:3001/api'
+
+export default {
+  async getTrips(page: number = 1, limit: number = 10): Promise<{ trips: Trip[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets?page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des trajets')
+      }
+      const result = await response.json()
+      return {
+        trips: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        trips: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
+  },
+
+  async getTrip(id: number): Promise<Trip | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/${id}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération du trajet')
+      }
+      const result = await response.json()
+      return result.data || null
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async getTripById(id: number): Promise<{ data: Trip }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/${id}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération du trajet')
+      }
+      const result = await response.json()
+      return { data: result.data }
+    } catch (error) {
+      console.error('Erreur:', error)
+      throw error
+    }
+  },
+
+  async createTrip(trip: Omit<Trip, 'id' | 'createdAt' | 'updatedAt'>): Promise<Trip | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets`, {
+        method: 'POST',
+        body: JSON.stringify(trip)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la création du trajet')
+      }
+      const result = await response.json()
+      return result.data || null
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async updateTrip(id: number, trip: Partial<Trip>): Promise<Trip | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/${id}`, {
+        method: 'PUT',
+        body: JSON.stringify(trip)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la mise à jour du trajet')
+      }
+      const result = await response.json()
+      return result.data || null
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async partialUpdateTrip(id: number, trip: Partial<Trip>): Promise<Trip | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/${id}`, {
+        method: 'PATCH',
+        body: JSON.stringify(trip)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la mise à jour partielle du trajet')
+      }
+      const result = await response.json()
+      return result.data || null
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async deleteTrip(id: number): Promise<boolean> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/${id}`, {
+        method: 'DELETE'
+      })
+      return response.ok
+    } catch (error) {
+      console.error('Erreur:', error)
+      return false
+    }
+  },
+
+  async getTripsByCompany(companyId: number, page: number = 1, limit: number = 10): Promise<{ trips: Trip[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/company/${companyId}?page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des trajets de l\'entreprise')
+      }
+      const result = await response.json()
+      return {
+        trips: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        trips: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
+  },
+
+  async getTripsByDriver(driverId: number, page: number = 1, limit: number = 10): Promise<{ trips: Trip[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/driver/${driverId}?page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des trajets du chauffeur')
+      }
+      const result = await response.json()
+      return {
+        trips: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        trips: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
+  },
+
+  async getTripsByStatus(status: string, page: number = 1, limit: number = 10): Promise<{ trips: Trip[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/status/${status}?page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des trajets par statut')
+      }
+      const result = await response.json()
+      return {
+        trips: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        trips: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
+  },
+
+  async updateTripStatus(tripId: number, status: 'upcoming' | 'ongoing' | 'completed' | 'cancelled'): Promise<boolean> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/${tripId}/status`, {
+        method: 'PATCH',
+        body: JSON.stringify({ status })
+      })
+      return response.ok
+    } catch (error) {
+      console.error('Erreur:', error)
+      return false
+    }
+  },
+
+  async searchTrips(query: string, page: number = 1, limit: number = 10): Promise<{ trips: Trip[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/trajets/search?q=${encodeURIComponent(query)}&page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la recherche de trajets')
+      }
+      const result = await response.json()
+      return {
+        trips: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        trips: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
+  }
+}

+ 19 - 0
app_transport_front/src/services/UserService.ts

@@ -1,5 +1,6 @@
 import { User } from '@/types/index'
 import { authenticatedFetch } from '@/services/authService'
+import { Role } from '@/Enum/Role'
 
 const API_URL = 'http://localhost:3001/api'
 
@@ -93,6 +94,24 @@ export default {
     }
   },
 
+  async updatedByPatchUser(id: number, user: Partial<User>): Promise<User | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/users/${id}`, {
+        method: 'PATCH',
+        body: JSON.stringify(user)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la mise à jour de l\'utilisateur')
+      }
+      const result = await response.json()
+      // Extraire l'utilisateur de la structure de réponse du backend
+      return result.data?.user || result.user || result
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
   async deleteUser(id: number): Promise<boolean> {
     try {
       const response = await authenticatedFetch(`${API_URL}/users/${id}`, {

+ 172 - 0
app_transport_front/src/services/VehiculeService.ts

@@ -0,0 +1,172 @@
+import type { Vehicle } from '@/types'
+import { authenticatedFetch } from '@/services/authService'
+
+const API_URL = 'http://localhost:3001/api'
+
+export default {
+  async getVehicles(page: number = 1, limit: number = 10): Promise<{ vehicles: Vehicle[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles?page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des véhicules')
+      }
+      const result = await response.json()
+      return {
+        vehicles: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        vehicles: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
+  },
+
+  async getVehicle(id: number): Promise<Vehicle | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles/${id}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération du véhicule')
+      }
+      const result = await response.json()
+      return result.data || null
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async createVehicle(vehicle: Omit<Vehicle, 'id'>): Promise<Vehicle | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles`, {
+        method: 'POST',
+        body: JSON.stringify(vehicle)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la création du véhicule')
+      }
+      const result = await response.json()
+      return result.data || null
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async updateVehicle(id: number, vehicle: Partial<Vehicle>): Promise<Vehicle | null> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles/${id}`, {
+        method: 'PUT',
+        body: JSON.stringify(vehicle)
+      })
+      if (!response.ok) {
+        throw new Error('Erreur lors de la mise à jour du véhicule')
+      }
+      const result = await response.json()
+      return result.data || null
+    } catch (error) {
+      console.error('Erreur:', error)
+      return null
+    }
+  },
+
+  async deleteVehicle(id: number): Promise<boolean> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles/${id}`, {
+        method: 'DELETE'
+      })
+      return response.ok
+    } catch (error) {
+      console.error('Erreur:', error)
+      return false
+    }
+  },
+
+  async getVehiclesByCompany(companyId: number, page: number = 1, limit: number = 10): Promise<{ vehicles: Vehicle[], pagination: { total: number, page: number, limit: number, totalPages: number } }> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/Vehicules/company/${companyId}?page=${page}&limit=${limit}`)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des véhicules de l\'entreprise')
+      }
+      const result = await response.json()
+      return {
+        vehicles: result.data?.data || [],
+        pagination: {
+          total: result.data?.total || 0,
+          page: result.data?.page || 1,
+          limit: result.data?.limit || 10,
+          totalPages: result.data?.totalPages || 1
+        }
+      }
+    } catch (error) {
+      console.error('Erreur:', error)
+      return {
+        vehicles: [],
+        pagination: { total: 0, page: 1, limit: 10, totalPages: 1 }
+      }
+    }
+  },
+
+  async getAvailableVehicles(companyId?: number): Promise<Vehicle[]> {
+    try {
+      const url = companyId 
+        ? `${API_URL}/vehicles/available?companyId=${companyId}`
+        : `${API_URL}/vehicles/available`
+      
+      const response = await authenticatedFetch(url)
+      if (!response.ok) {
+        throw new Error('Erreur lors de la récupération des véhicules disponibles')
+      }
+      const result = await response.json()
+      return result.data || []
+    } catch (error) {
+      console.error('Erreur:', error)
+      return []
+    }
+  },
+
+  async assignDriver(vehicleId: number, driverId: number): Promise<boolean> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles/${vehicleId}/assign-driver`, {
+        method: 'PUT',
+        body: JSON.stringify({ driverId })
+      })
+      return response.ok
+    } catch (error) {
+      console.error('Erreur:', error)
+      return false
+    }
+  },
+
+  async unassignDriver(vehicleId: number): Promise<boolean> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles/${vehicleId}/unassign-driver`, {
+        method: 'PUT'
+      })
+      return response.ok
+    } catch (error) {
+      console.error('Erreur:', error)
+      return false
+    }
+  },
+
+  async updateVehicleStatus(vehicleId: number, status: string): Promise<boolean> {
+    try {
+      const response = await authenticatedFetch(`${API_URL}/vehicles/${vehicleId}/status`, {
+        method: 'PUT',
+        body: JSON.stringify({ status })
+      })
+      return response.ok
+    } catch (error) {
+      console.error('Erreur:', error)
+      return false
+    }
+  }
+}

+ 11 - 3
app_transport_front/src/stores/useStationStore.ts

@@ -7,15 +7,23 @@ export const useStationStore = defineStore('station', {
     stations: [] as Station[],
     currentStation: null as Station | null,
     loading: false,
-    error: null as string | null
+    error: null as string | null,
+    pagination: {
+      total: 0,
+      page: 1,
+      limit: 100,
+      totalPages: 1
+    }
   }),
 
   actions: {
-    async fetchStations() {
+    async fetchStations(page: number = 1, limit: number = 100) {
       this.loading = true
       this.error = null
       try {
-        this.stations = await StationService.getStations()
+        const result = await StationService.getStations(page, limit)
+        this.stations = result.stations
+        this.pagination = result.pagination
       } catch (error) {
         this.error = 'Erreur lors du chargement des stations'
         console.error(error)

+ 46 - 1
app_transport_front/src/stores/useUserStore.ts

@@ -1,6 +1,16 @@
 import { defineStore } from 'pinia'
 import type { PersistenceOptions } from 'pinia-plugin-persistedstate'
 
+// Interface pour le payload du JWT
+interface JWTPayload {
+  sub: string
+  email: string
+  role: string
+  companyId?: string
+  iat: number
+  exp: number
+}
+
 export const useUserStore = defineStore('user', {
   state: () => ({
     token: null as string | null,
@@ -11,7 +21,35 @@ export const useUserStore = defineStore('user', {
     telephone: null as string | null,
     name: null as string | null,
     otherName: null as string | null,
+    companyId: null as string | null,
   }),
+  getters: {
+    // Getter pour décoder le token JWT et extraire le companyId
+    decodedCompanyId: (state) => {
+      if (!state.token) return null
+      
+      try {
+        // Décoder le JWT (partie payload)
+        const payload = JSON.parse(atob(state.token.split('.')[1])) as JWTPayload
+        return payload.companyId || null
+      } catch (error) {
+        console.error('Erreur lors du décodage du token JWT:', error)
+        return null
+      }
+    },
+    // Getter pour obtenir toutes les informations du token décodé
+    decodedToken: (state) => {
+      if (!state.token) return null
+      
+      try {
+        const payload = JSON.parse(atob(state.token.split('.')[1])) as JWTPayload
+        return payload
+      } catch (error) {
+        console.error('Erreur lors du décodage du token JWT:', error)
+        return null
+      }
+    }
+  },
   actions: {
     setUser(data: { 
       token: string, 
@@ -21,7 +59,8 @@ export const useUserStore = defineStore('user', {
       email: string, 
       telephone: string, 
       name: string, 
-      otherName: string 
+      otherName: string,
+      companyId?: string 
     }) {
       this.token = data.token
       this.userId = data.userId
@@ -31,6 +70,12 @@ export const useUserStore = defineStore('user', {
       this.telephone = data.telephone
       this.name = data.name
       this.otherName = data.otherName
+      this.companyId = data.companyId || null
+      
+      // Mettre à jour le companyId depuis le token décodé si pas fourni
+      if (!this.companyId && this.decodedCompanyId) {
+        this.companyId = this.decodedCompanyId
+      }
     },
     clearUser() {
       this.$reset()

+ 63 - 6
app_transport_front/src/types/index.ts

@@ -1,3 +1,4 @@
+// Types pour l'application de transport
 export interface Company {
   id: number
   name: string
@@ -67,6 +68,35 @@ export interface Travel {
   active: boolean
 }
 
+export interface Trip {
+  id: number
+  departAt: string
+  arriveAt: string
+  departStationId: number
+  arriveStationId: number
+  vehiculeId: number
+  vehicule?: Vehicle
+  departStation?: Station
+  arriveStation?: Station
+  reservations?: any[]
+  // Propriétés pour le formulaire frontend uniquement
+  departureCity?: number
+  arrivalCity?: number
+  departureDateTime?: string
+  estimatedArrivalDateTime?: string
+  actualArrivalDateTime?: string
+  pricePerSeat?: number
+  totalSeats?: number
+  availableSeats?: number
+  driverId?: number
+  vehicleId?: number
+  status?: 'upcoming' | 'ongoing' | 'completed' | 'cancelled'
+  notes?: string
+  companyId?: number
+  createdAt?: string
+  updatedAt?: string
+}
+
 export interface Station {
   id: number
   name: string
@@ -77,16 +107,43 @@ export interface Station {
   updatedAt?: string
 }
 
-export interface Transaction {
+export interface Reservation {
   id: number
-  amount: number
-  type: string
-  status: string
+  dateAt: string
+  placeId: number
   userId: number
-  reservationId?: number
-  description?: string
+  trajetId: number
+  qrcode?: Qrcode
+  place?: Place
+  trajet?: Trip
+  user?: User
+  transaction?: Transaction
+}
+
+export interface Place {
+  id: number
+  number: number
+  vehiculeId: number
+  isAvailable: boolean
+  occupiedAt?: string
+  vehicule?: Vehicle
+  reservations?: Reservation[]
+}
+
+export interface Transaction {
+  id: string
+  amount?: number
+  currency?: string
+  status?: string
+  provider?: string
+  reference?: string
+  metadata?: any
   createdAt: string
   updatedAt?: string
+  userId: number
+  reservationId: number
+  reservation?: Reservation
+  user?: User
 }
 
 export interface Qrcode {

+ 45 - 32
app_transport_front/src/views/CompanyView.vue

@@ -110,37 +110,54 @@
 
   <!-- Déplacer toutes les modals ici, en dehors du conteneur flex -->
   <!-- Formulaire d'ajout/modification -->
-  <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" v-if="showAddForm || editingCompany">
-    <div class="bg-white rounded-lg w-11/12 max-w-2xl max-h-screen overflow-y-auto shadow-lg">
+  <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" v-if="showAddForm || editingCompany">
+    <div class="bg-white rounded-lg w-11/12 max-w-6xl max-h-screen overflow-y-auto shadow-lg">
       <div class="flex justify-between items-center py-4 px-5 border-b border-gray-200">
         <h2 class="text-xl text-gray-800 m-0">{{ editingCompany ? 'Modifier l\'entreprise' : 'Ajouter une entreprise' }}</h2>
         <button class="bg-transparent border-0 text-xl text-gray-400 cursor-pointer" @click="closeForm">
           <i class="fas fa-times"></i>
         </button>
       </div>
-      <div class="p-5">
-        <div class="mb-4">
-          <label class="block mb-2 font-medium text-gray-800">Nom de l'entreprise <span class="text-blue-600">*</span></label>
-          <input type="text" v-model="formData.name" required class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm">
-        </div>
+      <div class="flex">
+        <!-- Formulaire principal -->
+        <div class="flex-1 p-5">
+          <div class="mb-4">
+            <label class="block mb-2 font-medium text-gray-800">Nom de l'entreprise <span class="text-blue-600">*</span></label>
+            <input type="text" v-model="formData.name" required class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm">
+          </div>
 
-        <div class="mb-4">
-          <label class="block mb-2 font-medium text-gray-800">Adresse <span class="text-blue-600">*</span></label>
-          <input type="text" v-model="formData.address" required class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm">
-        </div>
+          <div class="mb-4">
+            <label class="block mb-2 font-medium text-gray-800">Adresse <span class="text-blue-600">*</span></label>
+            <input type="text" v-model="formData.address" required class="w-full py-2.5 px-2.5 border border-gray-300 rounded text-sm">
+          </div>
 
-        <div class="mb-4">
-          <label class="block mb-2 font-medium text-gray-800">Gestionnaire <span class="text-blue-600">*</span></label>
-          <div class="flex gap-2">
-            <select v-model="formData.managerId" required class="flex-1 py-2.5 px-2.5 border border-gray-300 rounded text-sm">
-              <option value="">Sélectionner un gestionnaire</option>
-              <option v-for="manager in managers" :key="manager.id" :value="manager.id">
-                {{ manager.name }} {{ manager.otherName }} ({{ manager.email }})
-              </option>
-            </select>
-            <button type="button" @click="openCreateManagerModal" class="bg-blue-600 text-white px-3 py-2.5 rounded text-sm hover:bg-blue-700">
-              <i class="fas fa-plus"></i> Nouveau
-            </button>
+          <div class="mb-4">
+            <label class="block mb-2 font-medium text-gray-800">Gestionnaire <span class="text-blue-600">*</span></label>
+            <div class="flex gap-2">
+              <select v-model="formData.managerId" required class="flex-1 py-2.5 px-2.5 border border-gray-300 rounded text-sm">
+                <option value="">Sélectionner un gestionnaire</option>
+                <option v-for="manager in managers" :key="manager.id" :value="manager.id">
+                  {{ manager.name }} {{ manager.otherName }} ({{ manager.email }})
+                </option>
+              </select>
+              <button type="button" @click="openCreateManagerModal" class="bg-blue-600 text-white px-3 py-2.5 rounded text-sm hover:bg-blue-700">
+                <i class="fas fa-plus"></i> Nouveau
+              </button>
+            </div>
+          </div>
+        </div>
+        
+        <!-- Modal Manager à droite -->
+        <div class="w-1/2 border-l border-gray-200" v-if="showCreateManager">
+          <div class="p-5">
+            <div class="flex justify-between items-center mb-4">
+            </div>
+            <ManagerModal
+              :show="showCreateManager" 
+              @close="showCreateManager = false"
+              @manager-created="onManagerCreated"
+              :inline="true"
+            />
           </div>
         </div>
       </div>
@@ -244,12 +261,7 @@
       </div>
     </div>
 
-    <!-- Composant Modal pour créer un nouveau manager -->
-    <ManagerModal 
-      :show="showCreateManager" 
-      @close="showCreateManager = false"
-      @manager-created="onManagerCreated"
-    />
+
   </div>
 </template>
 
@@ -289,7 +301,7 @@ const companyStats = ref({
 const formData = ref({
   name: '',
   address: '',
-  managerId: '', // Aucun gestionnaire sélectionné par défaut
+  managerId: 0, // Aucun gestionnaire sélectionné par défaut
   vehiculeIds: [] as number[],
   driverIds: [] as number[]
 })
@@ -302,7 +314,7 @@ const isFormValid = computed(() => {
   return formData.value.name && 
          formData.value.address && 
          formData.value.managerId && 
-         formData.value.managerId !== ''
+         formData.value.managerId !== 0
 })
 
 const filteredCompanies = computed(() => {
@@ -378,6 +390,7 @@ const handlePageChange = async (page: number) => {
 const loadManagers = async () => {
   try {
     const result = await UserService.getManagers()
+    console.log('Managers chargés:', result)
     managers.value = result
   } catch (error) {
     console.error('Erreur lors du chargement des managers:', error)
@@ -434,7 +447,7 @@ const resetForm = () => {
   formData.value = {
     name: '',
     address: '',
-    managerId: '',
+    managerId: 0,
     vehiculeIds: [],
     driverIds: []
   }

+ 363 - 221
app_transport_front/src/views/EmployeeView.vue

@@ -39,8 +39,8 @@
           <div class="filter-group">
             <select v-model="roleFilter">
               <option value="">Tous les rôles</option>
-              <option v-for="role in roles" :key="role.id" :value="role.id">
-                {{ role.name }}
+              <option v-for="(label, role) in RoleLabel" :key="role" :value="role">
+                {{ label }}
               </option>
             </select>
           </div>
@@ -53,8 +53,14 @@
           </div>
         </div>
 
+        <!-- Loading state -->
+        <div v-if="loading" class="loading-state">
+          <i class="fas fa-spinner fa-spin"></i>
+          <p>Chargement des employés...</p>
+        </div>
+
         <!-- Tableau des employés -->
-        <div class="employee-table-container">
+        <div class="employee-table-container" v-else>
           <table class="employee-table" v-if="filteredEmployees.length > 0">
             <thead>
               <tr>
@@ -72,13 +78,13 @@
                 <td class="photo-cell">
                   <div class="employee-photo" :style="{ backgroundImage: `url(${employee.photo || '/assets/images/default-avatar.png'})` }"></div>
                 </td>
-                <td>{{ employee.firstName }} {{ employee.lastName }}</td>
+                <td>{{ getEmployeeFullName(employee) }}</td>
                 <td>{{ employee.email }}</td>
-                <td>{{ employee.phone }}</td>
-                <td>{{ getEmployeeRoleName(employee) }}</td>
+                <td>{{ employee.telephone || employee.phone || 'N/A' }}</td>
+                <td>{{ getRoleLabel(employee.role) }}</td>
                 <td>
-                  <span :class="['status-badge', employee.active ? 'active' : 'inactive']">
-                    {{ employee.active ? 'Actif' : 'Inactif' }}
+                  <span :class="['status-badge', employee.active !== false ? 'active' : 'inactive']">
+                    {{ employee.active !== false ? 'Actif' : 'Inactif' }}
                   </span>
                 </td>
                 <td class="actions">
@@ -98,38 +104,58 @@
           <div class="empty-state" v-else>
             <i class="fas fa-user empty-icon"></i>
             <p>Aucun employé trouvé</p>
-            <button class="add-button" @click="showAddForm = true">
+            <button class="add-button" @click="openEmployeeModal">
               Ajouter un employé
             </button>
           </div>
         </div>
 
+        <!-- Pagination -->
+        <div class="pagination" v-if="pagination.totalPages > 1">
+          <button 
+            @click="loadEmployees(pagination.page - 1)" 
+            :disabled="pagination.page <= 1"
+            class="pagination-btn"
+          >
+            <i class="fas fa-chevron-left"></i>
+          </button>
+          <span class="pagination-info">
+            Page {{ pagination.page }} sur {{ pagination.totalPages }}
+          </span>
+          <button 
+            @click="loadEmployees(pagination.page + 1)" 
+            :disabled="pagination.page >= pagination.totalPages"
+            class="pagination-btn"
+          >
+            <i class="fas fa-chevron-right"></i>
+          </button>
+        </div>
+
         <!-- Composant EmployeeModal -->
-        <EmployeeModal 
-          :show="showEmployeeModal" 
-          :editing-employee="editingEmployee" 
-          :roles="roles"
-          @close="closeEmployeeModal" 
-          @employee-saved="onEmployeeSaved" 
+        <EmployeeModal
+          :show="showEmployeeModal"
+          :editing-employee="editingEmployee"
+          @close="closeEmployeeModal"
+          @employee-saved="onEmployeeSaved"
         />
 
         <!-- Modal de détails de l'employé -->
-    <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" v-if="viewingEmployee">
-      <div class="bg-white rounded-lg w-11/12 max-w-2xl max-h-screen overflow-y-auto shadow-lg">
-        <div class="flex justify-between items-center py-4 px-5 border-b border-gray-200">
-          <h2 class="text-xl text-gray-800 m-0">Détails de l'employé</h2>
-          <button class="bg-transparent border-0 text-xl text-gray-400 cursor-pointer" @click="viewingEmployee = null">
-            <i class="fas fa-times"></i>
-          </button>
-        </div>
+        <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" v-if="viewingEmployee">
+          <div class="bg-white rounded-lg w-11/12 max-w-2xl max-h-screen overflow-y-auto shadow-lg">
+            <div class="flex justify-between items-center py-4 px-5 border-b border-gray-200">
+              <h2 class="text-xl text-gray-800 m-0">Détails de l'employé</h2>
+              <button class="bg-transparent border-0 text-xl text-gray-400 cursor-pointer" @click="viewingEmployee = null">
+                <i class="fas fa-times"></i>
+              </button>
+            </div>
             <div class="p-5 employee-details">
               <div class="employee-profile">
                 <div class="employee-profile-photo" :style="{ backgroundImage: `url(${viewingEmployee.photo || '/assets/images/default-avatar.png'})` }"></div>
                 <div class="employee-profile-info">
-                  <h3>{{ viewingEmployee.firstName }} {{ viewingEmployee.lastName }}</h3>
+                  <h3>{{ getEmployeeFullName(viewingEmployee) }}</h3>
                   <p class="email">{{ viewingEmployee.email }}</p>
-                  <span :class="['status-badge', viewingEmployee.active ? 'active' : 'inactive']">
-                    {{ viewingEmployee.active ? 'Actif' : 'Inactif' }}
+                  <span :class="['status-badge', viewingEmployee.active !== false ? 'active' : 'inactive']">
+                    {{ viewingEmployee.active !== false ? 'Actif' : 'Inactif' }}
                   </span>
                 </div>
               </div>
@@ -138,7 +164,7 @@
                 <h4>Informations de contact</h4>
                 <div class="info-item">
                   <i class="fas fa-phone"></i>
-                  <span>{{ viewingEmployee.phone }}</span>
+                  <span>{{ viewingEmployee.telephone || viewingEmployee.phone || 'N/A' }}</span>
                 </div>
                 <div class="info-item" v-if="viewingEmployee.address">
                   <i class="fas fa-map-marker-alt"></i>
@@ -146,7 +172,7 @@
                 </div>
                 <div class="info-item">
                   <i class="fas fa-user-tag"></i>
-                  <span>{{ getEmployeeRoleName(viewingEmployee) }}</span>
+                  <span>{{ getRoleLabel(viewingEmployee.role) }}</span>
                 </div>
               </div>
 
@@ -172,29 +198,32 @@
         </div> 
 
         <!-- Modal de confirmation de suppression -->
-    <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" v-if="showDeleteConfirm">
-      <div class="bg-white rounded-lg w-11/12 max-w-md max-h-screen overflow-y-auto shadow-lg">
-        <div class="flex justify-between items-center py-4 px-5 border-b border-gray-200">
-          <h2 class="text-xl text-gray-800 m-0">Confirmer la suppression</h2>
-          <button class="bg-transparent border-0 text-xl text-gray-400 cursor-pointer" @click="showDeleteConfirm = false">
-            <i class="fas fa-times"></i>
-          </button>
-        </div>
+        <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" v-if="showDeleteConfirm">
+          <div class="bg-white rounded-lg w-11/12 max-w-md max-h-screen overflow-y-auto shadow-lg">
+            <div class="flex justify-between items-center py-4 px-5 border-b border-gray-200">
+              <h2 class="text-xl text-gray-800 m-0">Confirmer la suppression</h2>
+              <button class="bg-transparent border-0 text-xl text-gray-400 cursor-pointer" @click="showDeleteConfirm = false">
+                <i class="fas fa-times"></i>
+              </button>
+            </div>
             <div class="p-5">
               <i class="fas fa-exclamation-triangle warning-icon"></i>
-              <p>Êtes-vous sûr de vouloir supprimer l'employé <strong>{{ employeeToDelete?.firstName }} {{ employeeToDelete?.lastName }}</strong> ?</p>
+              <p>Êtes-vous sûr de vouloir supprimer l'employé <strong>{{ getEmployeeFullName(employeeToDelete) }}</strong> ?</p>
               <p class="warning">Cette action est irréversible.</p>
             </div>
             <div class="py-4 px-5 border-t border-gray-200 flex justify-end gap-2.5">
               <button class="bg-gray-100 text-gray-600 border-0 rounded py-2.5 px-4 cursor-pointer" @click="showDeleteConfirm = false">Annuler</button>
-              <button class="bg-red-500 text-white border-0 rounded py-2.5 px-4 cursor-pointer hover:bg-red-600" @click="deleteEmployee">Supprimer</button>
+              <button 
+                class="bg-red-500 text-white border-0 rounded py-2.5 px-4 cursor-pointer hover:bg-red-600 disabled:bg-gray-400" 
+                @click="deleteEmployee"
+                :disabled="deleting"
+              >
+                <i v-if="deleting" class="fas fa-spinner fa-spin mr-2"></i>
+                {{ deleting ? 'Suppression...' : 'Supprimer' }}
+              </button>
             </div>
           </div>
         </div>
-        <!-- <ManagerModal 
-          :show="true" 
-          @close="false"
-          @manager-created="test" /> -->
       </div>
     </div>
   </div>
@@ -205,40 +234,26 @@ import { ref, computed, onMounted } from 'vue'
 import Sidebar from '@/components/Sidebar.vue'
 import TopBar from '@/components/TopBar.vue'
 import { useUserStore } from '@/stores/useUserStore'
-import ManagerModal from '@/components/ManagerModal.vue'
 import EmployeeModal from '@/components/EmployeeModal.vue'
 import { useToast } from '@/composables/useToast'
+import UserService from '@/services/UserService'
+import CompanyService from '@/services/CompanyService'
 import type { User } from '@/types/index'
 import { Role, RoleLabel } from '@/Enum/Role'
-// const test = () => {
-//   console.log('test')
-// }
-interface RoleOption {
-  id: number
-  name: string
-}
-
 
 interface Employee extends User {
-  firstName: string
-  lastName: string
-  phone: string
+  firstName?: string
+  lastName?: string
+  phone?: string
   password?: string
-  roleLabel?: string // Label lisible du rôle pour l'affichage
-}
-
-// Fonction utilitaire pour obtenir le label d'un rôle
-const getRoleLabel = (role: Role): string => {
-  const roleKey = Object.keys(Role).find(key => Role[key as keyof typeof Role] === role) as keyof typeof RoleLabel
-  return roleKey ? RoleLabel[roleKey] : role
+  roleLabel?: string
 }
 
+// État de l'application
 const employees = ref<Employee[]>([])
-const roles = ref<RoleOption[]>([])
+const loading = ref(false)
+const deleting = ref(false)
 const showEmployeeModal = ref(false)
-
-// Toast notifications
-const { success, error } = useToast()
 const editingEmployee = ref<Employee | null>(null)
 const viewingEmployee = ref<Employee | null>(null)
 const showDeleteConfirm = ref(false)
@@ -247,17 +262,35 @@ const searchQuery = ref('')
 const roleFilter = ref('')
 const statusFilter = ref('')
 
-// Variables supprimées car gérées par EmployeeModal
+// Pagination
+const pagination = ref({
+  total: 0,
+  page: 1,
+  limit: 10,
+  totalPages: 1
+})
+
+// Toast notifications
+const { success, error } = useToast()
+
+// Rôles disponibles pour le filtre et le modal
+const availableRoles = computed(() => {
+  return Object.entries(RoleLabel).map(([key, label]) => ({
+    id: key,
+    name: label
+  }))
+})
 
+// Employés filtrés
 const filteredEmployees = computed(() => {
   return employees.value.filter(employee => {
     // Filtre de recherche
     const searchLower = searchQuery.value.toLowerCase()
-    const nameMatch = `${employee.firstName} ${employee.lastName}`.toLowerCase().includes(searchLower)
+    const fullName = getEmployeeFullName(employee).toLowerCase()
     const emailMatch = employee.email.toLowerCase().includes(searchLower)
-    const phoneMatch = employee.phone.toLowerCase().includes(searchLower)
+    const phoneMatch = (employee.telephone || employee.phone || '').toLowerCase().includes(searchLower)
     
-    if (searchQuery.value && !nameMatch && !emailMatch && !phoneMatch) {
+    if (searchQuery.value && !fullName.includes(searchLower) && !emailMatch && !phoneMatch) {
       return false
     }
     
@@ -267,10 +300,10 @@ const filteredEmployees = computed(() => {
     }
     
     // Filtre par statut
-    if (statusFilter.value === 'active' && !employee.active) {
+    if (statusFilter.value === 'active' && employee.active === false) {
       return false
     }
-    if (statusFilter.value === 'inactive' && employee.active) {
+    if (statusFilter.value === 'inactive' && employee.active !== false) {
       return false
     }
     
@@ -278,12 +311,19 @@ const filteredEmployees = computed(() => {
   })
 })
 
-// Obtenir le nom du rôle à partir de l'Employee
-const getEmployeeRoleName = (employee: Employee): string => {
-  return employee.roleLabel || getRoleLabel(employee.role)
+// Fonctions utilitaires
+const getRoleLabel = (role: Role | string): string => {
+  const roleKey = Object.keys(Role).find(key => Role[key as keyof typeof Role] === role) as keyof typeof RoleLabel
+  return roleKey ? RoleLabel[roleKey] : role
+}
+
+const getEmployeeFullName = (employee: Employee | null): string => {
+  if (!employee) return ''
+  const firstName = employee.otherName || employee.firstName || ''
+  const lastName = employee.name || employee.lastName || ''
+  return `${firstName} ${lastName}`.trim() || employee.email
 }
 
-// Formater une date
 const formatDate = (dateString?: string): string => {
   if (!dateString) return 'N/A'
   const date = new Date(dateString)
@@ -294,114 +334,146 @@ const formatDate = (dateString?: string): string => {
   }).format(date)
 }
 
-// Afficher les détails d'un employé
+// Gestion des employés
+const loadEmployees = async (page: number = 1) => {
+  try {
+    loading.value = true
+    const userStore = useUserStore()
+    
+    // Récupérer le companyId depuis le token JWT décodé
+    const companyId = userStore.decodedCompanyId || userStore.companyId
+    
+    // Vérifier si l'utilisateur a une entreprise associée
+    if (!companyId) {
+      error('Aucune entreprise associée à votre compte')
+      return
+    }
+    
+    const result = await CompanyService.getCompanyEmployees(parseInt(companyId), page, pagination.value.limit)
+    
+    employees.value = result.employees.map(user => ({
+      ...user,
+      firstName: user.otherName,
+      lastName: user.name,
+      phone: user.telephone
+    }))
+    
+    pagination.value = result.pagination
+  } catch (err) {
+    console.error('Erreur lors du chargement des employés:', err)
+    error('Erreur lors du chargement des employés')
+  } finally {
+    loading.value = false
+  }
+}
+
 const viewEmployee = (employee: Employee) => {
   viewingEmployee.value = employee
   editingEmployee.value = null
   showEmployeeModal.value = false
 }
 
-// Ouvrir le modal pour ajouter un employé
 const openEmployeeModal = () => {
   editingEmployee.value = null
   showEmployeeModal.value = true
 }
 
-// Modifier un employé
 const editEmployee = (employee: Employee) => {
   editingEmployee.value = employee
   viewingEmployee.value = null
   showEmployeeModal.value = true
 }
 
-// Confirmer la suppression d'un employé
 const confirmDelete = (employee: Employee) => {
   employeeToDelete.value = employee
   showDeleteConfirm.value = true
 }
 
-// Fermer le modal employé
 const closeEmployeeModal = () => {
   showEmployeeModal.value = false
   editingEmployee.value = null
 }
 
-// Gérer la sauvegarde d'un employé depuis le modal
-const onEmployeeSaved = (savedUser: any) => {
-  if (editingEmployee.value) {
-    // Mise à jour d'un employé existant
-    const index = employees.value.findIndex(e => e.id === editingEmployee.value?.id)
-    if (index !== -1) {
-      // Convertir les données User en format Employee
-      const updatedEmployee: Employee = {
-        ...employees.value[index],
-        id: savedUser.id,
-        name: savedUser.name,
-        otherName: savedUser.otherName,
-        firstName: savedUser.otherName || savedUser.firstName,
-        lastName: savedUser.name || savedUser.lastName,
-        email: savedUser.email,
-        phone: savedUser.telephone || savedUser.phone,
-        telephone: savedUser.telephone,
-        address: savedUser.address,
-        role: savedUser.role as Role,
-        roleLabel: getRoleLabel(savedUser.role as Role),
-        updatedAt: new Date().toISOString()
+const onEmployeeSaved = async (savedUser: any) => {
+  try {
+    if (editingEmployee.value) {
+      // Mise à jour d'un employé existant
+      const index = employees.value.findIndex(e => e.id === editingEmployee.value?.id)
+      if (index !== -1) {
+        // Mettre à jour l'employé dans la liste avec les nouvelles données
+        employees.value[index] = {
+          ...employees.value[index],
+          ...savedUser,
+          firstName: savedUser.otherName || savedUser.firstName,
+          lastName: savedUser.name || savedUser.lastName,
+          phone: savedUser.telephone || savedUser.phone,
+          roleLabel: RoleLabel[savedUser.role as keyof typeof RoleLabel] || savedUser.roleLabel
+        }
+      }
+    } else {
+      // Ajout d'un nouvel employé
+      const newEmployee: Employee = {
+        ...savedUser,
+        firstName: savedUser.otherName,
+        lastName: savedUser.name,
+        phone: savedUser.telephone,
+        roleLabel: RoleLabel[savedUser.role as keyof typeof RoleLabel]
+      }
+      
+      // Ajouter le nouvel employé au début de la liste
+      employees.value.unshift(newEmployee)
+      
+      // Mettre à jour la pagination
+      pagination.value.total += 1
+      pagination.value.totalPages = Math.ceil(pagination.value.total / pagination.value.limit)
+      
+      // Si on dépasse la limite par page, supprimer le dernier élément
+      if (employees.value.length > pagination.value.limit) {
+        employees.value.pop()
       }
-      employees.value[index] = updatedEmployee
-    }
-  } else {
-    // Ajout d'un nouvel employé
-    const newEmployee: Employee = {
-      id: savedUser.id,
-      name: savedUser.name || '',
-      otherName: savedUser.otherName || '',
-      firstName: savedUser.otherName || '',
-      lastName: savedUser.name || '',
-      email: savedUser.email,
-      phone: savedUser.telephone || '',
-      telephone: savedUser.telephone || '',
-      address: savedUser.address || '',
-      role: savedUser.role as Role,
-      roleLabel: getRoleLabel(savedUser.role as Role),
-      password: '', // Ne pas stocker le mot de passe
-      createdAt: savedUser.createdAt || new Date().toISOString(),
-      updatedAt: savedUser.updatedAt || new Date().toISOString()
     }
-    employees.value.push(newEmployee)
+    
+    // Fermer le modal
+    closeEmployeeModal()
+    
+  } catch (err) {
+    console.error('Erreur lors de la sauvegarde:', err)
+    error('Erreur lors de la mise à jour de la liste des employés')
   }
-  
-  // Réinitialiser l'état d'édition
-  editingEmployee.value = null
 }
 
-// Supprimer un employé
 const deleteEmployee = async () => {
   if (!employeeToDelete.value) return
   
   try {
-    // Dans un environnement réel, vous feriez un appel API ici
-    // await fetch(`http://localhost:3001/api/employees/${employeeToDelete.value.id}`, {
-    //   method: 'DELETE'
-    // })
-    
-    // Supprimer l'employé de la liste locale
-    employees.value = employees.value.filter(e => e.id !== employeeToDelete.value?.id)
-    
-    // Fermer la modal de confirmation
-    showDeleteConfirm.value = false
-    employeeToDelete.value = null
+    deleting.value = true
+    const success_delete = await UserService.deleteUser(employeeToDelete.value.id)
     
-    // Afficher une notification de succès
-    success('Employé supprimé avec succès')
-  } catch (error) {
-    console.error('Erreur lors de la suppression:', error)
+    if (success_delete) {
+      // Supprimer l'employé de la liste locale
+      employees.value = employees.value.filter(e => e.id !== employeeToDelete.value?.id)
+      
+      // Fermer la modal de confirmation
+      showDeleteConfirm.value = false
+      employeeToDelete.value = null
+      
+      success('Employé supprimé avec succès')
+      
+      // Recharger la page si nécessaire
+      if (employees.value.length === 0 && pagination.value.page > 1) {
+        await loadEmployees(pagination.value.page - 1)
+      }
+    } else {
+      error('Erreur lors de la suppression de l\'employé')
+    }
+  } catch (err) {
+    console.error('Erreur lors de la suppression:', err)
     error('Erreur lors de la suppression de l\'employé')
+  } finally {
+    deleting.value = false
   }
 }
 
-// Méthode saveEmployee supprimée car gérée par EmployeeModal
-
 // Fonctions pour gérer les événements de la TopBar
 const handleSearch = (query: string) => {
   searchQuery.value = query
@@ -419,62 +491,9 @@ const handleUserMenu = () => {
   console.log('Afficher le menu utilisateur')
 }
 
-// Charger les données
+// Charger les données au montage du composant
 onMounted(async () => {
-  // Simuler le chargement des rôles
-  roles.value = [
-    { id: 1, name: 'Administrateur' },
-    { id: 2, name: 'Gestionnaire' },
-    { id: 3, name: 'Agent' },
-    { id: 4, name: 'Support' }
-  ]
-  
-  // Simuler le chargement des employés
-  setTimeout(() => {
-    employees.value = [
-      {
-        id: 1,
-        name: 'Dupont',
-        otherName: 'Jean',
-        firstName: 'Jean',
-        lastName: 'Dupont',
-        email: 'jean.dupont@example.com',
-        phone: '+33 6 12 34 56 78',
-        telephone: '+33 6 12 34 56 78',
-        address: '123 Rue de Paris, 75001 Paris',
-        role: Role.Admin,
-        roleLabel: getRoleLabel(Role.Admin),
-        createdAt: '2022-01-15T10:30:00Z'
-      },
-      {
-        id: 2,
-        name: 'Laurent',
-        otherName: 'Marie',
-        firstName: 'Marie',
-        lastName: 'Laurent',
-        email: 'marie.laurent@example.com',
-        phone: '+33 6 23 45 67 89',
-        telephone: '+33 6 23 45 67 89',
-        address: '45 Avenue Victor Hugo, 75016 Paris',
-        role: Role.Manager,
-        roleLabel: getRoleLabel(Role.Manager),
-        createdAt: '2022-02-20T14:15:00Z'
-      },
-      {
-        id: 3,
-        name: 'Martin',
-        otherName: 'Pierre',
-        firstName: 'Pierre',
-        lastName: 'Martin',
-        email: 'pierre.martin@example.com',
-        phone: '+33 6 34 56 78 90',
-        telephone: '+33 6 34 56 78 90',
-        role: Role.Employee,
-        roleLabel: getRoleLabel(Role.Employee),
-        createdAt: '2022-03-10T09:45:00Z'
-      }
-    ]
-  }, 500)
+  await loadEmployees()
 })
 </script>
 
@@ -516,6 +535,54 @@ onMounted(async () => {
   box-shadow: 0 4px 8px rgba(0,0,0,0.2);
 }
 
+/* Loading state */
+.loading-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 50px 20px;
+  color: #666;
+}
+
+.loading-state i {
+  font-size: 24px;
+  margin-bottom: 10px;
+}
+
+/* Pagination */
+.pagination {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 15px;
+  margin-top: 20px;
+  padding: 20px;
+}
+
+.pagination-btn {
+  background: #f5f5f5;
+  border: 1px solid #ddd;
+  padding: 8px 12px;
+  border-radius: 4px;
+  cursor: pointer;
+  color: #666;
+}
+
+.pagination-btn:hover:not(:disabled) {
+  background: #e9e9e9;
+}
+
+.pagination-btn:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+.pagination-info {
+  color: #666;
+  font-size: 14px;
+}
+
 /* Filtres */
 .filters {
   display: flex;
@@ -729,49 +796,124 @@ onMounted(async () => {
   gap: 10px;
 }
 
-/* Formulaire */
-.form-photo {
+/* Détails employé */
+.employee-details {
+  max-width: 600px;
+}
+
+.employee-profile {
   display: flex;
-  flex-direction: column;
   align-items: center;
-  margin-bottom: 20px;
+  margin-bottom: 30px;
+  padding-bottom: 20px;
+  border-bottom: 1px solid #eee;
 }
 
-.photo-preview {
-  width: 100px;
-  height: 100px;
+.employee-profile-photo {
+  width: 80px;
+  height: 80px;
   border-radius: 50%;
   background-size: cover;
   background-position: center;
   background-color: #eee;
+  margin-right: 20px;
+}
+
+.employee-profile-info h3 {
+  margin: 0 0 5px 0;
+  font-size: 24px;
+  color: #333;
+}
+
+.employee-profile-info .email {
+  color: #666;
   margin-bottom: 10px;
 }
 
-.upload-photo {
-  background-color: #f5f5f5;
-  border: none;
-  padding: 8px 12px;
-  border-radius: 4px;
-  cursor: pointer;
+.employee-info-section {
+  margin-bottom: 25px;
+}
+
+.employee-info-section h4 {
+  margin: 0 0 15px 0;
+  font-size: 16px;
+  color: #333;
+  border-bottom: 1px solid #eee;
+  padding-bottom: 5px;
+}
+
+.info-item {
   display: flex;
   align-items: center;
-  gap: 5px;
+  margin-bottom: 10px;
+}
+
+.info-item i {
+  width: 20px;
+  margin-right: 10px;
+  color: #666;
+}
+
+.warning-icon {
+  font-size: 48px;
+  color: #ff9800;
+  display: block;
+  text-align: center;
+  margin-bottom: 20px;
+}
+
+.warning {
+  color: #f44336;
   font-size: 14px;
+  margin-top: 10px;
 }
 
-.form-row {
-  display: flex;
-  gap: 15px;
-  margin-bottom: 15px;
+/* Animation pour le spinner */
+.fa-spin {
+  animation: fa-spin 2s infinite linear;
 }
 
-.form-group {
-  flex: 1;
-  margin-bottom: 15px;
+@keyframes fa-spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(359deg);
+  }
 }
 
-.form-group label {
-  display: block;
-  margin-bottom: 5px;
+/* Responsive */
+@media (max-width: 768px) {
+  .employee-container {
+    padding: 10px;
+  }
+  
+  .employee-header {
+    flex-direction: column;
+    gap: 15px;
+    align-items: stretch;
+  }
+  
+  .filters {
+    flex-direction: column;
+  }
+  
+  .filter-group {
+    min-width: auto;
+  }
+  
+  .employee-table {
+    font-size: 14px;
+  }
+  
+  .employee-table th,
+  .employee-table td {
+    padding: 10px 5px;
+  }
+  
+  .actions {
+    flex-direction: column;
+    gap: 5px;
+  }
 }
 </style>

+ 251 - 29
app_transport_front/src/views/HomeView.vue

@@ -173,8 +173,6 @@
                 <li>Service entre particuler de là à là</li>
                 <!-- <li>Valable 30 jours</li> -->
                 <li>Support standard</li>
-                <!-- <li class="non-function">Réservation prioritaire</li>
-                <li class="non-function">Annulation gratuite</li> -->
               </ul>
               <div class="border-button">
                 <a href="#">Acheter maintenant</a>
@@ -183,8 +181,6 @@
           </div>
           <div class="col-lg-4">
             <div class="pricing-item-pro">
-              <!-- <span class="price">49€</span>
-              <h4>Forfait Premium</h4> -->
               <div class="icon">
                 <i class="fa fa-bus"></i>
               </div>
@@ -222,36 +218,47 @@
       </div>
     </div>
 
-       <!-- Trajet disponibles -->
-       <div id="trajets" class="pricing-tables">
-        <div class="container">
-          <div class="row">
-            <div class="col-lg-8 offset-lg-2">
-              <div class="section-heading">
+    <!-- Trajets disponibles -->
+    <div id="trajets" class="pricing-tables">
+      <div class="container">
+        <div class="row">
+          <div class="col-lg-8 offset-lg-2">
+            <div class="section-heading">
               <h2><em>Trajets</em> disponibles</h2>
+              <p>Découvrez nos derniers trajets disponibles</p>
             </div>
           </div>
-          <!--fait ma boucle ici pour les trajets -->
-          <div class="flex ">
-            <div class="col">
-              <div class="pricing-item-regular">
-                <div class="icon">
-                  <i class="fa fa-car"></i>
+        </div>
+        
+        <!-- Conteneur avec scroll automatique pour les trajets -->
+        <div class="trips-scroll-container">
+          <div class="trips-wrapper" ref="tripsWrapper">
+            <div v-for="trip in availableTrips" :key="trip.id" class="trip-card">
+              <div class="trip-icon">
+                <i class="fa fa-bus"></i>
+              </div>
+              <div class="trip-info">
+                <h4>{{ getStationName(trip, 'depart') || 'Station de départ' }} → {{ getStationName(trip, 'arrive') || 'Station d\'arrivée' }}</h4>
+                <div class="trip-details">
+                  <p><i class="fa fa-calendar"></i> {{ formatDate(trip.departAt) }}</p>
+                  <p><i class="fa fa-clock"></i> {{ formatTime(trip.departAt) }}</p>
+                  <p v-if="trip.pricePerSeat"><i class="fa fa-money-bill-wave"></i> {{ trip.pricePerSeat }} CFA / place</p>
+                  <p v-if="trip.availableSeats"><i class="fa fa-users"></i> {{ trip.availableSeats }} places disponibles</p>
                 </div>
-                <ul>
-                  <li>Service entre particuler de là à là</li>
-                  <!-- <li>Valable 30 jours</li> -->
-                  <li>Support standard</li>
-                  <!-- <li class="non-function">Réservation prioritaire</li>
-                  <li class="non-function">Annulation gratuite</li> -->
-                </ul>
+              </div>
+              <div class="trip-actions">
                 <div class="border-button">
-                  <a href="#">Acheter maintenant</a>
+                  <a href="#" @click.prevent="reserveTrip(trip)">Réserver</a>
                 </div>
               </div>
             </div>
           </div>
         </div>
+        
+        <!-- Message si aucun trajet -->
+        <div v-if="availableTrips.length === 0" class="no-trips">
+          <p>Aucun trajet disponible pour le moment</p>
+        </div>
       </div>
     </div>
 
@@ -307,12 +314,19 @@ import { useRouter } from 'vue-router'
 // import axios from 'axios'
 import TripSearchForm from '@/components/TripSearchForm.vue'; // Importez le nouveau composant
 import { useToast } from '@/composables/useToast'
+import TripService from '@/services/TripService';
+import { Trip } from '@/types';
+import { useStationStore } from '@/stores/useStationStore';
 
 const router = useRouter()
 const menuActive = ref(false)
 // const isRechercheTrajet = ref(false) // Remplacé par showSearchForm
 const showSearchForm = ref(false); // Pour contrôler la visibilité du formulaire
 
+// Variables pour les trajets disponibles
+const availableTrips = ref<Trip[]>([])
+const tripsWrapper = ref<Trip[]>([])
+
 // Toast notifications
 const { info } = useToast()
 
@@ -332,6 +346,76 @@ const toggleSearchForm = () => {
   showSearchForm.value = !showSearchForm.value;
 };
 
+// Fonctions utilitaires pour les dates
+const formatDate = (dateString: string) => {
+  const date = new Date(dateString)
+  return date.toLocaleDateString('fr-FR', {
+    day: '2-digit',
+    month: '2-digit',
+    year: 'numeric'
+  })
+}
+
+const formatTime = (dateString: string) => {
+  const date = new Date(dateString)
+  return date.toLocaleTimeString('fr-FR', {
+    hour: '2-digit',
+    minute: '2-digit'
+  })
+}
+
+// Fonction pour charger les trajets disponibles
+const loadAvailableTrips = async () => {
+  try {
+    const trips = await TripService.getTrips()
+    console.log('Trips:', trips)
+    availableTrips.value = trips.trips
+    // availableTrips.value = [
+    //   {
+    //     id: 1,
+    //     departStation: { name: 'Abidjan' },
+    //     arriveStation: { name: 'Bouaké' },
+    //     departAt: '2024-01-15T08:00:00Z',
+    //     pricePerSeat: 5000,
+    //     availableSeats: 15
+    //   },
+    //   {
+    //     id: 2,
+    //     departStation: { name: 'Yamoussoukro' },
+    //     arriveStation: { name: 'Korhogo' },
+    //     departAt: '2024-01-15T14:30:00Z',
+    //     pricePerSeat: 7500,
+    //     availableSeats: 8
+    //   },
+    //   {
+    //     id: 3,
+    //     departStation: { name: 'San-Pédro' },
+    //     arriveStation: { name: 'Abidjan' },
+    //     departAt: '2024-01-16T06:45:00Z',
+    //     pricePerSeat: 4500,
+    //     availableSeats: 22
+    //   }
+    // ]
+  } catch (error) {
+    console.error('Erreur lors du chargement des trajets:', error)
+    info('Erreur lors du chargement des trajets disponibles')
+  }
+}
+
+const getStationName = (trip: Trip, type: 'depart' | 'arrive' = 'depart') => {
+  const stationStore = useStationStore()
+  const stationId = type === 'depart' ? trip.departStationId : trip.arriveStationId
+  const station = stationStore.stations.find((station) => station.id === stationId)
+  return station?.name || 'Station inconnue'
+}
+// Fonction pour réserver un trajet
+const reserveTrip = (trip: any) => {
+  console.log('Réservation du trajet:', trip)
+  info(`Redirection vers la réservation pour le trajet #${trip.id}`)
+  // Naviguer vers la page de réservation
+  router.push({ name: 'reservation', params: { id: trip.id } })
+}
+
 const handleTripSearch = (criteria: any) => {
   console.log('Critères de recherche:', criteria);
   // Ici, vous naviguerez vers une page de résultats ou afficherez les résultats.
@@ -343,7 +427,11 @@ const handleTripSearch = (criteria: any) => {
   showSearchForm.value = false; // Optionnel: cacher le formulaire après la recherche
 };
 
-onMounted(() => {
+onMounted(async () => {
+  // Charger les trajets disponibles
+  await loadAvailableTrips()
+  await useStationStore().fetchStations()
+  console.log('Stations:', useStationStore().stations)
   // Smooth scrolling pour les liens d'ancrage
   document.querySelectorAll('a[href^="#"]').forEach(anchor => {
     anchor.addEventListener('click', function (e) {
@@ -804,8 +892,7 @@ html, body {
   margin-top: 20px;
 }
 
-.pricing-item-regular .border-button a,
-.pricing-item-pro .border-button a {
+.pricing-item-regular .border-button a {
   display: inline-block;
   padding: 12px 25px;
   background-color: transparent;
@@ -818,12 +905,147 @@ html, body {
   transition: all 0.3s;
 }
 
-.pricing-item-regular .border-button a:hover,
-.pricing-item-pro .border-button a:hover {
+.pricing-item-pro .border-button a {
+  display: inline-block;
+  width: 100%;
+  height: 50px;
+  border: none;
+  border-radius: 25px;
+  background-image: linear-gradient(to right, #4b8ef1 0%, #7a6ad8 100%);
+  color: #fff;
+  font-size: 15px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.3s;
+  text-decoration: none;
+  line-height: 50px;
+  text-align: center;
+}
+
+.pricing-item-regular .border-button a:hover {
   background-color: #4b8ef1;
   color: #fff;
 }
 
+.pricing-item-pro .border-button a:hover {
+  background-image: linear-gradient(to right, #7a6ad8 0%, #4b8ef1 100%);
+}
+
+/* Styles pour les trajets disponibles */
+.trips-scroll-container {
+  overflow-x: auto;
+  padding: 20px 0;
+}
+
+.trips-wrapper {
+  display: flex;
+  gap: 20px;
+  min-width: max-content;
+  padding: 0 15px;
+}
+
+.trip-card {
+  background: #fff;
+  border-radius: 15px;
+  padding: 25px;
+  min-width: 350px;
+  box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1);
+  transition: all 0.3s ease;
+  border: 1px solid #f0f0f0;
+}
+
+.trip-card:hover {
+  transform: translateY(-5px);
+  box-shadow: 0px 10px 30px rgba(0, 0, 0, 0.15);
+}
+
+.trip-icon {
+  width: 60px;
+  height: 60px;
+  background: linear-gradient(135deg, #4b8ef1 0%, #7a6ad8 100%);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 20px;
+}
+
+.trip-icon i {
+  font-size: 24px;
+  color: #fff;
+}
+
+.trip-info h4 {
+  font-size: 18px;
+  font-weight: 600;
+  color: #2a2a2a;
+  margin-bottom: 15px;
+  line-height: 1.4;
+}
+
+.trip-details {
+  margin-bottom: 20px;
+}
+
+.trip-details p {
+  font-size: 14px;
+  color: #666;
+  margin-bottom: 8px;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.trip-details i {
+  color: #4b8ef1;
+  width: 16px;
+}
+
+.trip-actions .border-button {
+  text-align: center;
+}
+
+.trip-actions .border-button a {
+  display: inline-block;
+  padding: 12px 25px;
+  background: linear-gradient(135deg, #4b8ef1 0%, #7a6ad8 100%);
+  color: #fff;
+  border: none;
+  font-size: 14px;
+  font-weight: 500;
+  border-radius: 25px;
+  text-decoration: none;
+  transition: all 0.3s ease;
+  min-width: 120px;
+}
+
+.trip-actions .border-button a:hover {
+  transform: translateY(-2px);
+  box-shadow: 0px 5px 15px rgba(75, 142, 241, 0.4);
+}
+
+.no-trips {
+  text-align: center;
+  padding: 60px 20px;
+  color: #666;
+}
+
+.no-trips p {
+  font-size: 16px;
+  font-style: italic;
+}
+
+/* Responsive pour les trajets */
+@media (max-width: 768px) {
+  .trips-wrapper {
+    padding: 0 10px;
+  }
+  
+  .trip-card {
+    min-width: 280px;
+  }
+}
+
 /* Newsletter Styles */
 .subscribe {
   padding: 100px 0;

+ 1 - 0
app_transport_front/src/views/ProfileVerificationView.vue

@@ -70,6 +70,7 @@ import { useUserStore } from '@/stores/useUserStore'
 import { onBeforeMount, ref } from 'vue'
 import { useRouter } from 'vue-router'
 import { useToast } from '@/composables/useToast'
+import UserService from '@/services/UserService'
 
 const router = useRouter()
 const selectedProfile = ref('')

+ 1576 - 0
app_transport_front/src/views/ReservationView.vue

@@ -0,0 +1,1576 @@
+<template>
+  <div class="reservation-page">
+    <!-- Header avec navigation -->
+    <header class="reservation-header">
+      <div class="container">
+        <div class="header-content">
+          <button @click="goBack" class="back-button" :disabled="goBackLoading">
+            <i v-if="!goBackLoading" class="fa fa-arrow-left"></i>
+            <i v-else class="fa fa-spinner fa-spin"></i>
+            {{ goBackLoading ? 'Chargement...' : 'Retour' }}
+          </button>
+          <h1>Réservation de trajet</h1>
+        </div>
+      </div>
+    </header>
+
+    <div class="container">
+      <div class="reservation-content">
+        <!-- Informations du trajet -->
+        <div class="trip-info-section">
+          <div class="section-header">
+            <h2><i class="fa fa-route"></i> Informations du trajet</h2>
+          </div>
+          <div class="trip-card">
+            <div class="trip-route">
+              <div class="route-point departure">
+                <div class="point-icon">
+                  <i class="fa fa-map-marker-alt"></i>
+                </div>
+                <div class="point-info">
+                  <h3>{{ departureStation?.name || 'Station de départ' }}</h3>
+                  <p>{{ departureStation?.address || 'Adresse non disponible' }}</p>
+                  <span class="time">{{ formatTime(trip?.departAt) }}</span>
+                </div>
+              </div>
+              <div class="route-line">
+                <div class="line"></div>
+                <div class="duration">{{ calculateDuration() }}</div>
+              </div>
+              <div class="route-point arrival">
+                <div class="point-icon">
+                  <i class="fa fa-map-marker-alt"></i>
+                </div>
+                <div class="point-info">
+                  <h3>{{ arrivalStation?.name || 'Station d\'arrivée' }}</h3>
+                  <p>{{ arrivalStation?.address || 'Adresse non disponible' }}</p>
+                  <span class="time">{{ formatTime(trip?.arriveAt) }}</span>
+                </div>
+              </div>
+            </div>
+            <div class="trip-details">
+              <div class="detail-item">
+                <i class="fa fa-calendar"></i>
+                <span>{{ formatDate(trip?.departAt) }}</span>
+              </div>
+              <div class="detail-item">
+                <i class="fa fa-money-bill-wave"></i>
+                <span>{{ trip?.pricePerSeat || 0 }} CFA / place</span>
+              </div>
+              <div class="detail-item">
+                <i class="fa fa-users"></i>
+                <span>{{ trip?.availableSeats || 0 }} places disponibles</span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- Informations de la compagnie -->
+        <div class="company-info-section">
+          <div class="section-header">
+            <h2><i class="fa fa-building"></i> Informations de la compagnie</h2>
+          </div>
+          <div class="company-card">
+            <div class="company-logo">
+              <img v-if="company?.logo" :src="company.logo" :alt="company.name" />
+              <div v-else class="logo-placeholder">
+                <i class="fa fa-building"></i>
+              </div>
+            </div>
+            <div class="company-details">
+              <h3>{{ company?.name || 'Compagnie de transport' }}</h3>
+              <p v-if="company?.description">{{ company.description }}</p>
+              <div class="company-contact">
+                <div v-if="company?.phone" class="contact-item">
+                  <i class="fa fa-phone"></i>
+                  <span>{{ company.phone }}</span>
+                </div>
+                <div v-if="company?.email" class="contact-item">
+                  <i class="fa fa-envelope"></i>
+                  <span>{{ company.email }}</span>
+                </div>
+                <div v-if="company?.address" class="contact-item">
+                  <i class="fa fa-map-marker-alt"></i>
+                  <span>{{ company.address }}</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- Carte des stations -->
+        <div class="map-section">
+          <div class="section-header">
+            <h2><i class="fa fa-map"></i> Localisation des stations</h2>
+          </div>
+          <div class="map-container">
+            <div id="map" class="map"></div>
+            <div class="map-legend">
+              <div class="legend-item">
+                <div class="legend-marker departure"></div>
+                <span>Station de départ</span>
+              </div>
+              <div class="legend-item">
+                <div class="legend-marker arrival"></div>
+                <span>Station d'arrivée</span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- Formulaire de réservation -->
+        <div class="reservation-form-section">
+          <div class="section-header">
+            <h2><i class="fa fa-ticket-alt"></i> Détails de la réservation</h2>
+          </div>
+          <form @submit.prevent="submitReservation" class="reservation-form">
+            <div class="form-group">
+              <label for="passenger-count">Nombre de passagers</label>
+              <select id="passenger-count" v-model="reservationData.passengerCount" required>
+                <option value="">Sélectionnez le nombre de passagers</option>
+                <option v-for="n in Math.min(trip?.availableSeats || 1, 10)" :key="n" :value="n">
+                  {{ n }} passager{{ n > 1 ? 's' : '' }}
+                </option>
+              </select>
+            </div>
+
+            <div class="passengers-info" v-if="reservationData.passengerCount">
+              <h3>Informations des passagers</h3>
+              <div v-for="(passenger, index) in passengers" :key="index" class="passenger-form">
+                <h4>Passager {{ index + 1 }}</h4>
+                <div class="form-row">
+                  <div class="form-group">
+                    <label :for="`firstName-${index}`">Prénom</label>
+                    <input 
+                      :id="`firstName-${index}`"
+                      type="text" 
+                      v-model="passenger.firstName" 
+                      required 
+                      placeholder="Prénom"
+                    />
+                  </div>
+                  <div class="form-group">
+                    <label :for="`lastName-${index}`">Nom</label>
+                    <input 
+                      :id="`lastName-${index}`"
+                      type="text" 
+                      v-model="passenger.lastName" 
+                      required 
+                      placeholder="Nom"
+                    />
+                  </div>
+                </div>
+                <div class="form-row">
+                  <div class="form-group">
+                    <label :for="`phone-${index}`">Téléphone</label>
+                    <input 
+                      :id="`phone-${index}`"
+                      type="tel" 
+                      v-model="passenger.phone" 
+                      required 
+                      placeholder="Numéro de téléphone"
+                    />
+                  </div>
+                  <div class="form-group">
+                    <label :for="`email-${index}`">Email (optionnel)</label>
+                    <input 
+                      :id="`email-${index}`"
+                      type="email" 
+                      v-model="passenger.email" 
+                      placeholder="Adresse email"
+                    />
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- Résumé de la commande -->
+            <div class="order-summary">
+              <h3>Résumé de la commande</h3>
+              <div class="summary-item">
+                <span>Prix par place:</span>
+                <span>{{ trip?.pricePerSeat || 0 }} CFA</span>
+              </div>
+              <div class="summary-item">
+                <span>Nombre de places:</span>
+                <span>{{ reservationData.passengerCount || 0 }}</span>
+              </div>
+              <div class="summary-item total">
+                <span>Total:</span>
+                <span>{{ calculateTotal() }} CFA</span>
+              </div>
+            </div>
+
+            <!-- Moyens de paiement -->
+            <div class="payment-section">
+              <h3><i class="fa fa-credit-card"></i> Moyen de paiement</h3>
+              <div class="payment-methods">
+                <div class="payment-method" 
+                     :class="{ active: reservationData.paymentMethod === 'card' }"
+                     @click="reservationData.paymentMethod = 'card'">
+                  <input type="radio" id="card" value="card" v-model="reservationData.paymentMethod" />
+                  <label for="card">
+                    <i class="fa fa-credit-card"></i>
+                    <span>Carte bancaire</span>
+                  </label>
+                </div>
+                <div class="payment-method" 
+                     :class="{ active: reservationData.paymentMethod === 'mobile' }"
+                     @click="reservationData.paymentMethod = 'mobile'">
+                  <input type="radio" id="mobile" value="mobile" v-model="reservationData.paymentMethod" />
+                  <label for="mobile">
+                    <i class="fa fa-mobile-alt"></i>
+                    <span>Mobile Money</span>
+                  </label>
+                </div>
+                <div class="payment-method" 
+                     :class="{ active: reservationData.paymentMethod === 'cash' }"
+                     @click="reservationData.paymentMethod = 'cash'">
+                  <input type="radio" id="cash" value="cash" v-model="reservationData.paymentMethod" />
+                  <label for="cash">
+                    <i class="fa fa-money-bill-wave"></i>
+                    <span>Paiement à bord</span>
+                  </label>
+                </div>
+              </div>
+              
+              <!-- Message d'avertissement pour paiement à bord -->
+              <div v-if="reservationData.paymentMethod === 'cash'" class="payment-warning">
+                <div class="warning-content">
+                  <i class="fa fa-exclamation-triangle"></i>
+                  <div class="warning-text">
+                    <strong>Attention :</strong> Le paiement à bord n'est pas prioritaire. 
+                    Les réservations avec paiement en ligne (carte bancaire ou Mobile Money) 
+                    sont traitées en priorité. Vous risquez de perdre votre place si un autre 
+                    passager réserve en ligne avant vous.
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <!-- Formulaire de paiement Mobile Money -->
+            <div v-if="reservationData.paymentMethod === 'mobile'" class="mobile-payment-form">
+              <h3><i class="fa fa-mobile-alt"></i> Choisissez votre opérateur Mobile Money</h3>
+              <div class="mobile-operators">
+                <div class="operator-option" 
+                     :class="{ active: mobileData.operator === 'orange' }"
+                     @click="mobileData.operator = 'orange'">
+                  <input type="radio" id="orange" value="orange" v-model="mobileData.operator" />
+                  <label for="orange" class="operator-label">
+                    <div class="operator-logo orange-logo">
+                      <svg viewBox="0 0 200 100" class="logo-svg">
+                        <rect width="200" height="100" fill="#FF6600" rx="10"/>
+                        <text x="100" y="35" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="16" font-weight="bold">Orange</text>
+                        <text x="100" y="55" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="14">Money</text>
+                        <path d="M30 70 L50 70 M40 60 L40 80" stroke="white" stroke-width="2" fill="none"/>
+                        <circle cx="160" cy="70" r="8" fill="white"/>
+                      </svg>
+                    </div>
+                    <span class="operator-name">Orange Money</span>
+                  </label>
+                </div>
+                
+                <div class="operator-option" 
+                     :class="{ active: mobileData.operator === 'mtn' }"
+                     @click="mobileData.operator = 'mtn'">
+                  <input type="radio" id="mtn" value="mtn" v-model="mobileData.operator" />
+                  <label for="mtn" class="operator-label">
+                    <div class="operator-logo mtn-logo">
+                      <svg viewBox="0 0 200 100" class="logo-svg">
+                        <rect width="200" height="100" fill="#FFCC00" rx="10"/>
+                        <rect x="10" y="10" width="180" height="80" fill="white" rx="5"/>
+                        <ellipse cx="100" cy="50" rx="60" ry="25" fill="#004B87"/>
+                        <text x="100" y="40" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="18" font-weight="bold">MTN</text>
+                        <text x="100" y="58" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="10">Mobile Money</text>
+                      </svg>
+                    </div>
+                    <span class="operator-name">MTN Mobile Money</span>
+                  </label>
+                </div>
+                
+                <div class="operator-option" 
+                     :class="{ active: mobileData.operator === 'wave' }"
+                     @click="mobileData.operator = 'wave'">
+                  <input type="radio" id="wave" value="wave" v-model="mobileData.operator" />
+                  <label for="wave" class="operator-label">
+                    <div class="operator-logo wave-logo">
+                      <svg viewBox="0 0 200 100" class="logo-svg">
+                        <circle cx="100" cy="50" r="45" fill="#00BFFF"/>
+                        <circle cx="85" cy="35" r="3" fill="white"/>
+                        <circle cx="95" cy="35" r="3" fill="white"/>
+                        <ellipse cx="90" cy="45" rx="8" ry="6" fill="white"/>
+                        <ellipse cx="90" cy="60" rx="15" ry="10" fill="white"/>
+                        <path d="M75 65 Q85 70 95 65" stroke="#FF6600" stroke-width="2" fill="none"/>
+                        <path d="M105 65 Q115 70 125 65" stroke="#FF6600" stroke-width="2" fill="none"/>
+                        <text x="100" y="85" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="14" font-weight="bold">wave</text>
+                      </svg>
+                    </div>
+                    <span class="operator-name">Wave</span>
+                  </label>
+                </div>
+              </div>
+              
+              <!-- Formulaire de numéro de téléphone pour Mobile Money -->
+              <div v-if="mobileData.operator" class="mobile-form">
+                <div class="form-group">
+                  <label for="mobile-number">Numéro de téléphone</label>
+                  <input 
+                    id="mobile-number"
+                    type="tel" 
+                    v-model="mobileData.phoneNumber" 
+                    :placeholder="getPhoneNumberPlaceholder()"
+                    required
+                  />
+                </div>
+                <div class="mobile-info">
+                  <i class="fa fa-info-circle"></i>
+                  <span>Vous recevrez un SMS de confirmation pour valider le paiement</span>
+                </div>
+              </div>
+            </div>
+
+            <!-- Formulaire de paiement par carte -->
+            <div v-if="reservationData.paymentMethod === 'card'" class="card-payment-form">
+              <h3><i class="fa fa-credit-card"></i> Informations de paiement</h3>
+              <div class="card-form">
+                <div class="form-row">
+                  <div class="form-group">
+                    <label for="card-number">Numéro de carte</label>
+                    <input 
+                      id="card-number"
+                      type="text" 
+                      v-model="cardData.number" 
+                      placeholder="1234 5678 9012 3456"
+                      maxlength="19"
+                      @input="formatCardNumber"
+                      required
+                    />
+                  </div>
+                  <div class="form-group">
+                    <label for="card-name">Nom sur la carte</label>
+                    <input 
+                      id="card-name"
+                      type="text" 
+                      v-model="cardData.name" 
+                      placeholder="JEAN DUPONT"
+                      style="text-transform: uppercase;"
+                      required
+                    />
+                  </div>
+                </div>
+                <div class="form-row">
+                  <div class="form-group">
+                    <label for="card-expiry">Date d'expiration</label>
+                    <input 
+                      id="card-expiry"
+                      type="text" 
+                      v-model="cardData.expiry" 
+                      placeholder="MM/AA"
+                      maxlength="5"
+                      @input="formatExpiryDate"
+                      required
+                    />
+                  </div>
+                  <div class="form-group">
+                    <label for="card-cvv">CVV</label>
+                    <input 
+                      id="card-cvv"
+                      type="text" 
+                      v-model="cardData.cvv" 
+                      placeholder="123"
+                      maxlength="4"
+                      required
+                    />
+                  </div>
+                </div>
+                <div class="security-info">
+                  <i class="fa fa-shield-alt"></i>
+                  <span>Vos informations de paiement sont sécurisées et cryptées</span>
+                </div>
+              </div>
+            </div>
+
+            <!-- Bouton de soumission -->
+            <div class="form-actions">
+              <button type="button" @click="goBack" class="btn-secondary">
+                Annuler
+              </button>
+              <button type="submit" class="btn-primary" :disabled="!isFormValid">
+                <i class="fa fa-check"></i>
+                Confirmer la réservation
+              </button>
+            </div>
+          </form>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { useToast } from '@/composables/useToast'
+import { useStationStore } from '@/stores/useStationStore'
+import TripService from '@/services/TripService'
+import type { Trip, Station, Company } from '@/types'
+import 'leaflet/dist/leaflet.css'
+// @ts-ignore
+import * as L from 'leaflet'
+import CompanyService from '@/services/CompanyService'
+
+const route = useRoute()
+const router = useRouter()
+const { success, error } = useToast()
+const stationStore = useStationStore()
+
+// État réactif
+const loading = ref(true)
+const goBackLoading = ref(false)
+const trip = ref<Trip | null>(null)
+const company = ref<Company | null>(null)
+const departureStation = ref<Station | null>(null)
+const arrivalStation = ref<Station | null>(null)
+const mapInstance = ref<L.Map | null>(null)
+
+// Données de réservation
+const reservationData = ref({
+  passengerCount: '',
+  paymentMethod: ''
+})
+
+// Données de carte bancaire
+const cardData = ref({
+  number: '',
+  name: '',
+  expiry: '',
+  cvv: ''
+})
+
+// Données Mobile Money
+const mobileData = ref({
+  operator: '',
+  phoneNumber: ''
+})
+
+const passengers = ref<Array<{
+  firstName: string
+  lastName: string
+  phone: string
+  email: string
+}>>([]);
+
+// Watchers
+watch(() => reservationData.value.passengerCount, (newCount) => {
+  const count = parseInt(newCount)
+  if (count > 0) {
+    passengers.value = Array.from({ length: count }, () => ({
+      firstName: '',
+      lastName: '',
+      phone: '',
+      email: ''
+    }))
+  } else {
+    passengers.value = []
+  }
+})
+
+// Computed
+const isFormValid = computed(() => {
+  if (!reservationData.value.passengerCount || !reservationData.value.paymentMethod) {
+    return false
+  }
+  
+  const passengersValid = passengers.value.every(passenger => 
+    passenger.firstName.trim() && 
+    passenger.lastName.trim() && 
+    passenger.phone.trim()
+  )
+  
+  // Validation supplémentaire pour le paiement par carte
+  if (reservationData.value.paymentMethod === 'card') {
+    const cardValid = cardData.value.number.replace(/\s/g, '').length >= 13 &&
+                     cardData.value.name.trim() &&
+                     cardData.value.expiry.length === 5 &&
+                     cardData.value.cvv.length >= 3
+    return passengersValid && cardValid
+  }
+  
+  // Validation supplémentaire pour Mobile Money
+  if (reservationData.value.paymentMethod === 'mobile') {
+    const mobileValid = mobileData.value.operator &&
+                       mobileData.value.phoneNumber.trim().length >= 8
+    return passengersValid && mobileValid
+  }
+  
+  return passengersValid
+})
+
+// Méthodes
+const goBack = () => {
+  goBackLoading.value = true
+  router.go(-1)
+}
+
+const formatDate = (dateString: string | undefined) => {
+  if (!dateString) return 'Date non disponible'
+  const date = new Date(dateString)
+  return date.toLocaleDateString('fr-FR', {
+    day: '2-digit',
+    month: '2-digit',
+    year: 'numeric'
+  })
+}
+
+const formatTime = (dateString: string | undefined) => {
+  if (!dateString) return 'Heure non disponible'
+  const date = new Date(dateString)
+  return date.toLocaleTimeString('fr-FR', {
+    hour: '2-digit',
+    minute: '2-digit'
+  })
+}
+
+const calculateDuration = () => {
+  if (!trip.value?.departAt || !trip.value?.arriveAt) return 'Durée inconnue'
+  
+  const departure = new Date(trip.value.departAt)
+  const arrival = new Date(trip.value.arriveAt)
+  const duration = arrival.getTime() - departure.getTime()
+  
+  const hours = Math.floor(duration / (1000 * 60 * 60))
+  const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60))
+  
+  return `${hours}h${minutes.toString().padStart(2, '0')}`
+}
+
+const calculateTotal = () => {
+  const count = parseInt(reservationData.value.passengerCount) || 0
+  const price = trip.value?.pricePerSeat || 0
+  return count * price
+}
+
+// Fonctions de formatage pour la carte
+const formatCardNumber = (event: Event) => {
+  const input = event.target as HTMLInputElement
+  let value = input.value.replace(/\s/g, '').replace(/\D/g, '')
+  value = value.replace(/(\d{4})(?=\d)/g, '$1 ')
+  cardData.value.number = value
+}
+
+const formatExpiryDate = (event: Event) => {
+  const input = event.target as HTMLInputElement
+  let value = input.value.replace(/\D/g, '')
+  if (value.length >= 2) {
+    value = value.substring(0, 2) + '/' + value.substring(2, 4)
+  }
+  cardData.value.expiry = value
+}
+
+const getPhoneNumberPlaceholder = () => {
+  switch (mobileData.value.operator) {
+    case 'orange':
+      return '07 XX XX XX XX ou 01 XX XX XX XX'
+    case 'mtn':
+      return '05 XX XX XX XX ou 02 XX XX XX XX'
+    case 'wave':
+      return '01 XX XX XX XX'
+    default:
+      return 'Numéro de téléphone'
+  }
+}
+
+const loadTripData = async () => {
+  try {
+    loading.value = true
+    const tripId = route.params.id as string
+    
+    if (!tripId) {
+      error('ID du trajet manquant')
+      router.push('/')
+      return
+    }
+
+    // Charger les stations si pas déjà fait
+    if (stationStore.stations.length === 0) {
+      await stationStore.fetchStations()
+    }
+
+    // Charger les détails du trajet
+    const tripResponse = await TripService.getTripById(parseInt(tripId))
+    trip.value = tripResponse.data
+    console.log('Trip', tripResponse.data)
+    // Trouver les stations de départ et d'arrivée
+    if (trip.value) {
+      departureStation.value = stationStore.stations.find(
+        station => station.id === trip.value!.departStationId
+      ) || null
+      
+      arrivalStation.value = stationStore.stations.find(
+        station => station.id === trip.value!.arriveStationId
+      ) || null
+      console.log('trip.value.companyId', trip.value.companyId)
+      // Charger les informations de la compagnie si disponible
+      if (trip.value.companyId) {
+        // Ici vous devriez avoir un service pour charger les détails de la compagnie
+        const response = await CompanyService.getCompany(trip.value.companyId)
+        company.value = response?.data
+      }
+    }
+
+    // Initialiser la carte
+    initializeMap()
+    
+  } catch (err) {
+    console.error('Erreur lors du chargement des données:', err)
+    error('Erreur lors du chargement des informations du trajet')
+  } finally {
+    loading.value = false
+  }
+}
+
+const initializeMap = async () => {
+  await nextTick()
+  
+  if (!departureStation.value || !arrivalStation.value) {
+    console.warn('Stations de départ ou d\'arrivée manquantes')
+    return
+  }
+
+  // Supprimer l'instance de carte existante si elle existe
+  if (mapInstance.value) {
+    mapInstance.value.remove()
+  }
+
+  // Coordonnées par défaut pour les stations (vous devriez avoir ces données dans votre base)
+  const defaultCoords = {
+    [departureStation.value.name]: { lat: 5.348, lng: -4.007 }, // Abidjan par défaut
+    [arrivalStation.value.name]: { lat: 7.683, lng: -5.030 }   // Bouaké par défaut
+  }
+
+  const departureCoords = {
+    lat: departureStation.value.lat || defaultCoords[departureStation.value.name]?.lat || 5.348,
+    lng: departureStation.value.long || defaultCoords[departureStation.value.name]?.lng || -4.007
+  }
+
+  const arrivalCoords = {
+    lat: arrivalStation.value.lat|| defaultCoords[arrivalStation.value.name]?.lat || 7.683,
+    lng: arrivalStation.value.long|| defaultCoords[arrivalStation.value.name]?.lng || -5.030
+  }
+
+  // Créer la carte
+  mapInstance.value = L.map('map').setView([departureCoords.lat, departureCoords.lng], 7)
+
+  // Ajouter les tuiles OpenStreetMap
+  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+    attribution: '&copy; OpenStreetMap contributors'
+  }).addTo(mapInstance.value)
+
+  // Créer des icônes personnalisées
+  const departureIcon = L.divIcon({
+    html: '<i class="fa fa-map-marker-alt" style="color: #4CAF50; font-size: 24px;"></i>',
+    iconSize: [30, 30],
+    className: 'custom-div-icon'
+  })
+
+  const arrivalIcon = L.divIcon({
+    html: '<i class="fa fa-map-marker-alt" style="color: #f44336; font-size: 24px;"></i>',
+    iconSize: [30, 30],
+    className: 'custom-div-icon'
+  })
+
+  // Ajouter les marqueurs
+  L.marker([departureCoords.lat, departureCoords.lng], { icon: departureIcon })
+    .addTo(mapInstance.value)
+    .bindPopup(`<strong>Départ:</strong> ${departureStation.value.name}`)
+
+  L.marker([arrivalCoords.lat, arrivalCoords.lng], { icon: arrivalIcon })
+    .addTo(mapInstance.value)
+    .bindPopup(`<strong>Arrivée:</strong> ${arrivalStation.value.name}`)
+
+  // Ajouter une ligne entre les deux points
+  L.polyline([
+    [departureCoords.lat, departureCoords.lng],
+    [arrivalCoords.lat, arrivalCoords.lng]
+  ], { 
+    color: '#2196F3', 
+    weight: 3,
+    opacity: 0.7
+  }).addTo(mapInstance.value)
+
+  // Ajuster la vue pour inclure les deux marqueurs
+  mapInstance.value.fitBounds([
+    [departureCoords.lat, departureCoords.lng],
+    [arrivalCoords.lat, arrivalCoords.lng]
+  ], { padding: [20, 20] })
+}
+
+const submitReservation = async () => {
+  try {
+    if (!isFormValid.value) {
+      error('Veuillez remplir tous les champs obligatoires')
+      return
+    }
+
+    const reservationPayload = {
+      tripId: trip.value?.id,
+      passengers: passengers.value,
+      paymentMethod: reservationData.value.paymentMethod,
+      totalAmount: calculateTotal(),
+      ...(reservationData.value.paymentMethod === 'card' && {
+        cardInfo: {
+          number: cardData.value.number.replace(/\s/g, ''),
+          name: cardData.value.name,
+          expiry: cardData.value.expiry,
+          cvv: cardData.value.cvv
+        }
+      })
+    }
+
+    console.log('Données de réservation:', reservationPayload)
+    
+    // Ici vous devriez appeler votre service de réservation
+    // await ReservationService.createReservation(reservationPayload)
+    
+    success('Réservation confirmée avec succès!')
+    
+    // Rediriger vers une page de confirmation ou le dashboard
+    //router.push({ name: 'home' })
+    
+  } catch (err) {
+    console.error('Erreur lors de la réservation:', err)
+    error('Erreur lors de la confirmation de la réservation')
+  }
+}
+
+onMounted(() => {
+  loadTripData()
+})
+
+onBeforeUnmount(() => {
+  if (mapInstance.value) {
+    mapInstance.value.remove()
+  }
+})
+</script>
+
+<style scoped>
+/* Styles pour Mobile Money */
+.mobile-payment-form {
+  margin-top: 2rem;
+  padding: 1.5rem;
+  border: 1px solid #e5e7eb;
+  border-radius: 0.5rem;
+  background-color: #f9fafb;
+}
+
+.mobile-operators {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 1rem;
+  margin: 1rem 0;
+}
+
+.operator-option {
+  position: relative;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.operator-option input[type="radio"] {
+  position: absolute;
+  opacity: 0;
+  width: 0;
+  height: 0;
+}
+
+.operator-label {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 1rem;
+  border: 2px solid #e5e7eb;
+  border-radius: 0.75rem;
+  background-color: white;
+  transition: all 0.3s ease;
+  cursor: pointer;
+}
+
+.operator-option:hover .operator-label {
+  border-color: #3b82f6;
+  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
+}
+
+.operator-option.active .operator-label {
+  border-color: #3b82f6;
+  background-color: #eff6ff;
+  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.25);
+}
+
+.operator-logo {
+  width: 120px;
+  height: 60px;
+  margin-bottom: 0.5rem;
+  border-radius: 0.5rem;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.logo-svg {
+  width: 100%;
+  height: 100%;
+}
+
+.operator-name {
+  font-weight: 600;
+  color: #374151;
+  text-align: center;
+}
+
+.mobile-form {
+  margin-top: 1.5rem;
+  padding: 1rem;
+  background-color: white;
+  border-radius: 0.5rem;
+  border: 1px solid #e5e7eb;
+}
+
+.mobile-info {
+  display: flex;
+  align-items: center;
+  gap: 0.5rem;
+  margin-top: 0.75rem;
+  padding: 0.75rem;
+  background-color: #eff6ff;
+  border-radius: 0.5rem;
+  color: #1e40af;
+  font-size: 0.875rem;
+}
+
+.mobile-info i {
+  color: #3b82f6;
+}
+
+/* Responsive design pour mobile */
+@media (max-width: 768px) {
+  .mobile-operators {
+    grid-template-columns: 1fr;
+  }
+  
+  .operator-logo {
+    width: 100px;
+    height: 50px;
+  }
+}
+
+/* Styles existants... */
+.reservation-page {
+  min-height: 100vh;
+  background-color: #f8f9fa;
+}
+
+.reservation-header {
+  background: linear-gradient(135deg, #4b8ef1 0%, #7a6ad8 100%);
+  color: white;
+  padding: 20px 0;
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+}
+
+.header-content {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+}
+
+.back-button {
+  background: rgba(255, 255, 255, 0.2);
+  border: none;
+  color: white;
+  padding: 10px 15px;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.back-button:hover:not(:disabled) {
+  background: rgba(255, 255, 255, 0.3);
+}
+
+.back-button:disabled {
+  background: rgba(255, 255, 255, 0.1);
+  cursor: not-allowed;
+  opacity: 0.7;
+}
+
+.back-button:disabled .fa-spinner {
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+.container {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 0 20px;
+}
+
+.reservation-content {
+  padding: 30px 0;
+  display: grid;
+  gap: 30px;
+}
+
+.section-header {
+  margin-bottom: 20px;
+}
+
+.section-header h2 {
+  color: #2c3e50;
+  font-size: 24px;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.section-header h2 i {
+  color: #4b8ef1;
+}
+
+/* Trip Info Styles */
+.trip-card {
+  background: white;
+  border-radius: 12px;
+  padding: 25px;
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+}
+
+.trip-route {
+  display: flex;
+  align-items: center;
+  margin-bottom: 25px;
+}
+
+.route-point {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  gap: 15px;
+}
+
+.point-icon {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: white;
+  font-size: 18px;
+}
+
+.departure .point-icon {
+  background: #28a745;
+}
+
+.arrival .point-icon {
+  background: #dc3545;
+}
+
+.point-info h3 {
+  margin: 0 0 5px 0;
+  color: #2c3e50;
+  font-size: 18px;
+}
+
+.point-info p {
+  margin: 0 0 5px 0;
+  color: #6c757d;
+  font-size: 14px;
+}
+
+.time {
+  background: #e9ecef;
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 600;
+  color: #495057;
+}
+
+.route-line {
+  flex: 0 0 100px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin: 0 20px;
+}
+
+.line {
+  width: 2px;
+  height: 40px;
+  background: linear-gradient(to bottom, #28a745, #dc3545);
+  position: relative;
+}
+
+.duration {
+  background: #4b8ef1;
+  color: white;
+  padding: 4px 8px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: 600;
+  margin-top: 10px;
+}
+
+.trip-details {
+  display: flex;
+  gap: 30px;
+  padding-top: 20px;
+  border-top: 1px solid #e9ecef;
+}
+
+.detail-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #6c757d;
+}
+
+.detail-item i {
+  color: #4b8ef1;
+}
+
+/* Company Info Styles */
+.company-card {
+  background: white;
+  border-radius: 12px;
+  padding: 25px;
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+  display: flex;
+  gap: 20px;
+}
+
+.company-logo {
+  flex: 0 0 80px;
+}
+
+.company-logo img {
+  width: 80px;
+  height: 80px;
+  border-radius: 8px;
+  object-fit: cover;
+}
+
+.logo-placeholder {
+  width: 80px;
+  height: 80px;
+  background: #e9ecef;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #6c757d;
+  font-size: 24px;
+}
+
+.company-details h3 {
+  margin: 0 0 10px 0;
+  color: #2c3e50;
+  font-size: 20px;
+}
+
+.company-details p {
+  margin: 0 0 15px 0;
+  color: #6c757d;
+  line-height: 1.5;
+}
+
+.company-contact {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.contact-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  color: #6c757d;
+  font-size: 14px;
+}
+
+.contact-item i {
+  color: #4b8ef1;
+  width: 16px;
+}
+
+/* Map Styles */
+.map-container {
+  background: white;
+  border-radius: 12px;
+  padding: 25px;
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+}
+
+.map {
+  width: 100%;
+  height: 300px;
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  margin-bottom: 15px;
+  position: relative;
+}
+
+.custom-div-icon {
+  background: transparent;
+  border: none;
+}
+
+.leaflet-popup-content-wrapper {
+  border-radius: 8px;
+}
+
+.leaflet-popup-content {
+  margin: 8px 12px;
+  font-family: inherit;
+}
+
+.static-map {
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 20px;
+  position: relative;
+}
+
+.map-marker {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 5px;
+  z-index: 2;
+}
+
+.departure-marker i {
+  color: #28a745;
+  font-size: 24px;
+}
+
+.arrival-marker i {
+  color: #dc3545;
+  font-size: 24px;
+}
+
+.map-marker span {
+  background: white;
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 600;
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+}
+
+.map-route {
+  position: absolute;
+  top: 50%;
+  left: 20%;
+  right: 20%;
+  height: 2px;
+  background: linear-gradient(to right, #28a745, #dc3545);
+  z-index: 1;
+}
+
+.map-legend {
+  display: flex;
+  gap: 20px;
+  justify-content: center;
+}
+
+.legend-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-size: 14px;
+  color: #6c757d;
+}
+
+.legend-marker {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+}
+
+.legend-marker.departure {
+  background: #28a745;
+}
+
+.legend-marker.arrival {
+  background: #dc3545;
+}
+
+/* Form Styles */
+.reservation-form-section {
+  background: white;
+  border-radius: 12px;
+  padding: 25px;
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+}
+
+.reservation-form {
+  display: flex;
+  flex-direction: column;
+  gap: 25px;
+}
+
+.form-group {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.form-group label {
+  font-weight: 600;
+  color: #2c3e50;
+}
+
+.form-group input,
+.form-group select {
+  padding: 12px;
+  border: 2px solid #e9ecef;
+  border-radius: 8px;
+  font-size: 16px;
+  transition: border-color 0.3s ease;
+}
+
+.form-group input:focus,
+.form-group select:focus {
+  outline: none;
+  border-color: #4b8ef1;
+}
+
+.form-row {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 20px;
+}
+
+.passengers-info {
+  border: 1px solid #e9ecef;
+  border-radius: 8px;
+  padding: 20px;
+}
+
+.passengers-info h3 {
+  margin: 0 0 20px 0;
+  color: #2c3e50;
+}
+
+.passenger-form {
+  border-bottom: 1px solid #e9ecef;
+  padding-bottom: 20px;
+  margin-bottom: 20px;
+}
+
+.passenger-form:last-child {
+  border-bottom: none;
+  margin-bottom: 0;
+  padding-bottom: 0;
+}
+
+.passenger-form h4 {
+  margin: 0 0 15px 0;
+  color: #495057;
+  font-size: 16px;
+}
+
+.order-summary {
+  background: #f8f9fa;
+  border-radius: 8px;
+  padding: 20px;
+}
+
+.order-summary h3 {
+  margin: 0 0 15px 0;
+  color: #2c3e50;
+}
+
+.summary-item {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 10px;
+  color: #6c757d;
+}
+
+.summary-item.total {
+  border-top: 1px solid #dee2e6;
+  padding-top: 10px;
+  margin-top: 10px;
+  font-weight: 600;
+  color: #2c3e50;
+  font-size: 18px;
+}
+
+.payment-section h3 {
+  margin: 0 0 15px 0;
+  color: #2c3e50;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.payment-methods {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 15px;
+}
+
+.payment-method {
+  border: 2px solid #e9ecef;
+  border-radius: 8px;
+  padding: 15px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.payment-method.active {
+  border-color: #4b8ef1;
+  background: #f8f9ff;
+}
+
+.payment-method input {
+  display: none;
+}
+
+.payment-method label {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  cursor: pointer;
+  margin: 0;
+}
+
+.payment-method i {
+  color: #4b8ef1;
+  font-size: 18px;
+}
+
+.form-actions {
+  display: flex;
+  gap: 15px;
+  justify-content: flex-end;
+  padding-top: 20px;
+  border-top: 1px solid #e9ecef;
+}
+
+.btn-secondary,
+.btn-primary {
+  padding: 12px 24px;
+  border-radius: 8px;
+  font-size: 16px;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.btn-secondary {
+  background: #6c757d;
+  color: white;
+  border: none;
+}
+
+.btn-secondary:hover {
+  background: #5a6268;
+}
+
+.btn-primary {
+  background: linear-gradient(135deg, #4b8ef1 0%, #7a6ad8 100%);
+  color: white;
+  border: none;
+}
+
+.btn-primary:hover:not(:disabled) {
+  transform: translateY(-2px);
+  box-shadow: 0 4px 15px rgba(75, 142, 241, 0.3);
+}
+
+.btn-primary:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+/* Payment Warning Styles */
+.payment-warning {
+  margin-top: 15px;
+  padding: 15px;
+  background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
+  border: 1px solid #ffeaa7;
+  border-radius: 8px;
+  animation: slideIn 0.3s ease-out;
+}
+
+.warning-content {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+}
+
+.warning-content i {
+  color: #856404;
+  font-size: 18px;
+  margin-top: 2px;
+  flex-shrink: 0;
+}
+
+.warning-text {
+  color: #856404;
+  font-size: 14px;
+  line-height: 1.5;
+}
+
+.warning-text strong {
+  color: #6c5400;
+  font-weight: 600;
+}
+
+@keyframes slideIn {
+  from {
+    opacity: 0;
+    transform: translateY(-10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+/* Card Payment Form Styles */
+.card-payment-form {
+  margin-top: 20px;
+  padding: 25px;
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+  border: 2px solid #e3f2fd;
+  animation: slideIn 0.3s ease-out;
+}
+
+.card-payment-form h3 {
+  color: #2c3e50;
+  font-size: 20px;
+  font-weight: 600;
+  margin-bottom: 20px;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.card-payment-form h3 i {
+  color: #4b8ef1;
+}
+
+.card-form {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.card-form .form-group input {
+  border: 2px solid #e9ecef;
+  border-radius: 8px;
+  padding: 12px 15px;
+  font-size: 16px;
+  transition: all 0.3s ease;
+  background: #f8f9fa;
+}
+
+.card-form .form-group input:focus {
+  border-color: #4b8ef1;
+  background: white;
+  box-shadow: 0 0 0 3px rgba(75, 142, 241, 0.1);
+}
+
+.card-form .form-group input[placeholder] {
+  font-family: 'Courier New', monospace;
+}
+
+.card-form .form-group input#card-name {
+  font-family: inherit;
+}
+
+.security-info {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 12px 15px;
+  background: linear-gradient(135deg, #e8f5e8 0%, #f0f8f0 100%);
+  border-radius: 8px;
+  color: #2e7d32;
+  font-size: 14px;
+  border: 1px solid #c8e6c9;
+}
+
+.security-info i {
+  color: #4caf50;
+  font-size: 16px;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+  .container {
+    padding: 0 15px;
+  }
+  
+  .trip-route {
+    flex-direction: column;
+    gap: 20px;
+  }
+  
+  .route-line {
+    flex-direction: row;
+    margin: 0;
+  }
+  
+  .line {
+    width: 40px;
+    height: 2px;
+  }
+  
+  .company-card {
+    flex-direction: column;
+    text-align: center;
+  }
+  
+  .form-row {
+    grid-template-columns: 1fr;
+  }
+  
+  .payment-methods {
+    grid-template-columns: 1fr;
+  }
+  
+  .form-actions {
+    flex-direction: column;
+  }
+  
+  .trip-details {
+    flex-direction: column;
+    gap: 15px;
+  }
+}
+</style>

+ 171 - 50
app_transport_front/src/views/StationCompanyView.vue

@@ -25,7 +25,7 @@
       <div class="p-5 max-w-6xl mx-auto">
         <div class="flex justify-between items-center mb-5">
           <h1 class="text-2xl text-gray-800">Relations Station-Entreprise</h1>
-          <button @click="showCreateModal = true" class="bg-blue-500 text-white border-0 rounded px-4 py-2.5 flex items-center gap-2 cursor-pointer font-medium hover:bg-blue-600">
+          <button @click="showCreateModal = true; initializeForm()" class="bg-blue-500 text-white border-0 rounded px-4 py-2.5 flex items-center gap-2 cursor-pointer font-medium hover:bg-blue-600">
             <i class="fas fa-plus"></i>
             Ajouter une relation
           </button>
@@ -46,7 +46,7 @@
             <label class="font-medium text-gray-600">Station:</label>
             <select v-model="stationFilter" class="py-2.5 px-2.5 border border-gray-300 rounded bg-white min-w-40">
               <option value="">Toutes les stations</option>
-              <option v-for="station in stations" :key="station.id" :value="station.id.toString()">
+              <option v-for="station in stationStore.stations" :key="station.id" :value="station.id.toString()">
                 {{ station.name }}
               </option>
             </select>
@@ -56,7 +56,7 @@
         <div v-if="!loading">
           <!-- Tableau des relations -->
           <div class="bg-white rounded-lg shadow-sm overflow-hidden">
-            <table class="w-full border-collapse" v-if="filteredRelations.length > 0">
+            <table class="w-full border-collapse" v-if="pagination.total > 0">
               <thead>
                 <tr>
                   <th class="bg-gray-50 py-3 px-4 text-left font-semibold text-gray-800 border-b border-gray-200">ID</th>
@@ -67,11 +67,11 @@
                 </tr>
               </thead>
               <tbody>
-                <tr v-for="relation in filteredRelations" :key="relation.id" class="hover:bg-gray-50">
+                <tr v-for="relation in displayedRelations" :key="relation.id" class="hover:bg-gray-50">
                   <td class="py-3 px-4 border-b border-gray-200">{{ relation.id }}</td>
                   <td class="py-3 px-4 border-b border-gray-200">{{ getStationName(relation.stationId) }}</td>
                   <td class="py-3 px-4 border-b border-gray-200">{{ getCompanyName(relation.companyId) }}</td>
-                  <td class="py-3 px-4 border-b border-gray-200">{{ relation.stationChiefId || 'Non assigné' }}</td>
+                  <td class="py-3 px-4 border-b border-gray-200">{{ getEmployeeName(relation.stationChiefId) }}</td>
                   <td class="py-3 px-4 border-b border-gray-200">
                     <div class="flex gap-1">
                       <button @click="editRelation(relation)" class="bg-yellow-500 text-white border-0 rounded p-2 cursor-pointer hover:bg-yellow-600">
@@ -87,11 +87,18 @@
             </table>
             
             <!-- Empty state -->
-            <div v-if="filteredRelations.length === 0" class="text-center py-12">
+            <div v-if="pagination.total === 0" class="text-center py-12">
               <i class="fas fa-inbox text-4xl text-gray-400 mb-4"></i>
               <p class="text-gray-600 mb-4">Aucune relation trouvée</p>
             </div>
           </div>
+          
+          <!-- Pagination -->
+          <PaginationComponent 
+            v-if="pagination.total > 0"
+            :pagination="pagination" 
+            @page-change="handlePageChange" 
+          />
         </div>
 
         <div v-else class="text-center py-12">
@@ -115,23 +122,35 @@
                  <label class="block text-sm font-medium text-gray-700 mb-2">Station <span class="text-red-500">*</span></label>
                  <select v-model="formData.stationId" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
                    <option value="">Sélectionner une station</option>
-                   <option v-for="station in stations" :key="station.id" :value="station.id">
+                   <option v-for="station in stationStore.stations" :key="station.id" :value="station.id">
                      {{ station.name }}
                    </option>
                  </select>
                </div>
                <div class="mb-4">
-                 <label class="block text-sm font-medium text-gray-700 mb-2">Entreprise <span class="text-red-500">*</span></label>
-                 <select v-model="formData.companyId" required class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
-                   <option value="">Sélectionner une entreprise</option>
-                   <option v-for="company in companies" :key="company.id" :value="company.id">
-                     {{ company.name }}
-                   </option>
-                 </select>
+                 <label class="block text-sm font-medium text-gray-700 mb-2">
+                   Entreprise <span class="text-red-500">*</span>
+                   <span v-if="isCompanyFieldDisabled" class="text-xs text-gray-500 font-normal">(Entreprise assignée automatiquement)</span>
+                 </label>
+                 <select 
+                   required 
+                   :disabled="isCompanyFieldDisabled"
+                   :class="{
+                     'w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500': !isCompanyFieldDisabled,
+                     'w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-100 text-gray-600 cursor-not-allowed': isCompanyFieldDisabled
+                   }"
+                 >
+                  <option>{{ useUserCompanyStore().name }}</option>
+                  </select>
                </div>
                <div class="mb-4">
                  <label class="block text-sm font-medium text-gray-700 mb-2">Chef de Station</label>
-                 <input v-model="formData.stationChiefId" type="number" placeholder="ID du chef de station" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
+                 <select v-model="formData.stationChiefId" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
+                   <option value="">Sélectionner un chef de station</option>
+                   <option v-for="employee in employees" :key="employee.id" :value="employee.id">
+                     {{ employee.name || `${employee.firstName} ${employee.lastName}` }}
+                   </option>
+                 </select>
                </div>
              </form>
            </div>
@@ -152,66 +171,139 @@
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted, reactive, computed } from 'vue'
+import { ref, onMounted, reactive, computed, watch, onBeforeMount } from 'vue'
 import type { StationCompany, Station, Company } from '@/types'
 import StationCompanyService from '@/services/StationCompanyService'
 import StationService from '@/services/StationService'
 import CompanyService from '@/services/CompanyService'
 import Sidebar from '@/components/Sidebar.vue'
 import TopBar from '@/components/TopBar.vue'
+import PaginationComponent from '@/components/PaginationComponent.vue'
 import { useUserStore } from '@/stores/useUserStore'
+import { useStationStore } from '@/stores/useStationStore'
+import { Role } from '@/Enum/Role'
+import { useUserCompanyStore } from '@/stores/userCompanyStore'
 
+const userStore = useUserStore()
+const stationStore = useStationStore()
 const loading = ref(false)
 const stationCompanies = ref<StationCompany[]>([])
-const stations = ref<Station[]>([])
 const companies = ref<Company[]>([])
+const employees = ref<any[]>([])
 const showCreateModal = ref(false)
 const showEditModal = ref(false)
 const currentRelation = ref<StationCompany | null>(null)
 const searchQuery = ref('')
 const stationFilter = ref('')
 
+// Objet de pagination
+const pagination = ref({
+  total: 0,
+  page: 1,
+  limit: 10,
+  totalPages: 0
+})
+
 const formData = reactive({
   stationId: '',
-  companyId: '',
-  stationChiefId: null as number | null
+  companyId: 0,
+  stationChiefId: undefined as number | undefined
 })
 
-// Computed pour les relations filtrées
-const filteredRelations = computed(() => {
-  let filtered = stationCompanies.value
-  
-  if (searchQuery.value) {
-    const query = searchQuery.value.toLowerCase()
-    filtered = filtered.filter(relation => {
-      const stationName = getStationName(relation.stationId).toLowerCase()
-      const companyName = getCompanyName(relation.companyId).toLowerCase()
-      return stationName.includes(query) || companyName.includes(query)
-    })
-  }
-  
-  if (stationFilter.value) {
-    filtered = filtered.filter(relation => relation.stationId.toString() === stationFilter.value)
-  }
-  
-  return filtered
+// Computed pour vérifier si l'utilisateur est admin
+const isAdmin = computed(() => {
+  return userStore.userRole === Role.Admin || userStore.userRole === 'super_admin'
+})
+
+// Computed pour vérifier si le champ entreprise doit être désactivé
+const isCompanyFieldDisabled = computed(() => {
+  return !isAdmin.value
+})
+
+// Computed pour les relations affichées (directement depuis le backend)
+const displayedRelations = computed(() => {
+  return stationCompanies.value
 })
 
 onMounted(async () => {
   await loadData()
 })
 
+// Variables pour le debounce
+let searchTimeout: NodeJS.Timeout | null = null
+
+// Watchers pour recharger les données quand les filtres changent
+watch([searchQuery, stationFilter], () => {
+  if (searchTimeout) {
+    clearTimeout(searchTimeout)
+  }
+  searchTimeout = setTimeout(() => {
+    pagination.value.page = 1
+    loadStationCompanies()
+  }, 300)
+})
+
+// Watcher pour charger les employés quand l'entreprise change
+watch(() => formData.companyId, async (newCompanyId) => {
+  if (newCompanyId) {
+    await loadEmployees(newCompanyId)
+  } else {
+    employees.value = []
+  }
+  // Réinitialiser le chef de station quand l'entreprise change
+  formData.stationChiefId = undefined
+})
+
+const loadStationCompanies = async () => {
+  loading.value = true
+  try {
+    const searchParam = searchQuery.value.trim() || undefined
+    const stationParam = stationFilter.value || undefined
+    const response = await StationCompanyService.getStationCompanies(
+      pagination.value.page, 
+      pagination.value.limit, 
+      searchParam, 
+      stationParam
+    )
+    stationCompanies.value = response.stationCompanies
+    pagination.value = response.pagination
+  } catch (error) {
+    console.error('Erreur lors du chargement des relations:', error)
+  } finally {
+    loading.value = false
+  }
+}
+
+const loadEmployees = async (companyId: number) => {
+  try {
+    const response = await CompanyService.getCompanyEmployees(companyId, 1, 100)
+    employees.value = response.employees
+  } catch (error) {
+    console.error('Erreur lors du chargement des employés:', error)
+    employees.value = []
+  }
+}
+
 const loadData = async () => {
   loading.value = true
   try {
-    const [relationsData, stationsData, companiesData] = await Promise.all([
-      StationCompanyService.getStationCompanies(),
-      StationService.getStations(),
+    const searchParam = searchQuery.value.trim() || undefined
+    const stationParam = stationFilter.value || undefined
+    const [relationsResponse, companiesResponse] = await Promise.all([
+      StationCompanyService.getStationCompanies(
+        pagination.value.page, 
+        pagination.value.limit, 
+        searchParam, 
+        stationParam
+      ),
       CompanyService.getCompanies()
     ])
-    stationCompanies.value = relationsData
-    stations.value = stationsData
-    companies.value = companiesData
+    // Charger les stations via le store
+    await stationStore.fetchStations()
+    
+    stationCompanies.value = relationsResponse.stationCompanies
+    pagination.value = relationsResponse.pagination
+    companies.value = companiesResponse.companies
   } catch (error) {
     console.error('Erreur lors du chargement des données:', error)
   } finally {
@@ -220,7 +312,7 @@ const loadData = async () => {
 }
 
 const getStationName = (stationId: number) => {
-  const station = stations.value.find(s => s.id === stationId)
+  const station = stationStore.stations.find(s => s.id === stationId)
   return station ? station.name : 'Station inconnue'
 }
 
@@ -229,11 +321,20 @@ const getCompanyName = (companyId: number) => {
   return company ? company.name : 'Entreprise inconnue'
 }
 
-const editRelation = (relation: StationCompany) => {
+const getEmployeeName = (employeeId: number | undefined) => {
+  if (!employeeId) return 'Non assigné'
+  const employee = employees.value.find(e => e.id === employeeId)
+  return employee ? (employee.name || `${employee.firstName} ${employee.lastName}`) : 'Employé inconnu'
+}
+
+const editRelation = async (relation: StationCompany) => {
   currentRelation.value = relation
   formData.stationId = relation.stationId.toString()
-  formData.companyId = relation.companyId.toString()
-  formData.stationChiefId = relation.stationChiefId || null
+  // Pour l'édition, garder l'entreprise existante même si l'utilisateur n'est pas admin
+  formData.companyId = relation.companyId
+  formData.stationChiefId = relation.stationChiefId
+  // Charger les employés de l'entreprise
+  await loadEmployees(relation.companyId)
   showEditModal.value = true
 }
 
@@ -249,7 +350,7 @@ const deleteRelation = async (id: number) => {
 const submitForm = async () => {
   const data = {
     stationId: parseInt(formData.stationId),
-    companyId: parseInt(formData.companyId),
+    companyId: formData.companyId,
     stationChiefId: formData.stationChiefId
   }
 
@@ -272,8 +373,19 @@ const closeModals = () => {
 
 const resetForm = () => {
   formData.stationId = ''
-  formData.companyId = ''
-  formData.stationChiefId = null
+  formData.companyId = isAdmin.value ? 0 : (parseInt(userStore.decodedCompanyId) || 0)
+  formData.stationChiefId = undefined
+  employees.value = []
+}
+
+const initializeForm = async () => {
+  resetForm()
+  // Si l'utilisateur n'est pas admin, pré-remplir avec son entreprise
+  if (!isAdmin.value && userStore.decodedCompanyId) {
+    formData.companyId = userStore.decodedCompanyId
+    // Charger les employés de l'entreprise
+    await loadEmployees(userStore.decodedCompanyId)
+  }
 }
 
 // Handlers pour TopBar
@@ -292,5 +404,14 @@ const handleMessages = () => {
 const handleUserMenu = () => {
   console.log('User menu toggled')
 }
+
+// Méthode de gestion du changement de page
+const handlePageChange = async (page: number) => {
+  pagination.value.page = page
+  await loadStationCompanies()
+}
+
+// onBeforeMount supprimé pour éviter les doubles chargements
+// Le chargement initial se fait dans onMounted via loadData()
 </script>
 

+ 319 - 20
app_transport_front/src/views/StationView.vue

@@ -81,6 +81,94 @@
           </div>
         </div>
 
+        <!-- Loading state -->
+        <div v-if="loading" class="text-center py-12">
+          <i class="fas fa-spinner fa-spin text-4xl text-gray-400 mb-4"></i>
+          <p class="text-gray-600">Chargement des stations...</p>
+        </div>
+
+        <!-- Pagination -->
+        <div class="pagination" v-if="!loading && stations.length > 0">
+          <div class="flex justify-between items-center mt-6 px-4">
+            <!-- Informations de pagination -->
+            <div class="text-sm text-gray-600">
+              <span v-if="searchQuery.trim()">
+                {{ filteredStations.length }} station(s) trouvée(s) sur {{ stations.length }} au total
+              </span>
+              <span v-else>
+                Affichage de {{ ((pagination.page - 1) * pagination.limit) + 1 }} à {{ Math.min(pagination.page * pagination.limit, pagination.total) }} sur {{ pagination.total }} stations
+              </span>
+            </div>
+            
+            <!-- Sélecteur d'éléments par page -->
+            <div class="flex items-center gap-2">
+              <label class="text-sm text-gray-600">Éléments par page:</label>
+              <select v-model="pagination.limit" @change="changeItemsPerPage(pagination.limit)" class="border border-gray-300 rounded px-2 py-1 text-sm">
+                <option value="5">5</option>
+                <option value="10">10</option>
+                <option value="20">20</option>
+                <option value="50">50</option>
+              </select>
+            </div>
+            
+            <!-- Contrôles de pagination (masqués pendant la recherche) -->
+            <div class="flex items-center gap-2" v-if="!searchQuery.trim()">
+              <!-- Première page -->
+              <button 
+                @click="goToPage(1)" 
+                :disabled="pagination.page <= 1"
+                class="px-3 py-1 border border-gray-300 rounded text-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
+              >
+                <i class="fas fa-angle-double-left"></i>
+              </button>
+              
+              <!-- Page précédente -->
+              <button 
+                @click="previousPage()" 
+                :disabled="pagination.page <= 1"
+                class="px-3 py-1 border border-gray-300 rounded text-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
+              >
+                <i class="fas fa-angle-left"></i>
+              </button>
+              
+              <!-- Numéros de page -->
+              <template v-for="page in getVisiblePages()" :key="page">
+                <button 
+                  v-if="typeof page === 'number'"
+                  @click="goToPage(page)"
+                  :class="[
+                    'px-3 py-1 border text-sm',
+                    page === pagination.page 
+                      ? 'bg-blue-500 text-white border-blue-500' 
+                      : 'border-gray-300 hover:bg-gray-50'
+                  ]"
+                >
+                  {{ page }}
+                </button>
+                <span v-else class="px-2 text-gray-500">{{ page }}</span>
+              </template>
+              
+              <!-- Page suivante -->
+              <button 
+                @click="nextPage()" 
+                :disabled="pagination.page >= pagination.totalPages"
+                class="px-3 py-1 border border-gray-300 rounded text-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
+              >
+                <i class="fas fa-angle-right"></i>
+              </button>
+              
+              <!-- Dernière page -->
+              <button 
+                @click="goToPage(pagination.totalPages)" 
+                :disabled="pagination.page >= pagination.totalPages"
+                class="px-3 py-1 border border-gray-300 rounded text-sm hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
+              >
+                <i class="fas fa-angle-double-right"></i>
+              </button>
+            </div>
+          </div>
+        </div>
+
       <!-- Modal d'ajout/modification -->
       <div class="modal-overlay" v-if="showAddForm || editingStation" @click="closeModal">
         <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" @click.stop>
@@ -130,18 +218,32 @@
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted, reactive, computed } from 'vue'
+import { ref, onMounted, onUnmounted, reactive, computed, watch } from 'vue'
 import Sidebar from '@/components/Sidebar.vue'
 import TopBar from '@/components/TopBar.vue'
 import { useUserStore } from '@/stores/useUserStore'
-import { useStationStore } from '@/stores/useStationStore'
+import StationService from '@/services/StationService'
 import type { Station } from '@/types'
+import { useToast } from '@/composables/useToast'
 
-const stationStore = useStationStore()
+// État de l'application
+const stations = ref<Station[]>([])
+const loading = ref(false)
 const showAddForm = ref(false)
 const editingStation = ref<Station | null>(null)
 const searchQuery = ref('')
 
+// Pagination
+const pagination = ref({
+  total: 0,
+  page: 1,
+  limit: 5,
+  totalPages: 1
+})
+
+// Toast notifications
+const { success, error } = useToast()
+
 const formData = reactive({
   name: '',
   address: '',
@@ -149,19 +251,78 @@ const formData = reactive({
   long: null as number | null
 })
 
-// Computed pour les stations filtrées
+// Computed pour les stations affichées (optimisé)
 const filteredStations = computed(() => {
-  if (!searchQuery.value) {
-    return stationStore.stations
+  if (!searchQuery.value.trim()) {
+    return stations.value
   }
-  return stationStore.stations.filter(station => 
-    station.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
-    station.address?.toLowerCase().includes(searchQuery.value.toLowerCase())
+  
+  const query = searchQuery.value.toLowerCase().trim()
+  return stations.value.filter(station => 
+    station.name.toLowerCase().includes(query) ||
+    (station.address && station.address.toLowerCase().includes(query))
   )
 })
 
-onMounted(() => {
-  stationStore.fetchStations()
+// Fonction pour charger les stations avec pagination (optimisée)
+const loadStations = async (page: number = 1) => {
+  try {
+    loading.value = true
+    
+    const result = await StationService.getStations(page, pagination.value.limit)
+    
+    stations.value = result.stations
+    pagination.value = {
+      total: result.pagination.total,
+      page: result.pagination.page,
+      limit: result.pagination.limit,
+      totalPages: result.pagination.totalPages
+    }
+  } catch (err) {
+    console.error('Erreur lors du chargement des stations:', err)
+    error('Erreur lors du chargement des stations')
+    stations.value = []
+    pagination.value = { total: 0, page: 1, limit: 10, totalPages: 1 }
+  } finally {
+    loading.value = false
+  }
+}
+
+// Fonctions de navigation de pagination
+const goToPage = async (page: number | string) => {
+  if (typeof page === 'number' && page !== pagination.value.page) {
+    await loadStations(page)
+  }
+}
+
+const previousPage = async () => {
+  if (pagination.value.page > 1) {
+    await loadStations(pagination.value.page - 1)
+  }
+}
+
+const nextPage = async () => {
+  if (pagination.value.page < pagination.value.totalPages) {
+    await loadStations(pagination.value.page + 1)
+  }
+}
+
+const changeItemsPerPage = async (newLimit: number) => {
+  pagination.value.limit = newLimit
+  pagination.value.page = 1
+  await loadStations(1)
+}
+
+onMounted(async () => {
+  await loadStations(1)
+})
+
+// Nettoyage lors du démontage du composant
+onUnmounted(() => {
+  if (searchTimeout) {
+    clearTimeout(searchTimeout)
+    searchTimeout = null
+  }
 })
 
 // Handlers pour TopBar
@@ -195,20 +356,140 @@ const editStation = (station: Station) => {
   showAddForm.value = true
 }
 
-const deleteStation = async (id: number) => {
-  if (confirm('Êtes-vous sûr de vouloir supprimer cette station ?')) {
-    await stationStore.deleteStation(id)
+
+
+// Fonction pour obtenir les pages visibles
+const getVisiblePages = (): (number | string)[] => {
+  const pages: (number | string)[] = []
+  const total = pagination.value.totalPages
+  const current = pagination.value.page
+  
+  if (total <= 7) {
+    // Si moins de 7 pages, afficher toutes les pages
+    for (let i = 1; i <= total; i++) {
+      pages.push(i)
+    }
+  } else {
+    // Logique pour afficher les pages avec des ellipses
+    if (current <= 4) {
+      // Début: 1, 2, 3, 4, 5, ..., total
+      for (let i = 1; i <= 5; i++) {
+        pages.push(i)
+      }
+      pages.push('...')
+      pages.push(total)
+    } else if (current >= total - 3) {
+      // Fin: 1, ..., total-4, total-3, total-2, total-1, total
+      pages.push(1)
+      pages.push('...')
+      for (let i = total - 4; i <= total; i++) {
+        pages.push(i)
+      }
+    } else {
+      // Milieu: 1, ..., current-1, current, current+1, ..., total
+      pages.push(1)
+      pages.push('...')
+      for (let i = current - 1; i <= current + 1; i++) {
+        pages.push(i)
+      }
+      pages.push('...')
+      pages.push(total)
+    }
   }
+  
+  return pages
 }
 
 const submitForm = async () => {
-  if (editingStation.value) {
-    await stationStore.updateStation(editingStation.value.id!, formData)
-  } else {
-    await stationStore.createStation(formData)
+  // Validation des données
+  if (!formData.name.trim()) {
+    error('Le nom de la station est requis')
+    return
+  }
+  
+  if (!formData.address.trim()) {
+    error('L\'adresse de la station est requise')
+    return
+  }
+  
+  if (formData.lat === null || formData.long === null) {
+    error('Les coordonnées (latitude et longitude) sont requises')
+    return
+  }
+  
+  if (formData.lat < -90 || formData.lat > 90) {
+    error('La latitude doit être comprise entre -90 et 90')
+    return
+  }
+  
+  if (formData.long < -180 || formData.long > 180) {
+    error('La longitude doit être comprise entre -180 et 180')
+    return
+  }
+  
+  try {
+    loading.value = true
+    
+    if (editingStation.value) {
+      const result = await StationService.updateStation(editingStation.value.id!, formData)
+      if (result) {
+        success('Station modifiée avec succès')
+        await loadStations(pagination.value.page)
+      } else {
+        error('Erreur lors de la modification de la station')
+        return
+      }
+    } else {
+      const result = await StationService.createStation(formData)
+      if (result) {
+        success('Station créée avec succès')
+        await loadStations(1) // Retourner à la première page après création
+      } else {
+        error('Erreur lors de la création de la station')
+        return
+      }
+    }
+    
+    closeModal()
+    resetForm()
+  } catch (err) {
+    console.error('Erreur lors de la soumission:', err)
+    error('Une erreur est survenue lors de la sauvegarde')
+  } finally {
+    loading.value = false
+  }
+}
+
+const deleteStation = async (id: number) => {
+  if (!confirm('Êtes-vous sûr de vouloir supprimer cette station ?')) {
+    return
+  }
+  
+  try {
+    loading.value = true
+    const result = await StationService.deleteStation(id)
+    
+    if (result) {
+      success('Station supprimée avec succès')
+      
+      // Calculer la page à recharger après suppression
+      const currentPage = pagination.value.page
+      const totalAfterDelete = Math.max(0, pagination.value.total - 1)
+      const totalPagesAfterDelete = totalAfterDelete > 0 ? Math.ceil(totalAfterDelete / pagination.value.limit) : 1
+      
+      // Si on était sur la dernière page et qu'elle devient vide, aller à la page précédente
+      const pageToLoad = currentPage > totalPagesAfterDelete ? Math.max(1, totalPagesAfterDelete) : currentPage
+      
+      await loadStations(pageToLoad)
+    } else {
+      error('Erreur lors de la suppression de la station')
+    }
+  } catch (err) {
+    console.error('Erreur lors de la suppression:', err)
+    error('Une erreur est survenue lors de la suppression')
+  } finally {
+    loading.value = false
   }
-  closeModal()
-  resetForm()
 }
 
 const closeModal = () => {
@@ -222,6 +503,24 @@ const resetForm = () => {
   formData.lat = null
   formData.long = null
 }
+
+// Watch optimisé pour la recherche avec debounce
+let searchTimeout: NodeJS.Timeout | null = null
+watch(searchQuery, () => {
+  // Debounce pour éviter trop d'appels lors de la saisie
+  if (searchTimeout) {
+    clearTimeout(searchTimeout)
+  }
+  
+  searchTimeout = setTimeout(async () => {
+    // Pour la recherche côté client, pas besoin de recharger depuis le serveur
+    // Le computed filteredStations se charge du filtrage
+    if (searchQuery.value.trim()) {
+      // Si on recherche, on reste sur la première page logique du filtrage
+      pagination.value.page = 1
+    }
+  }, 300) // Attendre 300ms après la dernière saisie
+})
 </script>
 
 <!-- Suppression de la section <style> car les styles sont maintenant dans app-theme.css -->

+ 428 - 329
app_transport_front/src/views/TripView.vue

@@ -40,7 +40,7 @@
             <select v-model="driverFilter">
               <option value="">Tous les chauffeurs</option>
               <option v-for="driver in drivers" :key="driver.id" :value="driver.id">
-                {{ driver.firstName }} {{ driver.lastName }}
+                {{ driver.name }} {{ driver.otherName }}
               </option>
             </select>
           </div>
@@ -57,7 +57,13 @@
 
         <!-- Tableau des voyages -->
         <div class="trip-table-container">
-          <table class="trip-table" v-if="filteredTrips.length > 0">
+          <!-- Indicateur de chargement -->
+          <div v-if="loading" class="loading-state">
+            <div class="loading-spinner"></div>
+            <p>Chargement des voyages...</p>
+          </div>
+          
+          <table class="trip-table" v-else-if="filteredTrips.length > 0">
             <thead>
               <tr>
                 <th>Départ</th>
@@ -73,16 +79,16 @@
             </thead>
             <tbody>
               <tr v-for="trip in filteredTrips" :key="trip.id">
-                <td>{{ getCityName(trip.departureCity) }}</td>
-                <td>{{ getCityName(trip.arrivalCity) }}</td>
-                <td>{{ formatDateTime(trip.departureDateTime) }}</td>
-                <td>{{ formatDateTime(trip.estimatedArrivalDateTime) }}</td>
-                <td>{{ trip.pricePerSeat }} FCFA</td>
-                <td>{{ trip.availableSeats }}/{{ trip.totalSeats }}</td>
+                <td>{{ getStationName(trip.departStationId) }}</td>
+                <td>{{ getStationName(trip.arriveStationId) }}</td>
+                <td>{{ formatDateTime(trip.departAt) }}</td>
+                <td>{{ formatDateTime(trip.arriveAt) }}</td>
+                <td>{{ trip.pricePerSeat || 0 }} FCFA</td>
+                <td>{{ trip.availableSeats || 0 }}/{{ trip.totalSeats || 0 }}</td>
                 <td>{{ getDriverName(trip.driverId) }}</td>
                 <td>
                   <span class="status-badge" :class="getStatusClass(trip.status)">
-                    {{ getStatusLabel(trip.status) }}
+                    {{ getStatusLabel(trip.status || 'upcoming') }}
                   </span>
                 </td>
                 <td class="actions-cell">
@@ -99,8 +105,16 @@
               </tr>
             </tbody>
           </table>
+          
           <div class="no-data" v-else>
-            <p>Aucun voyage trouvé</p>
+            <div class="no-data-icon">
+              <i class="fas fa-route"></i>
+            </div>
+            <h3>{{ trips.length === 0 ? 'Aucun voyage enregistré' : 'Aucun voyage trouvé' }}</h3>
+            <p>{{ trips.length === 0 ? 'Commencez par ajouter votre premier voyage.' : 'Essayez de modifier vos critères de recherche.' }}</p>
+            <button v-if="trips.length === 0" class="add-button" @click="showAddForm = true">
+              <i class="fas fa-plus"></i> Ajouter un voyage
+            </button>
           </div>
         </div>
       </div>
@@ -117,20 +131,20 @@
             <div class="p-5">
               <div class="form-row">
                 <div class="form-group">
-                  <label>Ville de départ <span class="required">*</span></label>
+                  <label>Station de départ <span class="required">*</span></label>
                   <select v-model="formData.departureCity" required>
-                    <option value="">Sélectionner une ville</option>
-                    <option v-for="city in cities" :key="city.id" :value="city.id">
-                      {{ city.name }} {{ city?.commune }}
+                    <option value="">Sélectionner une station</option>
+                    <option v-for="station in stationStore.stations" :key="station.id" :value="station.id">
+                      {{ station.name }}
                     </option>
                   </select>
                 </div>
                 <div class="form-group">
-                  <label>Ville d'arrivée <span class="required">*</span></label>
+                  <label>Station d'arrivée <span class="required">*</span></label>
                   <select v-model="formData.arrivalCity" required>
-                    <option value="">Sélectionner une ville</option>
-                    <option v-for="city in cities" :key="city.id" :value="city.id">
-                      {{ city.name }}
+                    <option value="">Sélectionner une station</option>
+                    <option v-for="station in stationStore.stations" :key="station.id" :value="station.id">
+                      {{ station.name }}
                     </option>
                   </select>
                 </div>
@@ -149,65 +163,62 @@
 
               <div class="form-row">
                 <div class="form-group">
-                  <label>Date et heure d'arrivée réelle</label>
-                  <input type="datetime-local" v-model="formData.actualArrivalDateTime">
+                  <label>Véhicule <span class="required">*</span></label>
+                  <select v-model="formData.vehicleId" required>
+                  <option value="">Sélectionner un véhicule</option>
+                  <option v-for="vehicle in vehicles" :key="vehicle.id" :value="vehicle.id">
+                    {{ vehicle.type }} {{ vehicle.model }}  - {{ vehicle.number_places }} places
+                  </option>
+                </select>
                 </div>
                 <div class="form-group">
-                  <label>Prix par place (FCFA) <span class="required">*</span></label>
-                  <input type="number" v-model="formData.pricePerSeat" min="0" required>
+                  <label>Chauffeur <span class="required">*</span></label>
+                  <select v-model="formData.driverId" required>
+                    <option value="">Sélectionner un chauffeur</option>
+                    <option v-for="driver in drivers" :key="driver.id" :value="driver.id">
+                      {{ driver.name }} {{ driver.otherName }}
+                    </option>
+                  </select>
                 </div>
               </div>
 
               <div class="form-row">
                 <div class="form-group">
-                  <label>Véhicule <span class="required">*</span></label>
-                  <select v-model="formData.vehicleId" required @change="updateTotalSeats">
-                    <option value="">Sélectionner un véhicule</option>
-                    <option v-for="vehicle in vehicles" :key="vehicle.id" :value="vehicle.id">
-                      {{ vehicle.make }} {{ vehicle.model }} ({{ vehicle.registrationNumber }})
-                    </option>
-                  </select>
+                  <label>Prix par place <span class="required">*</span></label>
+                  <input type="number" v-model="formData.pricePerSeat" min="0" step="100" required placeholder="Prix en FCFA">
                 </div>
                 <div class="form-group">
-                  <label>Nombre total de places</label>
-                  <input type="number" v-model="formData.totalSeats" readonly>
+                  <label>Nombre total de places <span class="required">*</span></label>
+                  <input type="number" v-model="formData.totalSeats" min="1" required placeholder="Nombre de places">
                 </div>
               </div>
 
               <div class="form-row">
                 <div class="form-group">
-                  <label>Places disponibles <span class="required">*</span></label>
-                  <input type="number" v-model="formData.availableSeats" min="0" :max="formData.totalSeats" required>
+                  <label>Statut</label>
+                  <select v-model="formData.status">
+                    <option value="upcoming">À venir</option>
+                    <option value="ongoing">En cours</option>
+                    <option value="completed">Terminé</option>
+                    <option value="cancelled">Annulé</option>
+                  </select>
                 </div>
                 <div class="form-group">
-                  <label>Chauffeur <span class="required">*</span></label>
-                  <select v-model="formData.driverId" required>
-                    <option value="">Sélectionner un chauffeur</option>
-                    <option v-for="driver in drivers" :key="driver.id" :value="driver.id">
-                      {{ driver.firstName }} {{ driver.lastName }}
-                    </option>
-                  </select>
+                  <label>Date d'arrivée réelle</label>
+                  <input type="datetime-local" v-model="formData.actualArrivalDateTime">
                 </div>
               </div>
 
-              <div class="form-group">
-                <label>Statut <span class="required">*</span></label>
-                <select v-model="formData.status" required>
-                  <option value="upcoming">À venir</option>
-                  <option value="ongoing">En cours</option>
-                  <option value="completed">Terminé</option>
-                  <option value="cancelled">Annulé</option>
-                </select>
-              </div>
-
-              <div class="form-group">
-                <label>Notes</label>
-                <textarea v-model="formData.notes" rows="3"></textarea>
+              <div class="form-row">
+                <div class="form-group full-width">
+                  <label>Notes</label>
+                  <textarea v-model="formData.notes" rows="3" placeholder="Notes optionnelles sur le voyage..."></textarea>
+                </div>
               </div>
             </div>
             <div class="py-4 px-5 border-t border-gray-200 flex justify-end gap-2.5">
               <button class="bg-gray-100 text-gray-600 border-0 rounded py-2.5 px-4 cursor-pointer" @click="closeForm">Annuler</button>
-              <button class="bg-blue-500 text-white border-0 rounded py-2.5 px-4 cursor-pointer hover:bg-blue-600" @click="saveTrip" :disabled="!isFormValid">
+              <button class="bg-blue-500 text-white border-0 rounded py-2.5 px-4 cursor-pointer hover:bg-blue-600" @click="saveTrip" > <!-- :disabled="!isFormValid" à vérifier et a ajouter -->
                 {{ editingTrip ? 'Mettre à jour' : 'Enregistrer' }}
               </button>
             </div>
@@ -227,26 +238,26 @@
               <div class="trip-details">
                 <div class="trip-route">
                   <div class="departure">
-                    <h4>{{ getCityName(viewingTrip.departureCity) }}</h4>
-                    <p>{{ formatDateTime(viewingTrip.departureDateTime) }}</p>
+                    <h4>{{ getStationName(viewingTrip.departStationId) }}</h4>
+                    <p>{{ formatDateTime(viewingTrip.departAt) }}</p>
                   </div>
                   <div class="route-line">
                     <i class="fas fa-arrow-right"></i>
                   </div>
                   <div class="arrival">
-                    <h4>{{ getCityName(viewingTrip.arrivalCity) }}</h4>
-                    <p>{{ formatDateTime(viewingTrip.estimatedArrivalDateTime) }}</p>
+                    <h4>{{ getStationName(viewingTrip.arriveStationId) }}</h4>
+                    <p>{{ formatDateTime(viewingTrip.arriveAt) }}</p>
                   </div>
                 </div>
 
                 <div class="trip-info-grid">
                   <div class="info-item">
                     <div class="info-label">Prix par place</div>
-                    <div class="info-value">{{ viewingTrip.pricePerSeat }} FCFA</div>
+                    <div class="info-value">{{ viewingTrip.pricePerSeat || 0 }} FCFA</div>
                   </div>
                   <div class="info-item">
                     <div class="info-label">Places disponibles</div>
-                    <div class="info-value">{{ viewingTrip.availableSeats }}/{{ viewingTrip.totalSeats }}</div>
+                    <div class="info-value">{{ viewingTrip.availableSeats || 0 }}/{{ viewingTrip.totalSeats || 0 }}</div>
                   </div>
                   <div class="info-item">
                     <div class="info-label">Chauffeur</div>
@@ -254,13 +265,13 @@
                   </div>
                   <div class="info-item">
                     <div class="info-label">Véhicule</div>
-                    <div class="info-value">{{ getVehicleName(viewingTrip.vehicleId) }}</div>
+                    <div class="info-value">{{ getVehicleName(viewingTrip.vehiculeId) }}</div>
                   </div>
                   <div class="info-item">
                     <div class="info-label">Statut</div>
                     <div class="info-value">
-                      <span class="status-badge" :class="getStatusClass(viewingTrip.status)">
-                        {{ getStatusLabel(viewingTrip.status) }}
+                      <span class="status-badge" :class="getStatusClass(viewingTrip.status || 'upcoming')">
+                        {{ getStatusLabel(viewingTrip.status || 'upcoming') }}
                       </span>
                     </div>
                   </div>
@@ -271,16 +282,16 @@
                     <!-- Ajout des sommes -->
                   <div class="info-item">
                     <div class="info-label">Totale des places prises</div>
-                    <div class="info-value">{{ (viewingTrip.totalSeats - viewingTrip.availableSeats) * viewingTrip.pricePerSeat }} FCFA</div>
+                    <div class="info-value">{{ ((viewingTrip.totalSeats || 0) - (viewingTrip.availableSeats || 0)) * (viewingTrip.pricePerSeat || 0) }} FCFA</div>
                   </div>
                   <div class="info-item">
                     <div class="info-label">Totale des places non occupés</div>
-                    <div class="info-value">{{ viewingTrip.availableSeats * viewingTrip.pricePerSeat }} FCFA</div>
+                    <div class="info-value">{{ (viewingTrip.availableSeats || 0) * (viewingTrip.pricePerSeat || 0) }} FCFA</div>
                   </div>
                   <div class="info-item">
                     <div class="info-label">Totale des places en espèces</div>
                     <!-- Calcul à faire -->
-                    <div class="info-value">{{ viewingTrip.availableSeats * viewingTrip.pricePerSeat }} FCFA</div>
+                    <div class="info-value">{{ (viewingTrip.availableSeats || 0) * (viewingTrip.pricePerSeat || 0) }} FCFA</div>
                   </div>
                 </div>
 
@@ -293,9 +304,9 @@
                 <div class="trip-map" v-if="viewingTrip">
                     <h4>Trajet sur la carte</h4>
                     <TripMap
-                      :departureCityId="viewingTrip.departureCity"
-                      :arrivalCityId="viewingTrip.arrivalCity"
-                      :cities="cities"
+                      :departureCityId="viewingTrip.departStationId"
+                      :arrivalCityId="viewingTrip.arriveStationId"
+                      :cities="stationStore.stations"
                     />
                   </div>
               </div>
@@ -335,57 +346,29 @@ import TopBar from '@/components/TopBar.vue'
 import 'leaflet/dist/leaflet.css'
 import TripMap from '@/components/TripMap.vue'
 import { useUserStore } from '@/stores/useUserStore'
+import { useStationStore } from '@/stores/useStationStore'
 import { useToast } from '@/composables/useToast'
+import TripService from '@/services/TripService'
+import VehiculeService from '@/services/VehiculeService'
+import CompanyService from '@/services/CompanyService'
+import type { Trip, Driver, Vehicle, Station } from '@/types'
+import { Role } from '@/Enum/Role'
 
-interface City {
-  id: number
-  name: string
-  latitude: number
-  longitude: number
-  commune: string
-}
-
-interface Driver {
-  id: number
-  firstName: string
-  lastName: string
-  phone: string
-  email?: string
-  companyId: number
-  licenseNumber: string
-  active: boolean
-}
-
-interface Vehicle {
-  id: number
-  make: string
-  model: string
-  registrationNumber: string
-  capacity: number
-  companyId: number
-  active: boolean
-}
-
-interface Trip {
-  id: number
-  departureCity: number
-  arrivalCity: number
-  departureDateTime: string
-  estimatedArrivalDateTime: string
-  actualArrivalDateTime?: string
-  pricePerSeat: number
-  totalSeats: number
-  availableSeats: number
-  driverId: number
-  vehicleId: number
-  status: 'upcoming' | 'ongoing' | 'completed' | 'cancelled'
-  notes?: string
-}
-
+// Variables réactives pour les données
 const trips = ref<Trip[]>([])
-const cities = ref<City[]>([])
-const drivers = ref<Driver[]>([])
+const drivers = ref<any[]>([])
 const vehicles = ref<Vehicle[]>([])
+const loading = ref(false)
+const userStore = useUserStore()
+const stationStore = useStationStore()
+const pagination = ref({
+  total: 0,
+  page: 1,
+  limit: 10,
+  totalPages: 1
+})
+
+// Variables pour les modales et formulaires
 const showAddForm = ref(false)
 
 // Toast notifications
@@ -398,7 +381,8 @@ const searchQuery = ref('')
 const driverFilter = ref('')
 const statusFilter = ref('')
 
-const formData = ref({
+const formData = ref<Trip>({
+  id: 0,
   departureCity: 0,
   arrivalCity: 0,
   departureDateTime: '',
@@ -409,29 +393,30 @@ const formData = ref({
   availableSeats: 0,
   driverId: 0,
   vehicleId: 0,
-  status: 'upcoming' as 'upcoming' | 'ongoing' | 'completed' | 'cancelled',
-  notes: ''
+  status: 'upcoming',
+  notes: '',
+  companyId: 0,
+  createdAt: '',
+  updatedAt: ''
 })
 
 const isFormValid = computed(() => {
-  return formData.value.departureCity && 
-         formData.value.arrivalCity && 
-         formData.value.departureDateTime && 
-         formData.value.estimatedArrivalDateTime && 
-         formData.value.pricePerSeat > 0 && 
-         formData.value.totalSeats > 0 && 
-         formData.value.availableSeats >= 0 && 
-         formData.value.availableSeats <= formData.value.totalSeats && 
-         formData.value.driverId && 
-         formData.value.vehicleId
+  return formData.value.departureCity &&
+    formData.value.arrivalCity &&
+    formData.value.departureDateTime &&
+    formData.value.estimatedArrivalDateTime &&
+    formData.value.vehicleId &&
+    formData.value.driverId &&
+    formData.value.pricePerSeat > 0 &&
+    formData.value.totalSeats > 0
 })
 
 const filteredTrips = computed(() => {
   return trips.value.filter(trip => {
     // Filtre de recherche
     const searchLower = searchQuery.value.toLowerCase()
-    const departureMatch = getCityName(trip.departureCity).toLowerCase().includes(searchLower)
-    const arrivalMatch = getCityName(trip.arrivalCity).toLowerCase().includes(searchLower)
+    const departureMatch = getStationName(trip.departStationId).toLowerCase().includes(searchLower)
+    const arrivalMatch = getStationName(trip.arriveStationId).toLowerCase().includes(searchLower)
     const driverMatch = getDriverName(trip.driverId).toLowerCase().includes(searchLower)
     
     if (searchQuery.value && !departureMatch && !arrivalMatch && !driverMatch) {
@@ -465,19 +450,19 @@ const formatDateTime = (dateTime: string): string => {
   })
 }
 
-const getCityName = (cityId: number): string => {
-  const city = cities.value.find(c => c.id === cityId)
-  return city ? city.name : 'Inconnue'
+const getStationName = (stationId: number): string => {
+  const station = stationStore.stations.find(s => s.id === stationId)
+  return station ? station.name : 'Station inconnue'
 }
 
 const getDriverName = (driverId: number): string => {
   const driver = drivers.value.find(d => d.id === driverId)
-  return driver ? `${driver.firstName} ${driver.lastName}` : 'Inconnu'
+  return driver ? `${driver.name} ${driver.otherName}` : 'Inconnu'
 }
 
 const getVehicleName = (vehicleId: number): string => {
   const vehicle = vehicles.value.find(v => v.id === vehicleId)
-  return vehicle ? `${vehicle.make} ${vehicle.model} (${vehicle.registrationNumber})` : 'Inconnu'
+  return vehicle ? `${vehicle.model} ${vehicle.type}` : 'Inconnu'
 }
 
 const getStatusLabel = (status: string): string => {
@@ -509,6 +494,7 @@ const closeForm = () => {
 
 const resetForm = () => {
   formData.value = {
+    id: 0,
     departureCity: 0,
     arrivalCity: 0,
     departureDateTime: '',
@@ -520,22 +506,116 @@ const resetForm = () => {
     driverId: 0,
     vehicleId: 0,
     status: 'upcoming',
-    notes: ''
+    notes: '',
+    companyId: 0,
+    createdAt: '',
+    updatedAt: ''
   }
 }
 
-const updateTotalSeats = () => {
-  const vehicle = vehicles.value.find(v => v.id === formData.value.vehicleId)
-  if (vehicle) {
-    formData.value.totalSeats = vehicle.capacity
-    formData.value.availableSeats = vehicle.capacity
+// Store utilisateur (déjà déclaré plus haut)
+
+// Fonctions de chargement des données
+const loadTrips = async () => {
+  loading.value = true
+  try {
+    const userRole = userStore.userRole
+    const companyId = userStore.companyId
+    
+    let result
+    if (userRole === 'admin') {
+      // Admin peut voir tous les trajets
+      result = await TripService.getTrips(pagination.value.page, pagination.value.limit)
+    } else {
+      // Utilisateur d'entreprise ne voit que les trajets de son entreprise
+      result = await TripService.getTripsByCompany(companyId, pagination.value.page, pagination.value.limit)
+    }
+    
+    trips.value = result.trips
+    pagination.value = result.pagination
+  } catch (error) {
+    console.error('Erreur lors du chargement des trajets:', error)
+    error('Erreur lors du chargement des trajets')
+  } finally {
+    loading.value = false
+  }
+}
+
+const loadDrivers = async () => {
+  try {
+    const userRole = userStore.userRole
+    const companyId = userStore.decodedCompanyId || userStore.companyId
+    
+    if (userRole === Role.Manager || userRole === Role.Admin) {
+
+      drivers.value = (await CompanyService.getCompanyEmployees(parseInt(companyId))).employees.filter(driver => driver.role === Role.Driver)
+    } else {
+      // Pour les entreprises, récupérer uniquement les chauffeurs de l'entreprise
+      const companyId = userStore.decodedCompanyId || userStore.companyId
+      if (companyId) {
+        drivers.value = (await CompanyService.getCompanyEmployees(parseInt(companyId))).employees.filter(driver => driver.role === Role.Driver)
+      } else {
+        console.error('Aucune entreprise associée à l\'utilisateur')
+        drivers.value = []
+      }
+    }
+    console.log('Driver valeur ======>',drivers.value)
+  } catch (error) {
+    console.error('Erreur lors du chargement des chauffeurs:', error)
+    drivers.value = []
+  }
+}
+
+const loadVehicles = async () => {
+  try {
+    const userRole = userStore.userRole
+    const companyId = userStore.companyId
+    
+    let result
+    if (userRole === 'admin') {
+      result = await VehiculeService.getVehicles(1, 100) // Charger tous les véhicules pour admin
+    } else {
+      result = await VehiculeService.getVehiclesByCompany(companyId, 1, 100)
+    }
+    
+    vehicles.value = result.vehicles
+  } catch (error) {
+    console.error('Erreur lors du chargement des véhicules:', error)
   }
 }
 
+const loadStations = async () => {
+  try {
+    await stationStore.fetchStations()
+  } catch (error) {
+    console.error('Erreur lors du chargement des stations:', error)
+  }
+}
+
+// Fonction updateTotalSeats supprimée car non nécessaire avec la nouvelle structure
+
 const editTrip = (trip: Trip) => {
   editingTrip.value = trip
   viewingTrip.value = null
-  formData.value = { ...trip }
+  formData.value = {
+    id: trip.id,
+    // Mapper les données du backend vers les propriétés du formulaire
+    departureCity: trip.departStationId,
+    arrivalCity: trip.arriveStationId,
+    departureDateTime: trip.departAt,
+    estimatedArrivalDateTime: trip.arriveAt,
+    actualArrivalDateTime: trip.actualArrivalDateTime || '',
+    pricePerSeat: trip.pricePerSeat || 0,
+    totalSeats: trip.totalSeats || 0,
+    availableSeats: trip.availableSeats || 0,
+    driverId: trip.driverId || 0,
+    vehicleId: trip.vehiculeId,
+    status: trip.status || 'upcoming',
+    notes: trip.notes || '',
+    companyId: trip.companyId || 0,
+    createdAt: trip.createdAt || '',
+    updatedAt: trip.updatedAt || ''
+  }
   showAddForm.value = true
 }
 
@@ -552,16 +632,14 @@ const deleteTrip = async () => {
   if (!tripToDelete.value) return
   
   try {
-    // Dans un environnement réel, vous feriez un appel API ici
-    // await fetch(`http://localhost:3001/api/trips/${tripToDelete.value.id}`, {
-    //   method: 'DELETE'
-    // })
-    
-    // Supprimer le voyage de la liste locale
-    trips.value = trips.value.filter(t => t.id !== tripToDelete.value?.id)
-    
-    // Afficher une notification de succès
-    success('Voyage supprimé avec succès')
+    const result = await TripService.deleteTrip(tripToDelete.value.id)
+    if (result) {
+      // Recharger la liste des trajets
+      await loadTrips()
+      success('Voyage supprimé avec succès')
+    } else {
+      error('Erreur lors de la suppression du voyage')
+    }
   } catch (error) {
     console.error('Erreur lors de la suppression:', error)
     error('Erreur lors de la suppression du voyage')
@@ -572,62 +650,113 @@ const deleteTrip = async () => {
 }
 
 const saveTrip = async () => {
-  if (!isFormValid.value) return
+  console.log('=== DEBUT saveTrip ===')
+  console.log('Données du formulaire:', formData.value)
+  
+  // Vérification détaillée de la validité
+  console.log('Validation détaillée:')
+  console.log('- departureCity:', formData.value.departureCity, 'valide:', !!formData.value.departureCity)
+  console.log('- arrivalCity:', formData.value.arrivalCity, 'valide:', !!formData.value.arrivalCity)
+  console.log('- departureDateTime:', formData.value.departureDateTime, 'valide:', !!formData.value.departureDateTime)
+  console.log('- estimatedArrivalDateTime:', formData.value.estimatedArrivalDateTime, 'valide:', !!formData.value.estimatedArrivalDateTime)
+  console.log('- pricePerSeat:', formData.value.pricePerSeat, 'valide:', formData.value.pricePerSeat > 0)
+  console.log('- totalSeats:', formData.value.totalSeats, 'valide:', formData.value.totalSeats > 0)
+  console.log('- availableSeats:', formData.value.availableSeats, 'valide:', formData.value.availableSeats >= 0 && formData.value.availableSeats <= formData.value.totalSeats)
+  console.log('- driverId:', formData.value.driverId, 'valide:', !!formData.value.driverId)
+  console.log('- vehicleId:', formData.value.vehicleId, 'valide:', !!formData.value.vehicleId)
+  
+  // Validation spécifique pour les champs requis par l'API
+  if (!formData.value.departureCity || !formData.value.arrivalCity) {
+    console.log('Erreur: Ville de départ ou d\'arrivée manquante')
+    error('Veuillez sélectionner les villes de départ et d\'arrivée')
+    return
+  }
   
+  if (!formData.value.departureDateTime || !formData.value.estimatedArrivalDateTime) {
+    console.log('Erreur: Date de départ ou d\'arrivée manquante')
+    error('Veuillez renseigner les dates de départ et d\'arrivée')
+    return
+  }
+  
+  if (!formData.value.vehicleId) {
+    console.log('Erreur: Véhicule non sélectionné')
+    error('Veuillez sélectionner un véhicule')
+    return
+  }
+  
+  if (!formData.value.pricePerSeat || formData.value.pricePerSeat <= 0) {
+    console.log('Erreur: Prix par place invalide')
+    error('Veuillez renseigner un prix par place valide')
+    return
+  }
+  
+  if (!formData.value.totalSeats || formData.value.totalSeats <= 0) {
+    console.log('Erreur: Nombre de places invalide')
+    error('Veuillez renseigner un nombre de places valide')
+    return
+  }
+  
+  if (!formData.value.driverId) {
+    console.log('Erreur: Chauffeur non sélectionné')
+    error('Veuillez sélectionner un chauffeur')
+    return
+  }
+
   try {
+    // Pour un nouveau voyage, initialiser les places disponibles au nombre total de places
+    if (!editingTrip.value) {
+      formData.value.availableSeats = formData.value.totalSeats
+    }
+    
+    // Mapper les données du formulaire vers la structure attendue par l'API (CreateTrajetDto)
+    const departStationId = Number(formData.value.departureCity)
+    const arriveStationId = Number(formData.value.arrivalCity)
+    const vehiculeId = Number(formData.value.vehicleId)
+    
+    // Validation des nombres
+    if (isNaN(departStationId) || isNaN(arriveStationId) || isNaN(vehiculeId)) {
+      console.log('Erreur: Valeurs invalides pour les IDs')
+      error('Erreur dans les données du formulaire. Veuillez réessayer.')
+      return
+    }
+    
+    const tripData = {
+      departAt: formData.value.departureDateTime,
+      arriveAt: formData.value.estimatedArrivalDateTime,
+      departStationId,
+      arriveStationId,
+      vehiculeId,
+      pricePerSeat: Number(formData.value.pricePerSeat)
+    }
+    
+    console.log('Données pour l\'API:', tripData)
+
     if (editingTrip.value) {
+      console.log('Mode édition - ID:', editingTrip.value.id)
       // Mise à jour d'un voyage existant
-      // Dans un environnement réel, vous feriez un appel API ici
-      // await fetch(`http://localhost:3001/api/trips/${editingTrip.value.id}`, {
-      //   method: 'PUT',
-      //   headers: {
-      //     'Content-Type': 'application/json'
-      //   },
-      //   body: JSON.stringify(formData.value)
-      // })
-      
-      // Mettre à jour le voyage dans la liste locale
-      const index = trips.value.findIndex(t => t.id === editingTrip.value?.id)
-      if (index !== -1) {
-        trips.value[index] = {
-          ...trips.value[index],
-          ...formData.value,
-          id: editingTrip.value.id
-        }
-      }
-      
-      // Afficher une notification de succès
+      const result = await TripService.updateTrip(editingTrip.value.id, tripData)
+      console.log('Résultat updateTrip:', result)
       success('Voyage mis à jour avec succès')
+      await loadTrips() // Recharger la liste
     } else {
-      // Ajout d'un nouveau voyage
-      // Dans un environnement réel, vous feriez un appel API ici
-      // const response = await fetch('http://localhost:3001/api/trips', {
-      //   method: 'POST',
-      //   headers: {
-      //     'Content-Type': 'application/json'
-      //   },
-      //   body: JSON.stringify(formData.value)
-      // })
-      // const data = await response.json()
-      
-      // Simuler l'ajout d'un nouveau voyage avec un ID généré
-      const newTrip: Trip = {
-        ...formData.value,
-        id: Math.max(0, ...trips.value.map(t => t.id)) + 1
-      }
-      
-      // Ajouter le voyage à la liste locale
-      trips.value.push(newTrip)
-      
-      // Afficher une notification de succès
-      success('Voyage ajouté avec succès')
+      console.log('Mode création')
+      // Création d'un nouveau voyage
+      const result = await TripService.createTrip(tripData)
+      console.log('Résultat createTrip:', result)
+      success('Voyage créé avec succès')
+      await loadTrips() // Recharger la liste
     }
     
-    // Fermer le formulaire
-    closeForm()
-  } catch (error) {
-    console.error('Erreur lors de l\'enregistrement:', error)
-    error('Erreur lors de l\'enregistrement du voyage')
+    // Réinitialiser le formulaire
+    resetForm()
+    showAddForm.value = false
+    editingTrip.value = null
+    
+    console.log('=== FIN saveTrip (succès) ===')
+  } catch (err) {
+    console.error('Erreur lors de la sauvegarde:', err)
+    error('Erreur lors de la sauvegarde du voyage')
+    console.log('=== FIN saveTrip (erreur) ===')
   }
 }
 
@@ -647,135 +776,43 @@ const handleMessages = () => {
 const handleUserMenu = () => {
   // Gérer l'affichage du menu utilisateur
 }
-// Charger les données
+// Initialisation des données
 onMounted(async () => {
-  try {
-    // Charger les villes depuis le fichier JSON
-    const citiesResponse = await fetch('/datas/civ.cities.json')
-    if (!citiesResponse.ok) {
-        throw new Error('Erreur HTTP: ' + citiesResponse.status)
-    }
+  await loadStations()
+  await loadDrivers()
+  await loadVehicles()
+  await loadTrips()
+})
 
-    console.log('citiesResponse:', citiesResponse)
-    cities.value = await citiesResponse.json()
-    console.log('citiesResponse:', cities.value)
-    console.log('citiesResponse:', await cities.value)
-    
-    // Simuler le chargement des chauffeurs
-    drivers.value = [
-      {
-        id: 1,
-        firstName: 'Jean',
-        lastName: 'Dupont',
-        phone: '+33 6 12 34 56 78',
-        email: 'jean.dupont@example.com',
-        companyId: 1,
-        licenseNumber: 'DRV-12345',
-        active: true
-      },
-      {
-        id: 2,
-        firstName: 'Marie',
-        lastName: 'Laurent',
-        phone: '+33 6 23 45 67 89',
-        email: 'marie.laurent@example.com',
-        companyId: 2,
-        licenseNumber: 'DRV-67890',
-        active: true
-      },
-      {
-        id: 3,
-        firstName: 'Ahmed',
-        lastName: 'Koné',
-        phone: '+225 07 12 34 56',
-        email: 'ahmed.kone@example.com',
-        companyId: 3,
-        licenseNumber: 'DRV-54321',
-        active: false
-      }
-    ]
-    
-    // // Simuler le chargement des véhicules
-    vehicles.value = [
-      {
-        id: 1,
-        make: 'Toyota',
-        model: 'Hiace',
-        registrationNumber: 'AB-123-CD',
-        capacity: 15,
-        companyId: 1,
-        active: true
-      },
-      {
-        id: 2,
-        make: 'Mercedes',
-        model: 'Sprinter',
-        registrationNumber: 'EF-456-GH',
-        capacity: 22,
-        companyId: 2,
-        active: true
-      },
-      {
-        id: 3,
-        make: 'Renault',
-        model: 'Master',
-        registrationNumber: 'IJ-789-KL',
-        capacity: 18,
-        companyId: 3,
-        active: true
+// Watchers pour la pagination
+watch(() => pagination.value.page, () => {
+  loadTrips()
+})
+
+watch(() => pagination.value.limit, () => {
+  pagination.value.page = 1
+  loadTrips()
+})
+
+// Watcher pour mettre à jour les places disponibles lors de la création d'un nouveau voyage
+watch(() => formData.value.totalSeats, (newTotalSeats) => {
+  // Seulement pour un nouveau voyage (pas en mode édition)
+  if (!editingTrip.value && newTotalSeats > 0) {
+    formData.value.availableSeats = newTotalSeats
+  }
+})
+
+// Watcher pour mettre à jour automatiquement le nombre total de places quand un véhicule est sélectionné
+watch(() => formData.value.vehicleId, (newVehicleId) => {
+  if (newVehicleId) {
+    const selectedVehicle = vehicles.value.find(v => v.id === newVehicleId)
+    if (selectedVehicle) {
+      formData.value.totalSeats = selectedVehicle.number_places
+      // Si c'est un nouveau voyage, mettre à jour aussi les places disponibles
+      if (!editingTrip.value) {
+        formData.value.availableSeats = selectedVehicle.number_places
       }
-    ]
-    
-    // Simuler le chargement des voyages
-    setTimeout(() => {
-      trips.value = [
-        {
-          id: 1,
-          departureCity: 1, // Abidjan
-          arrivalCity: 2, // Bouaké
-          departureDateTime: '2023-06-15T08:00:00',
-          estimatedArrivalDateTime: '2023-06-15T14:00:00',
-          actualArrivalDateTime: '2023-06-15T14:30:00',
-          pricePerSeat: 5000,
-          totalSeats: 15,
-          availableSeats: 3,
-          driverId: 1,
-          vehicleId: 1,
-          status: 'completed',
-          notes: 'Voyage régulier hebdomadaire'
-        },
-        {
-          id: 2,
-          departureCity: 2, // Bouaké
-          arrivalCity: 3, // Yamoussoukro
-          departureDateTime: '2023-06-20T10:00:00',
-          estimatedArrivalDateTime: '2023-06-20T12:30:00',
-          pricePerSeat: 3000,
-          totalSeats: 22,
-          availableSeats: 8,
-          driverId: 2,
-          vehicleId: 2,
-          status: 'upcoming',
-          notes: 'Voyage express'
-        },
-        {
-          id: 3,
-          departureCity: 1, // Abidjan
-          arrivalCity: 3, // Yamoussoukro
-          departureDateTime: '2023-06-18T07:30:00',
-          estimatedArrivalDateTime: '2023-06-18T11:30:00',
-          actualArrivalDateTime: '2023-06-18T11:45:00',
-          pricePerSeat: 4500,
-          totalSeats: 18,
-          availableSeats: 0,
-          driverId: 3,
-          vehicleId: 3,
-          status: 'completed'
-        }
-      ]
-    }, 500)
-  } catch (error) {
-    console.error('Erreur lors du chargement des données:', error)
+    }
   }
 })
 </script>
@@ -952,10 +989,68 @@ onMounted(async () => {
   background-color: #f8f9fc;
 }
 
+.loading-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 60px 20px;
+  color: #666;
+}
+
+.loading-spinner {
+  width: 40px;
+  height: 40px;
+  border: 4px solid #f3f3f3;
+  border-top: 4px solid #007bff;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
+  margin-bottom: 16px;
+}
+
+@keyframes spin {
+  0% { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+
 .no-data {
-  padding: 40px;
   text-align: center;
-  color: #858796;
+  padding: 60px 20px;
+  color: #666;
+}
+
+.no-data-icon {
+  font-size: 48px;
+  color: #ddd;
+  margin-bottom: 16px;
+}
+
+.no-data h3 {
+  margin: 0 0 8px 0;
+  color: #333;
+  font-weight: 600;
+}
+
+.no-data p {
+  margin: 0 0 20px 0;
+  color: #666;
+  font-size: 14px;
+}
+
+.no-data .add-button {
+  background: #007bff;
+  color: white;
+  border: none;
+  padding: 12px 24px;
+  border-radius: 6px;
+  cursor: pointer;
+  font-size: 14px;
+  font-weight: 500;
+  transition: background-color 0.2s;
+}
+
+.no-data .add-button:hover {
+  background: #0056b3;
 }
 
 /* Modal styles */
@@ -1047,6 +1142,10 @@ onMounted(async () => {
   margin-bottom: 15px;
 }
 
+.form-group.full-width {
+  flex: 1 1 100%;
+}
+
 .form-group label {
   margin-bottom: 5px;
   font-size: 14px;

+ 143 - 118
app_transport_front/src/views/VehicleView.vue

@@ -68,11 +68,18 @@
 
         <!-- Tableau des véhicules -->
         <div class="vehicle-table-container">
-          <table class="vehicle-table" v-if="filteredVehicles.length > 0">
+          <!-- Indicateur de chargement -->
+          <div v-if="loading" class="loading-state">
+            <i class="fas fa-spinner fa-spin"></i>
+            <p>Chargement des véhicules...</p>
+          </div>
+          
+          <!-- Tableau des véhicules -->
+          <table class="vehicle-table" v-else-if="filteredVehicles.length > 0">
             <thead>
               <tr>
                 <th>Image</th>
-                <th>Marque/Modèle</th>
+                <th>Modèle</th>
                 <th>Immatriculation</th>
                 <th>Type</th>
                 <th>Entreprise</th>
@@ -83,10 +90,10 @@
             <tbody>
               <tr v-for="vehicle in filteredVehicles" :key="vehicle.id">
                 <td class="image-cell">
-                  <div class="vehicle-image" :style="{ backgroundImage: `url(${vehicle.image || '/assets/images/default-vehicle.png'})` }"></div>
+                  <div class="vehicle-image" :style="{ backgroundImage: `url('/assets/images/default-vehicle.png')` }"></div>
                 </td>
-                <td>{{ vehicle.brand }} {{ vehicle.model }}</td>
-                <td>{{ vehicle.licensePlate }}</td>
+                <td>{{ vehicle.model }}</td>
+                <td>{{ vehicle.immatriculation }}</td>
                 <td>{{ getVehicleTypeName(vehicle.type) }}</td>
                 <td>{{ getCompanyName(vehicle.companyId) }}</td>
                 <td>
@@ -108,10 +115,13 @@
               </tr>
             </tbody>
           </table>
+          
+          <!-- État vide -->
           <div class="empty-state" v-else>
             <i class="fas fa-truck-moving empty-icon"></i>
-            <p>Aucun véhicule trouvé</p>
-            <button class="add-button" @click="showAddForm = true">
+            <p v-if="vehicles.length === 0">Aucun véhicule enregistré</p>
+            <p v-else>Aucun véhicule ne correspond aux critères de recherche</p>
+            <button class="add-button" @click="showAddForm = true" v-if="vehicles.length === 0">
               Ajouter un véhicule
             </button>
           </div>
@@ -136,7 +146,7 @@
               </button>
             </div>
             <div class="p-5">
-              <p>Êtes-vous sûr de vouloir supprimer le véhicule <strong>{{ deletingVehicle?.brand }} {{ deletingVehicle?.model }}</strong> ({{ deletingVehicle?.licensePlate }}) ?</p>
+              <p>Êtes-vous sûr de vouloir supprimer le véhicule <strong>{{ deletingVehicle?.model }}</strong> ({{ deletingVehicle?.immatriculation }}) ?</p>
               <p class="text-red-600 text-sm"><i class="fas fa-exclamation-triangle"></i> Cette action est irréversible.</p>
             </div>
             <div class="py-4 px-5 border-t border-gray-200 flex justify-end gap-2.5">
@@ -157,10 +167,10 @@
     </div>
     <div class="p-5 vehicle-details">
               <div class="vehicle-profile">
-                <div class="vehicle-profile-image" :style="{ backgroundImage: `url(${viewingVehicle.image || '/assets/images/default-vehicle.png'})` }"></div>
+                <div class="vehicle-profile-image" :style="{ backgroundImage: `url('/assets/images/default-vehicle.png')` }"></div>
                 <div class="vehicle-profile-info">
-                  <h3>{{ viewingVehicle.brand }} {{ viewingVehicle.model }}</h3>
-                  <p class="license-plate">{{ viewingVehicle.licensePlate }}</p>
+                  <h3>{{ viewingVehicle.model }}</h3>
+                  <p class="license-plate">{{ viewingVehicle.immatriculation }}</p>
                   <p class="company-name">{{ getCompanyName(viewingVehicle.companyId) }}</p>
                   <span :class="['status-badge', getStatusClass(viewingVehicle.status)]">
                     {{ getStatusName(viewingVehicle.status) }}
@@ -170,13 +180,9 @@
 
               <div class="vehicle-info-section">
                 <h4>Informations générales</h4>
-                <div class="info-item">
-                  <i class="fas fa-calendar-alt"></i>
-                  <span>Année: {{ viewingVehicle.year }}</span>
-                </div>
                 <div class="info-item">
                   <i class="fas fa-users"></i>
-                  <span>Capacité: {{ viewingVehicle.capacity || 'Non spécifiée' }}</span>
+                  <span>Capacité: {{ viewingVehicle.number_places || 'Non spécifiée' }} places</span>
                 </div>
                 <div class="info-item" v-if="viewingVehicle.color">
                   <i class="fas fa-palette"></i>
@@ -186,6 +192,10 @@
                   <i class="fas fa-tag"></i>
                   <span>Type: {{ getVehicleTypeName(viewingVehicle.type) }}</span>
                 </div>
+                <div class="info-item">
+                  <i class="fas fa-building"></i>
+                  <span>Entreprise: {{ getCompanyName(viewingVehicle.companyId) }}</span>
+                </div>
               </div>
 
               <div class="vehicle-info-section" v-if="viewingVehicle.notes">
@@ -224,93 +234,30 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, onMounted } from 'vue'
+import { ref, computed, onMounted, watch } from 'vue'
 import Sidebar from '@/components/Sidebar.vue'
 import TopBar from '@/components/TopBar.vue'
 import VehicleModal from '@/components/VehicleModal.vue'
 import { useUserStore } from '@/stores/useUserStore'
 import { Role } from '@/Enum/Role'
 import { useVehiculeStore, Vehicule } from '@/stores/useVehiculeStore'
+import VehiculeService from '@/services/VehiculeService'
+import CompanyService from '@/services/CompanyService'
+import type { Vehicle, Company } from '@/types'
+
+// Variables réactives pour les données
+const vehicles = ref<Vehicle[]>([])
+const companies = ref<Company[]>([])
+const loading = ref(false)
+const pagination = ref({
+  total: 0,
+  page: 1,
+  limit: 10,
+  totalPages: 1
+})
 
-// Données fictives pour la démo
-const vehicles = ref([
-  {
-    id: 1,
-    brand: 'Toyota',
-    model: 'Hiace',
-    licensePlate: 'AB-123-CD',
-    year: 2020,
-    type: 'van',
-    companyId: 1,
-    capacity: 8,
-    color: 'Blanc',
-    status: 'active',
-    image: 'https://example.com/images/vehicle1.jpg',
-    notes: 'Véhicule en bon état, entretien régulier'
-  },
-  {
-    id: 2,
-    brand: 'Mercedes',
-    model: 'Sprinter',
-    licensePlate: 'EF-456-GH',
-    year: 2019,
-    type: 'van',
-    companyId: 2,
-    capacity: 12,
-    color: 'Gris',
-    status: 'active',
-    image: null,
-    notes: ''
-  },
-  {
-    id: 3,
-    brand: 'Renault',
-    model: 'Master',
-    licensePlate: 'IJ-789-KL',
-    year: 2021,
-    type: 'van',
-    companyId: 1,
-    capacity: 9,
-    color: 'Blanc',
-    status: 'maintenance',
-    image: null,
-    notes: 'En maintenance pour révision des 50 000 km'
-  },
-  {
-    id: 4,
-    brand: 'Iveco',
-    model: 'Daily',
-    licensePlate: 'MN-012-OP',
-    year: 2018,
-    type: 'truck',
-    companyId: 3,
-    capacity: 3,
-    color: 'Bleu',
-    status: 'inactive',
-    image: null,
-    notes: 'Véhicule hors service temporairement'
-  },
-  {
-    id: 5,
-    brand: 'Volkswagen',
-    model: 'Crafter',
-    licensePlate: 'QR-345-ST',
-    year: 2022,
-    type: 'van',
-    companyId: 2,
-    capacity: 7,
-    color: 'Noir',
-    status: 'active',
-    image: null,
-    notes: ''
-  }
-])
-
-const companies = ref([
-  { id: 1, name: 'Transport Express' },
-  { id: 2, name: 'Mobility Solutions' },
-  { id: 3, name: 'Cargo Logistics' }
-])
+// Store utilisateur pour récupérer l'entreprise connectée
+const userStore = useUserStore()
 
 // États pour les filtres
 const searchQuery = ref('')
@@ -341,6 +288,41 @@ const formData = ref({
 })
 const vehicleStore = useVehiculeStore()
 
+// Fonctions pour charger les données
+const loadVehicles = async () => {
+  try {
+    loading.value = true
+    
+    // Si l'utilisateur est admin, charger tous les véhicules
+    // Sinon, charger seulement les véhicules de son entreprise
+    let result
+    if (userStore.userRole === Role.Admin) {
+      result = await VehiculeService.getVehicles(pagination.value.page, pagination.value.limit)
+    } else {
+      // Récupérer l'ID de l'entreprise de l'utilisateur connecté
+      const companyId = userStore.companyId || 1 // Fallback temporaire
+      result = await VehiculeService.getVehiclesByCompany(companyId, pagination.value.page, pagination.value.limit)
+      console.log('Véhicules de l\'entreprise:', result)
+    }
+    
+    vehicles.value = result.vehicles
+    pagination.value = result.pagination
+  } catch (error) {
+    console.error('Erreur lors du chargement des véhicules:', error)
+  } finally {
+    loading.value = false
+  }
+}
+
+const loadCompanies = async () => {
+  try {
+    const result = await CompanyService.getCompanies(1, 100) // Charger toutes les entreprises
+    companies.value = result.companies
+  } catch (error) {
+    console.error('Erreur lors du chargement des entreprises:', error)
+  }
+}
+
 const createNewVehicle = async (vehicule) => {
   try {
     // Transformation des données pour correspondre au format attendu par le backend
@@ -350,7 +332,7 @@ const createNewVehicle = async (vehicule) => {
       type: vehicule.type || '',
       immatriculation: vehicule.licensePlate || '',
       model: `${vehicule.model}` || '',
-      companyId: vehicule.companyId || 0,
+      companyId: vehicule.companyId || userStore.companyId || 1,
       driverId: 0
     }
     
@@ -359,6 +341,8 @@ const createNewVehicle = async (vehicule) => {
     const newVehicle = await vehicleStore.createVehicle(vehiculeData)
     
     console.log('Véhicule créé avec succès:', newVehicle)
+    // Recharger la liste après création
+    await loadVehicles()
     return newVehicle
   } catch (error) {
     console.error('Erreur lors de la création du véhicule:', error)
@@ -370,11 +354,11 @@ const filteredVehicles = computed(() => {
   return vehicles.value.filter(vehicle => {
     // Filtre par recherche
     const searchMatch = 
-      vehicle.brand.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
       vehicle.model.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
-      vehicle.licensePlate.toLowerCase().includes(searchQuery.value.toLowerCase())
+      vehicle.immatriculation.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
+      vehicle.type.toLowerCase().includes(searchQuery.value.toLowerCase())
     
-    // Filtre par entreprise
+    // Filtre par entreprise (seulement pour les admins)
     const companyMatch = !companyFilter.value || vehicle.companyId.toString() === companyFilter.value
     
     // Filtre par type
@@ -474,35 +458,59 @@ const closeForm = () => {
 const saveVehicle = async (vehicleData) => {
   if (editingVehicle.value) {
     // Mise à jour d'un véhicule existant
-    const index = vehicles.value.findIndex(v => v.id === editingVehicle.value.id)
-    if (index !== -1) {
-      vehicles.value[index] = { ...vehicleData }
+    try {
+      const vehiculeData: Partial<Vehicle> = {
+        number_places: vehicleData.capacity || 0,
+        color: vehicleData.color || '',
+        type: vehicleData.type || '',
+        immatriculation: vehicleData.licensePlate || '',
+        model: vehicleData.model || '',
+        companyId: vehicleData.companyId || userStore.companyId || 1,
+        status: vehicleData.status || 'active'
+      }
+      
+      await VehiculeService.updateVehicle(editingVehicle.value.id, vehiculeData)
+      await loadVehicles() // Recharger la liste
+    } catch (error) {
+      console.error('Erreur lors de la mise à jour du véhicule:', error)
     }
   } else {
     // Ajout d'un nouveau véhicule
-    // Appel api pour la creation du véhicule
-    console.log('formData.value',vehicleData)
-    const dbVehicule = await createNewVehicle(vehicleData)
-    const newId = Math.max(0, ...vehicles.value.map(v => v.id)) + 1
-    vehicles.value.push({
-      ...vehicleData,
-      id: newId
-    })
-
-    vehicles.value.push({
-      ...dbVehicule,
-    })
+    await createNewVehicle(vehicleData)
   }
   closeForm()
 }
 
-const deleteVehicle = () => {
+const deleteVehicle = async () => {
   if (deletingVehicle.value) {
-    vehicles.value = vehicles.value.filter(v => v.id !== deletingVehicle.value.id)
-    deletingVehicle.value = null
+    try {
+      await VehiculeService.deleteVehicle(deletingVehicle.value.id)
+      await loadVehicles() // Recharger la liste
+      deletingVehicle.value = null
+    } catch (error) {
+      console.error('Erreur lors de la suppression du véhicule:', error)
+    }
   }
 }
 
+// Initialisation des données au montage du composant
+onMounted(async () => {
+  await Promise.all([
+    loadVehicles(),
+    loadCompanies()
+  ])
+})
+
+// Watchers pour recharger les données
+watch([searchQuery, companyFilter, typeFilter, statusFilter], () => {
+  // Le filtrage se fait côté client via computed
+  // Pas besoin de recharger depuis l'API
+})
+
+watch(() => pagination.value.page, () => {
+  loadVehicles()
+})
+
 // Méthodes pour les événements du TopBar
 const handleSearch = (query) => {
   searchQuery.value = query
@@ -704,6 +712,23 @@ const handleUserMenu = () => {
   color: #dc3545;
 }
 
+.loading-state {
+  text-align: center;
+  padding: 60px 20px;
+  color: #666;
+}
+
+.loading-state i {
+  font-size: 32px;
+  color: #0099cc;
+  margin-bottom: 15px;
+}
+
+.loading-state p {
+  font-size: 16px;
+  margin: 0;
+}
+
 .empty-state {
   padding: 50px 20px;
   text-align: center;

+ 8 - 0
node_app/prisma/migrations/20250604172111_add_company_employee_relation/migration.sql

@@ -0,0 +1,8 @@
+-- AlterTable
+ALTER TABLE `User` ADD COLUMN `companyId` INTEGER NULL;
+
+-- CreateIndex
+CREATE INDEX `User_companyId_fkey` ON `User`(`companyId`);
+
+-- AddForeignKey
+ALTER TABLE `User` ADD CONSTRAINT `User_companyId_fkey` FOREIGN KEY (`companyId`) REFERENCES `Company`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;

+ 2 - 0
node_app/prisma/migrations/20250605093215_add_price_per_seat_with_default/migration.sql

@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE `Trajet` ADD COLUMN `pricePerSeat` DOUBLE NOT NULL DEFAULT 0.0;

+ 12 - 0
node_app/prisma/migrations/20250605154937_add_company_relation_and_place_availability/migration.sql

@@ -0,0 +1,12 @@
+-- AlterTable
+ALTER TABLE `Place` ADD COLUMN `isAvailable` BOOLEAN NOT NULL DEFAULT true,
+    ADD COLUMN `occupiedAt` DATETIME(3) NULL;
+
+-- AlterTable
+ALTER TABLE `Trajet` ADD COLUMN `companyId` INTEGER NULL;
+
+-- CreateIndex
+CREATE INDEX `Trajet_companyId_fkey` ON `Trajet`(`companyId`);
+
+-- AddForeignKey
+ALTER TABLE `Trajet` ADD CONSTRAINT `Trajet_companyId_fkey` FOREIGN KEY (`companyId`) REFERENCES `Company`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;

+ 12 - 0
node_app/prisma/schema.prisma

@@ -29,9 +29,11 @@ model Company {
   address          String
   managerId        Int              @unique
   manager          User             @relation("CompanyManager", fields: [managerId], references: [id])
+  employees        User[]           @relation("CompanyEmployees")
   drivers          Driver[]         @relation("CompanyDrivers")
   stationCompanies StationCompany[]
   vehicules        Vehicule[]       @relation("CompanyVehicules")
+  trajets          Trajet[]         @relation("CompanyTrajets") // Relation inverse avec Trajet
 }
 
 model StationCompany {
@@ -71,11 +73,15 @@ model User {
   email            String           @unique
   password         String
   role             UserRole         @default(USER)
+  companyId        Int?             // Nouveau champ pour la relation employé-entreprise
+  company          Company?         @relation("CompanyEmployees", fields: [companyId], references: [id])
   managedCompany   Company?         @relation("CompanyManager")
   driver           Driver?          @relation("UserDriver")
   reservations     Reservation[]    @relation("UserReservations")
   stationCompanies StationCompany[] @relation("UserStationChief")
   transactions     Transaction[]    @relation("UserTransactions")
+
+  @@index([companyId], map: "User_companyId_fkey")
 }
 
 model Vehicule {
@@ -101,6 +107,8 @@ model Place {
   id           Int           @id @default(autoincrement())
   number       Int
   vehiculeId   Int
+  isAvailable  Boolean       @default(true) // Indique si la place est disponible
+  occupiedAt   DateTime?     // Date d'occupation si la place est occupée
   vehicule     Vehicule      @relation("VehiculePlaces", fields: [vehiculeId], references: [id])
   reservations Reservation[] @relation("PlaceReservations")
 
@@ -116,12 +124,16 @@ model Trajet {
   departStationId Int           // Nouveau champ pour l'ID de la station de départ
   arriveStationId Int           // Nouveau champ pour l'ID de la station d'arrivée
   vehiculeId      Int
+  companyId       Int?          // Relation directe avec Company (optionnel temporairement)
+  pricePerSeat    Float         @default(0.0) // Prix par place
   reservations    Reservation[] @relation("TrajetReservations")
   vehicule        Vehicule      @relation("VehiculeTrajets", fields: [vehiculeId], references: [id])
+  company         Company?      @relation("CompanyTrajets", fields: [companyId], references: [id]) // Relation directe avec Company
   departStation   Station       @relation("TrajetsDepartingFrom", fields: [departStationId], references: [id]) // Relation vers la station de départ
   arriveStation   Station       @relation("TrajetsArrivingAt", fields: [arriveStationId], references: [id])    // Relation vers la station d'arrivée
 
   @@index([vehiculeId], map: "Trajet_vehiculeId_fkey")
+  @@index([companyId], map: "Trajet_companyId_fkey") // Index pour la relation avec Company
   @@index([departStationId], map: "Trajet_departStationId_fkey") // Index pour la nouvelle clé étrangère
   @@index([arriveStationId], map: "Trajet_arriveStationId_fkey") // Index pour la nouvelle clé étrangère
 }

+ 48 - 1
node_app/src/app.module.ts

@@ -29,6 +29,7 @@ import { CreateTrajetUseCase } from './application/use-cases/trajet/create-traje
 import { UpdateTrajetUseCase } from './application/use-cases/trajet/update-trajet.use-case';
 import { FindAllTrajetsUseCase } from './application/use-cases/trajet/find-all-trajets.use-case'; // Ajoutez cette ligne
 import { FindTrajetByIdUseCase } from './application/use-cases/trajet/find-trajet-by-id.use-case'; // Ajoutez cette ligne
+import { FindTrajetsByCompanyUseCase } from './application/use-cases/trajet/find-trajets-by-company.use-case';
 import { DeleteTrajetUseCase } from './application/use-cases/trajet/delete-trajet.use-case'; // Ajoutez cette ligne
 import { TrajetMapper } from './infrastructure/mappers/trajet.mapper';
 import { TrajetRepository } from './infrastructure/database/repositories/trajet.repository';
@@ -42,6 +43,7 @@ import { APP_GUARD } from '@nestjs/core';
 import { AuthGuard } from './presentation/guards/auth.guard';
 import { FindUniqueCompanyByManagerUseCase } from './application/use-cases/company/find-unique-company-by-manager.use-case';
 import { FindCompanyByIdUseCase } from './application/use-cases/company/find-company-by-id.use-case';
+import { FindEmployeesByCompanyUseCase } from './application/use-cases/company/find-employees-by-company.use-case';
 import { FindAllDriversUseCase } from './application/use-cases/driver/find-all-drivers.use-case';
 import { DeleteDriverUseCase } from './application/use-cases/driver/delete-driver.use-case';
 import { FindDriverByIdUseCase } from './application/use-cases/driver/find-driver-by-id.use-case';
@@ -55,6 +57,23 @@ import { StationController } from './presentation/controllers/station.controller
 import { TransactionController } from './presentation/controllers/transaction.controller';
 import { QrcodeController } from './presentation/controllers/qrcode.controller';
 import { StationCompanyController } from './presentation/controllers/station-company.controller';
+import { StationRepository } from './infrastructure/database/repositories/station.repository';
+import { FindAllStationsUseCase } from './application/use-cases/station/find-all-stations.use-case';
+import { FindStationByIdUseCase } from './application/use-cases/station/find-station-by-id.use-case';
+import { CreateStationUseCase } from './application/use-cases/station/create-station.use-case';
+import { UpdateStationUseCase } from './application/use-cases/station/update-station.use-case';
+import { DeleteStationUseCase } from './application/use-cases/station/delete-station.use-case';
+import { SearchStationsByNameUseCase } from './application/use-cases/station/search-stations-by-name.use-case';
+import { FindStationsByCityUseCase } from './application/use-cases/station/find-stations-by-city.use-case';
+// Station-Company related imports
+import { CreateStationCompanyUseCase } from './application/use-cases/station-company/create-station-company.use-case';
+import { FindAllStationCompaniesUseCase } from './application/use-cases/station-company/find-all-station-companies.use-case';
+import { FindStationCompanyByIdUseCase } from './application/use-cases/station-company/find-station-company-by-id.use-case';
+import { FindStationCompaniesByCompanyUseCase } from './application/use-cases/station-company/find-station-companies-by-company.use-case';
+import { FindStationCompaniesByStationUseCase } from './application/use-cases/station-company/find-station-companies-by-station.use-case';
+import { DeleteStationCompanyUseCase } from './application/use-cases/station-company/delete-station-company.use-case';
+import { StationCompanyRepository } from './infrastructure/database/repositories/station-company.repository';
+import { StationCompanyMapper } from './infrastructure/mappers/station-company.mapper';
 
 @Module({
   imports: [
@@ -104,6 +123,7 @@ import { StationCompanyController } from './presentation/controllers/station-com
     FindAllCompaniesUseCase,
     FindUniqueCompanyByManagerUseCase,
     FindCompanyByIdUseCase,
+    FindEmployeesByCompanyUseCase,
     UpdateCompanyUseCase,
     CompanyMapper,
     {
@@ -142,13 +162,40 @@ import { StationCompanyController } from './presentation/controllers/station-com
     UpdateTrajetUseCase,
     FindAllTrajetsUseCase,
     FindTrajetByIdUseCase,
+    FindTrajetsByCompanyUseCase,
     DeleteTrajetUseCase,
     TrajetMapper,
-    StationMapper, // Ajoutez cette ligne
     {
       provide: 'TrajetRepository',
       useClass: TrajetRepository,
     },
+
+    // Station related providers
+    FindAllStationsUseCase,
+    FindStationByIdUseCase,
+    CreateStationUseCase,
+    UpdateStationUseCase,
+    DeleteStationUseCase,
+    SearchStationsByNameUseCase,
+    FindStationsByCityUseCase,
+    StationMapper,
+    {
+      provide: 'StationRepository',
+      useClass: StationRepository,
+    },
+
+    // Station-Company related providers
+    CreateStationCompanyUseCase,
+    FindAllStationCompaniesUseCase,
+    FindStationCompanyByIdUseCase,
+    FindStationCompaniesByCompanyUseCase,
+    FindStationCompaniesByStationUseCase,
+    DeleteStationCompanyUseCase,
+    StationCompanyMapper,
+    {
+      provide: 'StationCompanyRepository',
+      useClass: StationCompanyRepository,
+    },
   ],
 })
 export class AppModule implements NestModule {

+ 1 - 1
node_app/src/application/dto/create-station.dto.ts

@@ -3,5 +3,5 @@ export class CreateStationDto {
   name: string;
   address: string;
   lat?: Prisma.Decimal | null;
-  long?: number | null;
+  long?: Prisma.Decimal | null;
 }

+ 19 - 1
node_app/src/application/dto/create-trajet.dto.ts

@@ -1,4 +1,4 @@
-import { IsString, IsNotEmpty, IsNumber, IsDateString } from 'class-validator';
+import { IsString, IsNotEmpty, IsNumber, IsDateString, IsOptional } from 'class-validator';
 import { ApiProperty } from '@nestjs/swagger';
 
 export class CreateTrajetDto {
@@ -46,4 +46,22 @@ export class CreateTrajetDto {
   @IsNumber()
   @IsNotEmpty()
   vehiculeId: number;
+
+  @ApiProperty({
+    description: 'Prix par place en FCFA',
+    example: 5000,
+    required: true,
+  })
+  @IsNumber()
+  @IsNotEmpty()
+  pricePerSeat: number;
+
+  @ApiProperty({
+    description: 'ID de la compagnie (optionnel)',
+    example: 1,
+    required: false,
+  })
+  @IsNumber()
+  @IsOptional()
+  companyId?: number;
 }

+ 10 - 0
node_app/src/application/dto/create-user.dto.ts

@@ -4,6 +4,7 @@ import {
   IsNotEmpty,
   MinLength,
   IsOptional,
+  IsNumber,
 } from 'class-validator';
 import { ApiProperty } from '@nestjs/swagger';
 
@@ -63,4 +64,13 @@ export class CreateUserDto {
   @IsString()
   @MinLength(6)
   password: string;
+
+  @ApiProperty({
+    description: "L'ID de l'entreprise employeur",
+    example: 1,
+    required: false,
+  })
+  @IsOptional()
+  @IsNumber()
+  companyId?: number;
 }

+ 6 - 0
node_app/src/application/dto/responses/trajet-response.dto.ts

@@ -22,6 +22,12 @@ export class TrajetResponseDto {
   @ApiProperty({ description: "ID du véhicule associé au trajet" })
   vehiculeId: number;
 
+  @ApiProperty({ description: "ID de la compagnie (optionnel)", required: false })
+  companyId?: number;
+
+  @ApiProperty({ description: "Prix par place en FCFA" })
+  pricePerSeat: number;
+
   @ApiProperty({ type: () => VehiculeResponseDto, required: false }) // Rendre optionnel si le véhicule n'est pas toujours inclus
   vehicule?: VehiculeResponseDto;
 

+ 5 - 0
node_app/src/application/dto/update-user.dto.ts

@@ -3,6 +3,7 @@ import {
   IsEnum,
   IsOptional,
   IsString,
+  IsNumber,
   MinLength,
 } from 'class-validator';
 import { UserRole } from '../../domain/entities/user.entity';
@@ -41,4 +42,8 @@ export class UpdateUserDto {
       'Le rôle doit être une valeur valide (USER, ADMIN, DRIVER, MANAGER, EMPLOYEE)',
   })
   role?: UserRole;
+
+  @IsOptional()
+  @IsNumber()
+  companyId?: number;
 }

+ 25 - 0
node_app/src/application/use-cases/company/find-employees-by-company.use-case.ts

@@ -0,0 +1,25 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { CompanyRepository } from '../../../infrastructure/database/repositories/company.repository';
+import { PaginatedResponseDto } from '../../dto/responses/paginated-response.dto';
+import { User } from '../../../domain/entities/user.entity';
+
+@Injectable()
+export class FindEmployeesByCompanyUseCase {
+  constructor(
+    @Inject('CompanyRepository')
+    private readonly companyRepository: CompanyRepository,
+  ) {}
+
+  async execute(
+    companyId: number,
+    page = 1,
+    limit = 10,
+  ): Promise<PaginatedResponseDto<User>> {
+    const { data, total } = await this.companyRepository.findEmployeesByCompanyId(
+      companyId,
+      page,
+      limit,
+    );
+    return new PaginatedResponseDto<User>(data, total, page, limit);
+  }
+}

+ 18 - 0
node_app/src/application/use-cases/station-company/create-station-company.use-case.ts

@@ -0,0 +1,18 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationCompanyRepository } from '../../../infrastructure/database/repositories/station-company.repository';
+import { CreateStationCompanyDto } from '../../dto/create-station-company.dto';
+import { StationCompanyMapper } from 'src/infrastructure/mappers/station-company.mapper';
+
+@Injectable()
+export class CreateStationCompanyUseCase {
+  constructor(
+    @Inject('StationCompanyRepository')
+    private readonly stationCompanyRepository: StationCompanyRepository,
+    private readonly stationCompanyMapper: StationCompanyMapper,
+  ) {}
+
+  async execute(createStationCompanyDto: CreateStationCompanyDto) {
+    const createStationCompany = this.stationCompanyMapper.fromDtoToDomain(createStationCompanyDto);
+    return this.stationCompanyRepository.create(createStationCompany);
+  }
+}

+ 15 - 0
node_app/src/application/use-cases/station-company/delete-station-company.use-case.ts

@@ -0,0 +1,15 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationCompanyRepository } from '../../../infrastructure/database/repositories/station-company.repository';
+
+@Injectable()
+export class DeleteStationCompanyUseCase {
+  constructor(
+    @Inject('StationCompanyRepository')
+    private readonly stationCompanyRepository: StationCompanyRepository,
+  ) {}
+
+  async execute(id: number): Promise<boolean> {
+    const deletedStationCompany = await this.stationCompanyRepository.delete(id);
+    return deletedStationCompany !== null;
+  }
+}

+ 21 - 0
node_app/src/application/use-cases/station-company/find-all-station-companies.use-case.ts

@@ -0,0 +1,21 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationCompanyRepository } from '../../../infrastructure/database/repositories/station-company.repository';
+import { PaginatedResponseDto } from '../../dto/responses/paginated-response.dto';
+import { StationCompany } from '../../../domain/entities/station-company.entity';
+
+@Injectable()
+export class FindAllStationCompaniesUseCase {
+  constructor(
+    @Inject('StationCompanyRepository')
+    private readonly stationCompanyRepository: StationCompanyRepository,
+  ) {}
+
+  async execute(
+    page: number = 1,
+    limit: number = 10,
+    search?: string,
+    stationId?: number,
+  ): Promise<PaginatedResponseDto<StationCompany>> {
+    return this.stationCompanyRepository.findAll(page, limit, search, stationId);
+  }
+}

+ 15 - 0
node_app/src/application/use-cases/station-company/find-station-companies-by-company.use-case.ts

@@ -0,0 +1,15 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationCompanyRepository } from '../../../infrastructure/database/repositories/station-company.repository';
+import { StationCompany } from '../../../domain/entities/station-company.entity';
+
+@Injectable()
+export class FindStationCompaniesByCompanyUseCase {
+  constructor(
+    @Inject('StationCompanyRepository')
+    private readonly stationCompanyRepository: StationCompanyRepository,
+  ) {}
+
+  async execute(companyId: number): Promise<StationCompany[]> {
+    return this.stationCompanyRepository.findByCompany(companyId);
+  }
+}

+ 15 - 0
node_app/src/application/use-cases/station-company/find-station-companies-by-station.use-case.ts

@@ -0,0 +1,15 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationCompanyRepository } from '../../../infrastructure/database/repositories/station-company.repository';
+import { StationCompany } from '../../../domain/entities/station-company.entity';
+
+@Injectable()
+export class FindStationCompaniesByStationUseCase {
+  constructor(
+    @Inject('StationCompanyRepository')
+    private readonly stationCompanyRepository: StationCompanyRepository,
+  ) {}
+
+  async execute(stationId: number): Promise<StationCompany[]> {
+    return this.stationCompanyRepository.findByStation(stationId);
+  }
+}

+ 15 - 0
node_app/src/application/use-cases/station-company/find-station-company-by-id.use-case.ts

@@ -0,0 +1,15 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationCompanyRepository } from '../../../infrastructure/database/repositories/station-company.repository';
+import { StationCompany } from '../../../domain/entities/station-company.entity';
+
+@Injectable()
+export class FindStationCompanyByIdUseCase {
+  constructor(
+    @Inject('StationCompanyRepository')
+    private readonly stationCompanyRepository: StationCompanyRepository,
+  ) {}
+
+  async execute(id: number): Promise<StationCompany | null> {
+    return this.stationCompanyRepository.findById(id);
+  }
+}

+ 36 - 0
node_app/src/application/use-cases/station/create-station.use-case.ts

@@ -0,0 +1,36 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationRepository } from '../../../infrastructure/database/repositories/station.repository';
+import { CreateStationDto } from '../../dto/create-station.dto';
+import { StationResponseDto } from '../../dto/responses/station-response.dto';
+import { Station } from '../../../domain/entities/station.entity';
+import { ConflictException } from '@nestjs/common';
+
+@Injectable()
+export class CreateStationUseCase {
+  constructor(
+    @Inject('StationRepository')
+    private readonly stationRepository: StationRepository,
+  ) {}
+
+  async execute(createStationDto: CreateStationDto): Promise<StationResponseDto> {
+    // Vérifier si une station avec le même nom existe déjà
+    const { data: existingStations } = await this.stationRepository.findAll(1, 1000);
+    const existingStation = existingStations.find(
+      station => station.name.toLowerCase() === createStationDto.name.toLowerCase()
+    );
+    
+    if (existingStation) {
+      throw new ConflictException(`Une station avec le nom "${createStationDto.name}" existe déjà`);
+    }
+
+    const station = new Station({
+      name: createStationDto.name,
+      address: createStationDto.address,
+      lat: createStationDto.lat,
+      long: createStationDto.long,
+    });
+
+    const createdStation = await this.stationRepository.create(station);
+    return StationResponseDto.fromEntity(createdStation);
+  }
+}

+ 26 - 0
node_app/src/application/use-cases/station/delete-station.use-case.ts

@@ -0,0 +1,26 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationRepository } from '../../../infrastructure/database/repositories/station.repository';
+import { NotFoundException, ConflictException } from '@nestjs/common';
+
+@Injectable()
+export class DeleteStationUseCase {
+  constructor(
+    @Inject('StationRepository')
+    private readonly stationRepository: StationRepository,
+  ) {}
+
+  async execute(id: number): Promise<{ message: string }> {
+    // Vérifier que la station existe
+    const existingStation = await this.stationRepository.findById(id);
+    if (!existingStation) {
+      throw new NotFoundException(`Station avec l'ID ${id} non trouvée`);
+    }
+
+    try {
+      await this.stationRepository.delete(id);
+      return { message: `Station "${existingStation.name}" supprimée avec succès` };
+    } catch (error) {
+      throw error;
+    }
+  }
+}

+ 26 - 0
node_app/src/application/use-cases/station/find-all-stations.use-case.ts

@@ -0,0 +1,26 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationRepository } from '../../../infrastructure/database/repositories/station.repository';
+import { StationResponseDto } from '../../dto/responses/station-response.dto';
+import { PaginatedResponseDto } from '../../dto/responses/paginated-response.dto';
+
+@Injectable()
+export class FindAllStationsUseCase {
+  constructor(
+    @Inject('StationRepository')
+    private readonly stationRepository: StationRepository,
+  ) {}
+
+  async execute(
+    page = 1,
+    limit = 10,
+  ): Promise<PaginatedResponseDto<StationResponseDto>> {
+    const { data, total } = await this.stationRepository.findAll(page, limit);
+    const stationDtos = data.map((station) => StationResponseDto.fromEntity(station));
+    return new PaginatedResponseDto<StationResponseDto>(
+      stationDtos,
+      total,
+      page,
+      limit,
+    );
+  }
+}

+ 20 - 0
node_app/src/application/use-cases/station/find-station-by-id.use-case.ts

@@ -0,0 +1,20 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationRepository } from '../../../infrastructure/database/repositories/station.repository';
+import { StationResponseDto } from '../../dto/responses/station-response.dto';
+import { NotFoundException } from '@nestjs/common';
+
+@Injectable()
+export class FindStationByIdUseCase {
+  constructor(
+    @Inject('StationRepository')
+    private readonly stationRepository: StationRepository,
+  ) {}
+
+  async execute(id: number): Promise<StationResponseDto> {
+    const station = await this.stationRepository.findById(id);
+    if (!station) {
+      throw new NotFoundException(`Station avec l'ID ${id} non trouvée`);
+    }
+    return StationResponseDto.fromEntity(station);
+  }
+}

+ 23 - 0
node_app/src/application/use-cases/station/find-stations-by-city.use-case.ts

@@ -0,0 +1,23 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationRepository } from '../../../infrastructure/database/repositories/station.repository';
+import { StationResponseDto } from '../../dto/responses/station-response.dto';
+
+@Injectable()
+export class FindStationsByCityUseCase {
+  constructor(
+    @Inject('StationRepository')
+    private readonly stationRepository: StationRepository,
+  ) {}
+
+  async execute(city: string): Promise<StationResponseDto[]> {
+    // Pour l'instant, on récupère toutes les stations et on filtre côté application
+    // Dans une vraie implémentation, on ajouterait une méthode findByCity au repository
+    const { data: stations } = await this.stationRepository.findAll(1, 1000);
+    
+    const filteredStations = stations.filter(station => 
+      station.city?.toLowerCase() === city.toLowerCase()
+    );
+    
+    return filteredStations.map(station => StationResponseDto.fromEntity(station));
+  }
+}

+ 23 - 0
node_app/src/application/use-cases/station/search-stations-by-name.use-case.ts

@@ -0,0 +1,23 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationRepository } from '../../../infrastructure/database/repositories/station.repository';
+import { StationResponseDto } from '../../dto/responses/station-response.dto';
+
+@Injectable()
+export class SearchStationsByNameUseCase {
+  constructor(
+    @Inject('StationRepository')
+    private readonly stationRepository: StationRepository,
+  ) {}
+
+  async execute(name: string): Promise<StationResponseDto[]> {
+    // Pour l'instant, on récupère toutes les stations et on filtre côté application
+    // Dans une vraie implémentation, on ajouterait une méthode searchByName au repository
+    const { data: stations } = await this.stationRepository.findAll(1, 1000);
+    
+    const filteredStations = stations.filter(station => 
+      station.name.toLowerCase().includes(name.toLowerCase())
+    );
+    
+    return filteredStations.map(station => StationResponseDto.fromEntity(station));
+  }
+}

+ 32 - 0
node_app/src/application/use-cases/station/update-station.use-case.ts

@@ -0,0 +1,32 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { StationRepository } from '../../../infrastructure/database/repositories/station.repository';
+import { UpdateStationDto } from '../../dto/update-station.dto';
+import { StationResponseDto } from '../../dto/responses/station-response.dto';
+import { Station } from '../../../domain/entities/station.entity';
+import { NotFoundException } from '@nestjs/common';
+
+@Injectable()
+export class UpdateStationUseCase {
+  constructor(
+    @Inject('StationRepository')
+    private readonly stationRepository: StationRepository,
+  ) {}
+
+  async execute(id: number, updateStationDto: UpdateStationDto): Promise<StationResponseDto> {
+    // Vérifier que la station existe
+    const existingStation = await this.stationRepository.findById(id);
+    if (!existingStation) {
+      throw new NotFoundException(`Station avec l'ID ${id} non trouvée`);
+    }
+
+    // Créer l'entité mise à jour
+    const updatedStation = new Station({
+      ...existingStation,
+      ...updateStationDto,
+      id, // S'assurer que l'ID reste le même
+    });
+
+    const result = await this.stationRepository.update(id, updatedStation);
+    return StationResponseDto.fromEntity(result);
+  }
+}

+ 45 - 0
node_app/src/application/use-cases/trajet/find-trajets-by-company.use-case.ts

@@ -0,0 +1,45 @@
+import { Injectable, Inject } from '@nestjs/common';
+import { TrajetRepository } from '../../../infrastructure/database/repositories/trajet.repository';
+import { PaginatedResponseDto } from '../../dto/responses/paginated-response.dto';
+import { TrajetResponseDto } from '../../dto/responses/trajet-response.dto';
+import { TrajetMapper } from '../../../infrastructure/mappers/trajet.mapper';
+
+@Injectable()
+export class FindTrajetsByCompanyUseCase {
+  constructor(
+    @Inject('TrajetRepository')
+    private readonly trajetRepository: TrajetRepository,
+    private readonly trajetMapper: TrajetMapper,
+  ) {}
+
+  async execute(
+    companyId: number,
+    page = 1,
+    limit = 10,
+  ): Promise<PaginatedResponseDto<TrajetResponseDto>> {
+    // Validation de base
+    if (companyId <= 0) {
+      throw new Error('ID de compagnie invalide');
+    }
+
+    // Récupération des trajets par compagnie
+    const { data, total } = await this.trajetRepository.findByCompanyId(
+      companyId,
+      page,
+      limit,
+    );
+
+    // Conversion des entités en DTOs
+    const trajets = data.map((trajet) =>
+      this.trajetMapper.toResponseDto(trajet),
+    );
+
+    // Création de la réponse paginée
+    return new PaginatedResponseDto<TrajetResponseDto>(
+      trajets,
+      total,
+      page,
+      limit
+    );
+  }
+}

+ 2 - 0
node_app/src/domain/entities/company.entity.ts

@@ -1,5 +1,6 @@
 import { Vehicule } from './vehicule.entity';
 import { Driver } from './driver.entity';
+import { User } from './user.entity';
 
 export class Company {
   readonly id: number;
@@ -7,6 +8,7 @@ export class Company {
   address: string;
   manager: any;
   managerId: number;
+  employees?: User[]; // Nouvelle relation avec les employés
   vehicules: Vehicule[];
   drivers: Driver[];
 

+ 4 - 0
node_app/src/domain/entities/place.entity.ts

@@ -12,6 +12,10 @@ export class Place {
 
   vehiculeId: number;
 
+  isAvailable: boolean;
+
+  occupiedAt: Date | null;
+
   constructor(partial: Partial<Place>) {
     Object.assign(this, partial);
   }

+ 2 - 0
node_app/src/domain/entities/trajet.entity.ts

@@ -9,6 +9,8 @@ export class Trajet {
   departStationId: number; // Au lieu de depart: string
   arriveStationId: number; // Au lieu de arrive: string
   vehiculeId: number; // vehiculeId est conservé
+  companyId?: number; // Relation directe avec Company (optionnel)
+  pricePerSeat: number; // Prix par place
   // Relations
   vehicule?: Vehicule; // L'objet Vehicule peut être chargé optionnellement
   reservations?: Reservation[];

+ 2 - 0
node_app/src/domain/entities/user.entity.ts

@@ -20,6 +20,8 @@ export class User {
   email: string;
   password: string;
   role: UserRole; // Ajout de la propriété role
+  companyId?: number; // Nouveau champ pour la relation employé-entreprise
+  company?: Company; // Relation vers l'entreprise employeur
   reservations?: Reservation[];
   driver?: Driver;
   managedCompany?: Company;

+ 39 - 0
node_app/src/infrastructure/database/repositories/company.repository.ts

@@ -5,12 +5,15 @@ import { PrismaService } from '../prisma.service';
 import { Company } from '../../../domain/entities/company.entity';
 import { UpdateCompanyDto } from '../../../application/dto/update-company.dto';
 import { CompanyMapper } from '../../mappers/company.mapper';
+import { UserMapper } from '../../mappers/user.mapper';
 import { Vehicule } from 'src/domain/entities/vehicule.entity';
+import { User } from '../../../domain/entities/user.entity';
 import { IBaseRepository } from './BaseRepository';
 
 @Injectable()
 export class CompanyRepository implements IBaseRepository<Company> {
   private readonly companyMapper = new CompanyMapper();
+  private readonly userMapper = new UserMapper();
 
   constructor(private prisma: PrismaService) {}
 
@@ -114,4 +117,40 @@ export class CompanyRepository implements IBaseRepository<Company> {
     });
     return this.companyMapper.toDomain(response);
   }
+
+  /**
+   * Récupère tous les employés d'une entreprise
+   * @param companyId ID de l'entreprise
+   * @param page Numéro de page (défaut: 1)
+   * @param limit Nombre d'éléments par page (défaut: 10)
+   * @returns Liste paginée des employés
+   */
+  async findEmployeesByCompanyId(
+    companyId: number,
+    page = 1,
+    limit = 10,
+  ): Promise<{ data: User[]; total: number }> {
+    const skip = (page - 1) * limit;
+
+    const [data, total] = await Promise.all([
+      this.prisma.user.findMany({
+        where: { companyId },
+        skip,
+        take: limit,
+        include: {
+          company: true,
+          managedCompany: true,
+          driver: true,
+        },
+      }),
+      this.prisma.user.count({
+        where: { companyId },
+      }),
+    ]);
+
+    return {
+      data: data.map((user) => this.userMapper.toDomain(user)),
+      total,
+    };
+  }
 }

+ 66 - 2
node_app/src/infrastructure/database/repositories/station-company.repository.ts

@@ -16,10 +16,41 @@ export class StationCompanyRepository
   async findAll(
     page = 1,
     limit = 10,
-  ): Promise<{ data: StationCompany[]; total: number }> {
+    search?: string,
+    stationId?: number,
+  ): Promise<{ data: StationCompany[]; total: number; page: number; limit: number; totalPages: number }> {
     const skip = (page - 1) * limit;
+    
+    const where: any = {};
+    
+    if (search) {
+      where.OR = [
+        {
+          station: {
+            name: {
+              contains: search,
+              mode: 'insensitive',
+            },
+          },
+        },
+        {
+          company: {
+            name: {
+              contains: search,
+              mode: 'insensitive',
+            },
+          },
+        },
+      ];
+    }
+    
+    if (stationId) {
+      where.stationId = stationId;
+    }
+
     const [prismaStationCompanies, total] = await this.prisma.$transaction([
       this.prisma.stationCompany.findMany({
+        where,
         skip,
         take: limit,
         include: {
@@ -27,17 +58,50 @@ export class StationCompanyRepository
           company: true,
           stationChief: true,
         },
+        orderBy: {
+          id: 'desc',
+        },
       }),
-      this.prisma.stationCompany.count(),
+      this.prisma.stationCompany.count({ where }),
     ]);
+    
     return {
       data: prismaStationCompanies.map((sc) =>
         this.stationCompanyMapper.toDomain(sc),
       ),
       total,
+      page,
+      limit,
+      totalPages: Math.ceil(total / limit),
     };
   }
 
+  async findByCompany(companyId: number): Promise<StationCompany[]> {
+    const stationCompanies = await this.prisma.stationCompany.findMany({
+      where: { companyId },
+      include: {
+        station: true,
+        company: true,
+        stationChief: true,
+      },
+    });
+
+    return stationCompanies.map((sc) => this.stationCompanyMapper.toDomain(sc));
+  }
+
+  async findByStation(stationId: number): Promise<StationCompany[]> {
+    const stationCompanies = await this.prisma.stationCompany.findMany({
+      where: { stationId },
+      include: {
+        station: true,
+        company: true,
+        stationChief: true,
+      },
+    });
+
+    return stationCompanies.map((sc) => this.stationCompanyMapper.toDomain(sc));
+  }
+
   async findById(id: number): Promise<StationCompany | null> {
     const prismaStationCompany = await this.prisma.stationCompany.findUnique({
       where: { id },

+ 56 - 5
node_app/src/infrastructure/database/repositories/trajet.repository.ts

@@ -28,6 +28,8 @@ export class TrajetRepository implements IBaseRepository<Trajet> {
         arriveStationId: data.arriveStationId, // Au lieu de arrive
         // Utiliser directement vehiculeId au lieu de connect
         vehiculeId: data.vehiculeId,
+        companyId: data.companyId,
+        pricePerSeat: data.pricePerSeat,
       },
       include: {
         vehicule: {
@@ -55,11 +57,11 @@ export class TrajetRepository implements IBaseRepository<Trajet> {
       data: {
         departAt: entity.departAt,
         arriveAt: entity.arriveAt,
-        vehicule: entity.vehiculeId
-          ? {
-              connect: { id: entity.vehiculeId },
-            }
-          : undefined,
+        departStationId: entity.departStationId,
+        arriveStationId: entity.arriveStationId,
+        companyId: entity.companyId,
+        vehiculeId: entity.vehiculeId,
+        pricePerSeat: entity.pricePerSeat,
       },
       include: {
         vehicule: {
@@ -115,6 +117,55 @@ export class TrajetRepository implements IBaseRepository<Trajet> {
       total,
     };
   }
+  async findByCompanyId(
+    companyId: number,
+    page = 1,
+    limit = 10,
+  ): Promise<{ data: Trajet[]; total: number }> {
+    const skip = (page - 1) * limit;
+
+    const [data, total] = await Promise.all([
+      this.prisma.trajet.findMany({
+        where: {
+          vehicule: {
+            companyId: companyId,
+          },
+        },
+        skip,
+        take: limit,
+        include: {
+          vehicule: {
+            include: {
+              company: true,
+              driver: true,
+              places: true,
+            },
+          },
+          reservations: {
+            include: {
+              user: true,
+              place: true,
+            },
+          },
+          departStation: true,
+          arriveStation: true,
+        },
+      }),
+      this.prisma.trajet.count({
+        where: {
+          vehicule: {
+            companyId: companyId,
+          },
+        },
+      }),
+    ]);
+
+    return {
+      data: data.map((trajet) => this.trajetMapper.toDomain(trajet)),
+      total,
+    };
+  }
+
   delete(id: number): Promise<Trajet | null> {
     throw new Error('Method not implemented.' + id);
   }

+ 4 - 0
node_app/src/infrastructure/mappers/place.mapper.ts

@@ -8,6 +8,8 @@ export class PlaceMapper implements IPrismaMapper<PrismaPlace, Place> {
       id: prismaPlace.id,
       number: prismaPlace.number,
       vehiculeId: prismaPlace.vehiculeId,
+      isAvailable: prismaPlace.isAvailable,
+      occupiedAt: prismaPlace.occupiedAt,
     });
   }
 
@@ -16,6 +18,8 @@ export class PlaceMapper implements IPrismaMapper<PrismaPlace, Place> {
       id: place.id,
       number: place.number,
       vehiculeId: place.vehiculeId,
+      isAvailable: place.isAvailable,
+      occupiedAt: place.occupiedAt,
     };
   }
 

+ 13 - 1
node_app/src/infrastructure/mappers/station-company.mapper.ts

@@ -1,16 +1,28 @@
 import { Injectable } from '@nestjs/common';
 import { StationCompany as PrismaStationCompany } from '@prisma/client';
 import { StationCompany } from '../../domain/entities/station-company.entity';
+import { CreateStationCompanyDto } from '../../application/dto/create-station-company.dto';
 import { IPrismaMapper } from './prisma-mapper.interface';
 
 @Injectable()
 export class StationCompanyMapper implements IPrismaMapper<PrismaStationCompany, StationCompany> {
-  toDomain(prismaStationCompany: PrismaStationCompany): StationCompany {
+  toDomain(prismaStationCompany: any): StationCompany {
     return new StationCompany({
       id: prismaStationCompany.id,
       stationId: prismaStationCompany.stationId,
       companyId: prismaStationCompany.companyId,
       stationChiefId: prismaStationCompany.stationChiefId,
+      station: prismaStationCompany.station,
+      company: prismaStationCompany.company,
+      stationChief: prismaStationCompany.stationChief,
+    });
+  }
+
+  fromDtoToDomain(dto: CreateStationCompanyDto): StationCompany {
+    return new StationCompany({
+      stationId: dto.stationId,
+      companyId: dto.companyId,
+      stationChiefId: dto.stationChiefId,
     });
   }
 

+ 6 - 0
node_app/src/infrastructure/mappers/trajet.mapper.ts

@@ -59,6 +59,8 @@ export class TrajetMapper
       departStationId: prismaTrajet.departStationId,
       arriveStationId: prismaTrajet.arriveStationId,
       vehiculeId: prismaTrajet.vehiculeId,
+      companyId: prismaTrajet.companyId ?? undefined,
+      pricePerSeat: prismaTrajet.pricePerSeat,
       departStation: departStationDomain,
       arriveStation: arriveStationDomain,
       vehicule: vehiculeDomain,
@@ -77,6 +79,8 @@ export class TrajetMapper
       departStationId: dto.departStationId,
       arriveStationId: dto.arriveStationId,
       vehiculeId: dto.vehiculeId,
+      companyId: (dto as CreateTrajetDto).companyId,
+      pricePerSeat: (dto as CreateTrajetDto).pricePerSeat || 0,
       // Les objets Station et Vehicule ne sont pas créés à partir du DTO ici,
       // ils seraient récupérés/assignés dans la logique de service.
     });
@@ -90,6 +94,8 @@ export class TrajetMapper
     response.departStationId = trajet.departStationId;
     response.arriveStationId = trajet.arriveStationId;
     response.vehiculeId = trajet.vehiculeId;
+    response.companyId = trajet.companyId;
+    response.pricePerSeat = trajet.pricePerSeat;
 
     // Si TrajetResponseDto doit inclure des détails sur les stations ou le véhicule
     if (trajet.departStation) {

+ 2 - 0
node_app/src/infrastructure/mappers/user.mapper.ts

@@ -14,6 +14,7 @@ export class UserMapper implements IPrismaMapper<PrismaUser, User> {
       address: prismaUser.address || undefined,
       telephone: prismaUser.telephone || undefined,
       role: prismaUser.role ? (prismaUser.role as UserRole) : undefined,
+      companyId: prismaUser.companyId || undefined,
     });
   }
 
@@ -27,6 +28,7 @@ export class UserMapper implements IPrismaMapper<PrismaUser, User> {
       address: user.address || '',
       telephone: user.telephone || '',
       role: user.role || '',
+      companyId: user.companyId || null,
     };
   }
 

+ 33 - 1
node_app/src/presentation/controllers/company.controller.ts

@@ -26,8 +26,11 @@ import { UserRole } from 'src/domain/entities/user.entity';
 import { FindCompanyByIdUseCase } from '../../application/use-cases/company/find-company-by-id.use-case';
 import { NotFoundException } from '@nestjs/common';
 import { FindUniqueCompanyByManagerUseCase } from 'src/application/use-cases/company/find-unique-company-by-manager.use-case';
+import { FindEmployeesByCompanyUseCase } from '../../application/use-cases/company/find-employees-by-company.use-case';
 import { PaginationDto } from '../../application/dto/pagination.dto';
 import { PaginatedResponseDto } from '../../application/dto/responses/paginated-response.dto';
+import { User } from '../../domain/entities/user.entity';
+import { Public } from '../decorators/public.decorator';
 
 @ApiTags('companies')
 @Controller('companies')
@@ -38,6 +41,7 @@ export class CompanyController {
     private readonly createCompanyUseCase: CreateCompanyUseCase,
     private readonly findCompanyByIdUseCase: FindCompanyByIdUseCase,
     private readonly findCompanyByManager: FindUniqueCompanyByManagerUseCase,
+    private readonly findEmployeesByCompanyUseCase: FindEmployeesByCompanyUseCase,
   ) {}
 
   @Post()
@@ -70,7 +74,6 @@ export class CompanyController {
   }
 
   @Get(':id')
-  @Roles(UserRole.ADMIN, UserRole.MANAGER, UserRole.USER)
   @ApiOperation({ summary: 'Récupérer une entreprise par son ID' })
   @ApiParam({ name: 'id', description: "ID de l'entreprise" })
   @ApiResponse({
@@ -79,6 +82,7 @@ export class CompanyController {
     type: CompanyResponseDto,
   })
   @ApiResponse({ status: 404, description: 'Entreprise non trouvée' })
+  @Public()
   async findOne(@Param('id') id: string) {
     try {
       const company = await this.findCompanyByIdUseCase.execute(
@@ -140,6 +144,34 @@ export class CompanyController {
     // TODO: Implement update company use case
   }
 
+  @Get(':id/employees')
+  @ApiOperation({ summary: 'Récupérer les employés d\'une entreprise' })
+  @ApiParam({ name: 'id', description: "ID de l'entreprise" })
+  @ApiResponse({
+    status: 200,
+    description: 'Liste des employés récupérée avec succès',
+    type: PaginatedResponseDto<User>,
+  })
+  @Roles(UserRole.ADMIN, UserRole.MANAGER)
+  async getEmployees(
+    @Param('id') id: string,
+    @Query() paginationDto: PaginationDto,
+  ): Promise<PaginatedResponseDto<User>> {
+    const companyId = parseInt(id, 10);
+    
+    // Vérifier que l'entreprise existe
+    const company = await this.findCompanyByIdUseCase.execute(companyId);
+    if (!company) {
+      throw new NotFoundException(`Entreprise avec l'ID ${companyId} non trouvée`);
+    }
+
+    return this.findEmployeesByCompanyUseCase.execute(
+      companyId,
+      paginationDto.page,
+      paginationDto.limit,
+    );
+  }
+
   @Delete(':id')
   @ApiOperation({ summary: 'Supprimer une entreprise' })
   @ApiParam({ name: 'id', description: "ID de l'entreprise" })

+ 50 - 14
node_app/src/presentation/controllers/station-company.controller.ts

@@ -21,6 +21,13 @@ import { PaginatedResponseDto } from '../../application/dto/responses/paginated-
 import { Roles } from '../decorators/roles.decorator';
 import { UserRole } from '../../domain/entities/user.entity';
 import { CreateStationCompanyDto } from '../../application/dto/create-station-company.dto';
+import { CreateStationCompanyUseCase } from '../../application/use-cases/station-company/create-station-company.use-case';
+import { FindAllStationCompaniesUseCase } from '../../application/use-cases/station-company/find-all-station-companies.use-case';
+import { FindStationCompanyByIdUseCase } from '../../application/use-cases/station-company/find-station-company-by-id.use-case';
+import { FindStationCompaniesByCompanyUseCase } from '../../application/use-cases/station-company/find-station-companies-by-company.use-case';
+import { FindStationCompaniesByStationUseCase } from '../../application/use-cases/station-company/find-station-companies-by-station.use-case';
+import { DeleteStationCompanyUseCase } from '../../application/use-cases/station-company/delete-station-company.use-case';
+import { NotFoundException } from '@nestjs/common';
 
 // DTO de réponse pour station-company avec exemples
 export class StationCompanyResponseDto {
@@ -93,7 +100,12 @@ export class StationCompanyResponseDto {
 @ApiSecurity('access-token')
 export class StationCompanyController {
   constructor(
-    // Inject use cases here when they are created
+    private readonly createStationCompanyUseCase: CreateStationCompanyUseCase,
+    private readonly findAllStationCompaniesUseCase: FindAllStationCompaniesUseCase,
+    private readonly findStationCompanyByIdUseCase: FindStationCompanyByIdUseCase,
+    private readonly findStationCompaniesByCompanyUseCase: FindStationCompaniesByCompanyUseCase,
+    private readonly findStationCompaniesByStationUseCase: FindStationCompaniesByStationUseCase,
+    private readonly deleteStationCompanyUseCase: DeleteStationCompanyUseCase,
   ) {}
 
   @Post()
@@ -124,8 +136,12 @@ export class StationCompanyController {
     description: 'Accès refusé - Rôle insuffisant',
   })
   async create(@Body() createStationCompanyDto: CreateStationCompanyDto): Promise<StationCompanyResponseDto> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    try {
+      const result = await this.createStationCompanyUseCase.execute(createStationCompanyDto);
+      return result;
+    } catch (error) {
+      throw error;
+    }
   }
 
   @Get()
@@ -138,9 +154,9 @@ export class StationCompanyController {
     description: 'Liste des associations récupérée avec succès',
     type: PaginatedResponseDto<StationCompanyResponseDto>,
   })
-  async findAll(@Query() paginationDto: PaginationDto): Promise<PaginatedResponseDto<StationCompanyResponseDto>> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+  async findAll(@Query() paginationDto: PaginationDto, @Query('search') search?: string, @Query('stationId') stationId?: number): Promise<PaginatedResponseDto<StationCompanyResponseDto>> {
+    const { page = 1, limit = 10 } = paginationDto;
+    return this.findAllStationCompaniesUseCase.execute(page, limit, search, stationId);
   }
 
   @Get('company/:companyId')
@@ -164,8 +180,7 @@ export class StationCompanyController {
     description: 'Entreprise non trouvée',
   })
   async findByCompany(@Param('companyId', ParseIntPipe) companyId: number): Promise<StationCompanyResponseDto[]> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return this.findStationCompaniesByCompanyUseCase.execute(companyId);
   }
 
   @Get('station/:stationId')
@@ -189,8 +204,7 @@ export class StationCompanyController {
     description: 'Station non trouvée',
   })
   async findByStation(@Param('stationId', ParseIntPipe) stationId: number): Promise<StationCompanyResponseDto[]> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return this.findStationCompaniesByStationUseCase.execute(stationId);
   }
 
   @Get(':id')
@@ -214,8 +228,20 @@ export class StationCompanyController {
     description: 'Association non trouvée',
   })
   async findOne(@Param('id', ParseIntPipe) id: number): Promise<StationCompanyResponseDto> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    try {
+      const stationCompany = await this.findStationCompanyByIdUseCase.execute(id);
+
+      if (!stationCompany) {
+        throw new NotFoundException(`Association station-entreprise avec l'ID ${id} non trouvée`);
+      }
+
+      return stationCompany;
+    } catch (error) {
+      if (error instanceof NotFoundException) {
+        throw error;
+      }
+      throw new NotFoundException(`Association station-entreprise avec l'ID ${id} non trouvée`);
+    }
   }
 
   @Delete(':id')
@@ -243,7 +269,17 @@ export class StationCompanyController {
     description: 'Accès refusé - Seuls les administrateurs peuvent supprimer',
   })
   async remove(@Param('id', ParseIntPipe) id: number): Promise<void> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    try {
+      const success = await this.deleteStationCompanyUseCase.execute(id);
+      
+      if (!success) {
+        throw new NotFoundException(`Association station-entreprise avec l'ID ${id} non trouvée`);
+      }
+    } catch (error) {
+      if (error instanceof NotFoundException) {
+        throw error;
+      }
+      throw new NotFoundException(`Association station-entreprise avec l'ID ${id} non trouvée`);
+    }
   }
 }

+ 25 - 17
node_app/src/presentation/controllers/station.controller.ts

@@ -24,6 +24,14 @@ import { PaginatedResponseDto } from '../../application/dto/responses/paginated-
 import { Roles } from '../decorators/roles.decorator';
 import { UserRole } from '../../domain/entities/user.entity';
 import { Prisma } from '@prisma/client';
+import { FindAllStationsUseCase } from '../../application/use-cases/station/find-all-stations.use-case';
+import { FindStationByIdUseCase } from '../../application/use-cases/station/find-station-by-id.use-case';
+import { CreateStationUseCase } from '../../application/use-cases/station/create-station.use-case';
+import { UpdateStationUseCase } from '../../application/use-cases/station/update-station.use-case';
+import { DeleteStationUseCase } from '../../application/use-cases/station/delete-station.use-case';
+import { SearchStationsByNameUseCase } from '../../application/use-cases/station/search-stations-by-name.use-case';
+import { FindStationsByCityUseCase } from '../../application/use-cases/station/find-stations-by-city.use-case';
+import { Public } from '../decorators/public.decorator';
 
 // Enhanced CreateStationDto with Swagger examples
 export class CreateStationDtoWithExamples {
@@ -55,7 +63,7 @@ export class CreateStationDtoWithExamples {
     type: 'number',
     required: false,
   })
-  long?: number | null;
+  long?: Prisma.Decimal | null;
 }
 
 // Enhanced UpdateStationDto with Swagger examples
@@ -90,7 +98,7 @@ export class UpdateStationDtoWithExamples {
     type: 'number',
     required: false,
   })
-  long?: number | null;
+  long?: Prisma.Decimal | null;
 }
 
 // Enhanced StationResponseDto with Swagger examples
@@ -171,7 +179,13 @@ export class StationResponseDtoWithExamples {
 @ApiSecurity('access-token')
 export class StationController {
   constructor(
-    // Inject use cases here when they are created
+    private readonly findAllStationsUseCase: FindAllStationsUseCase,
+    private readonly findStationByIdUseCase: FindStationByIdUseCase,
+    private readonly createStationUseCase: CreateStationUseCase,
+    private readonly updateStationUseCase: UpdateStationUseCase,
+    private readonly deleteStationUseCase: DeleteStationUseCase,
+    private readonly searchStationsByNameUseCase: SearchStationsByNameUseCase,
+    private readonly findStationsByCityUseCase: FindStationsByCityUseCase,
   ) {}
 
   @Post()
@@ -198,8 +212,7 @@ export class StationController {
     description: 'Une station avec ce nom existe déjà',
   })
   async create(@Body() createStationDto: CreateStationDtoWithExamples): Promise<StationResponseDtoWithExamples> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return await this.createStationUseCase.execute(createStationDto);
   }
 
   @Get()
@@ -212,9 +225,9 @@ export class StationController {
     description: 'Liste des stations récupérée avec succès',
     type: PaginatedResponseDto<StationResponseDtoWithExamples>,
   })
+  @Public()
   async findAll(@Query() paginationDto: PaginationDto): Promise<PaginatedResponseDto<StationResponseDtoWithExamples>> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return await this.findAllStationsUseCase.execute(paginationDto.page, paginationDto.limit);
   }
 
   @Get(':id')
@@ -237,8 +250,7 @@ export class StationController {
     description: 'Station non trouvée',
   })
   async findOne(@Param('id') id: string): Promise<StationResponseDtoWithExamples> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return await this.findStationByIdUseCase.execute(+id);
   }
 
   @Put(':id')
@@ -273,8 +285,7 @@ export class StationController {
     @Param('id') id: string,
     @Body() updateStationDto: UpdateStationDtoWithExamples,
   ): Promise<StationResponseDtoWithExamples> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return await this.updateStationUseCase.execute(+id, updateStationDto);
   }
 
   @Delete(':id')
@@ -305,8 +316,7 @@ export class StationController {
     description: 'Impossible de supprimer - Station utilisée dans des trajets',
   })
   async remove(@Param('id') id: string): Promise<{ message: string }> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return await this.deleteStationUseCase.execute(+id);
   }
 
   @Get('search/:name')
@@ -325,8 +335,7 @@ export class StationController {
     type: [StationResponseDtoWithExamples],
   })
   async searchByName(@Param('name') name: string): Promise<StationResponseDtoWithExamples[]> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return await this.searchStationsByNameUseCase.execute(name);
   }
 
   @Get('city/:city')
@@ -345,7 +354,6 @@ export class StationController {
     type: [StationResponseDtoWithExamples],
   })
   async findByCity(@Param('city') city: string): Promise<StationResponseDtoWithExamples[]> {
-    // Implementation will be added when use cases are created
-    throw new Error('Not implemented yet');
+    return await this.findStationsByCityUseCase.execute(city);
   }
 }

+ 22 - 2
node_app/src/presentation/controllers/trajet.controller.ts

@@ -25,12 +25,14 @@ import { CreateTrajetUseCase } from '../../application/use-cases/trajet/create-t
 import { UpdateTrajetUseCase } from '../../application/use-cases/trajet/update-trajet.use-case';
 import { FindAllTrajetsUseCase } from '../../application/use-cases/trajet/find-all-trajets.use-case'; // Assurez-vous que ce cas d'utilisation existe
 import { FindTrajetByIdUseCase } from '../../application/use-cases/trajet/find-trajet-by-id.use-case'; // Assurez-vous que ce cas d'utilisation existe
+import { FindTrajetsByCompanyUseCase } from '../../application/use-cases/trajet/find-trajets-by-company.use-case';
 import { DeleteTrajetUseCase } from '../../application/use-cases/trajet/delete-trajet.use-case'; // Assurez-vous que ce cas d'utilisation existe
 import { PaginationDto } from '../../application/dto/pagination.dto';
 import { PaginatedResponseDto } from '../../application/dto/responses/paginated-response.dto';
 import { Roles } from '../decorators/roles.decorator';
 import { UserRole } from 'src/domain/entities/user.entity';
 import { TrajetNotFoundException } from '../../application/exceptions/trajet-not-found.exception';
+import { Public } from '../decorators/public.decorator';
 
 @ApiTags('Trajets')
 @Controller('trajets')
@@ -42,6 +44,7 @@ export class TrajetController {
     private readonly findTrajetByIdUseCase: FindTrajetByIdUseCase,
     private readonly updateTrajetUseCase: UpdateTrajetUseCase,
     private readonly deleteTrajetUseCase: DeleteTrajetUseCase, // Assurez-vous que ce cas d'utilisation est injecté
+    private readonly findTrajetsByCompanyUseCase: FindTrajetsByCompanyUseCase, // Nouveau cas d'utilisation pour les trajets par compagnie
   ) {}
 
   @Post()
@@ -69,12 +72,29 @@ export class TrajetController {
     description: 'Liste paginée de tous les trajets',
     type: PaginatedResponseDto,
   })
-  @Roles(UserRole.ADMIN, UserRole.MANAGER, UserRole.USER, UserRole.DRIVER) // Ouvert à plus de rôles pour la consultation
+  @Public()
   async findAll(@Query() paginationDto: PaginationDto) {
     const { page = 1, limit = 10 } = paginationDto;
     return this.findAllTrajetsUseCase.execute(page, limit);
   }
 
+  @Get('company/:companyId')
+  @ApiOperation({ summary: 'Récupérer les trajets d\'une compagnie' })
+  @ApiParam({ name: 'companyId', description: 'ID de la compagnie', type: 'number' })
+  @ApiResponse({
+    status: 200,
+    description: 'Liste paginée des trajets de la compagnie',
+    type: PaginatedResponseDto,
+  })
+  @Public()
+  async findByCompany(
+    @Param('companyId', ParseIntPipe) companyId: number,
+    @Query() paginationDto: PaginationDto,
+  ) {
+    const { page = 1, limit = 10 } = paginationDto;
+    return this.findTrajetsByCompanyUseCase.execute(companyId, page, limit);
+  }
+
   @Get(':id')
   @ApiOperation({ summary: 'Récupérer un trajet par son ID' })
   @ApiParam({ name: 'id', description: 'ID du trajet', type: 'number' })
@@ -84,7 +104,7 @@ export class TrajetController {
     type: TrajetResponseDto,
   })
   @ApiResponse({ status: 404, description: 'Trajet non trouvé' })
-  @Roles(UserRole.ADMIN, UserRole.MANAGER, UserRole.USER, UserRole.DRIVER)
+  @Public()
   async findOne(@Param('id', ParseIntPipe) id: number) {
     try {
       const trajet = await this.findTrajetByIdUseCase.execute(id);