From 4b056038d63576f3b87f3990e7dcaad5eb37a313 Mon Sep 17 00:00:00 2001 From: Nyavokevin <42602932+nyavokevin@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:17:50 +0300 Subject: [PATCH] add notification et crud fournisseur --- .../Controllers/Api/ContactController.php | 24 + .../app/Http/Requests/StoreContactRequest.php | 15 +- .../Http/Requests/UpdateContactRequest.php | 15 +- .../Resources/Contact/ContactResource.php | 17 +- thanasoft-back/app/Models/Contact.php | 8 +- .../app/Repositories/ContactRepository.php | 7 + .../ContactRepositoryInterface.php | 2 + ...3700_add_fournisseur_to_contacts_table.php | 41 ++ thanasoft-back/routes/api.php | 1 + .../src/components/GlobalNotification.vue | 252 ++++++++ .../CRM/AddFournisseurPresentation.vue | 39 ++ .../CRM/FournisseurDetailPresentation.vue | 32 +- .../Organism/CRM/FournisseurPresentation.vue | 5 +- .../fournisseur/FournisseurDetailContent.vue | 14 +- .../molecules/form/NewClientForm.vue | 13 - .../molecules/form/NewFournisseurForm.vue | 486 ++++++++++++++++ .../fournisseur/FournisseurContactModal.vue | 457 +++++++++++++++ .../fournisseur/FournisseurContactsTab.vue | 269 ++++++++- .../fournisseur/FournisseurInfoTab.vue | 537 +++++++++++++++++- .../fournisseur/FournisseurOverview.vue | 22 +- .../fournisseur/FournisseurTabNavigation.vue | 14 +- .../templates/CRM/NewFournisseurTemplate.vue | 21 + thanasoft-front/src/router/index.js | 5 + thanasoft-front/src/services/contact.ts | 20 +- thanasoft-front/src/services/fournisseur.ts | 206 +++++++ thanasoft-front/src/stores/contactStore.ts | 33 ++ .../src/stores/fournisseurStore.ts | 308 ++++++++++ .../src/views/pages/CRM/AddClient.vue | 15 +- .../src/views/pages/CRM/AddContact.vue | 24 +- .../src/views/pages/CRM/ClientDetails.vue | 34 +- .../pages/Fournisseurs/AddFournisseur.vue | 59 ++ .../pages/Fournisseurs/FournisseurDetails.vue | 181 +++--- .../views/pages/Fournisseurs/Fournisseurs.vue | 103 +--- 33 files changed, 3006 insertions(+), 273 deletions(-) create mode 100644 thanasoft-back/database/migrations/2025_10_29_103700_add_fournisseur_to_contacts_table.php create mode 100644 thanasoft-front/src/components/GlobalNotification.vue create mode 100644 thanasoft-front/src/components/Organism/CRM/AddFournisseurPresentation.vue create mode 100644 thanasoft-front/src/components/molecules/form/NewFournisseurForm.vue create mode 100644 thanasoft-front/src/components/molecules/fournisseur/FournisseurContactModal.vue create mode 100644 thanasoft-front/src/components/templates/CRM/NewFournisseurTemplate.vue create mode 100644 thanasoft-front/src/services/fournisseur.ts create mode 100644 thanasoft-front/src/stores/fournisseurStore.ts create mode 100644 thanasoft-front/src/views/pages/Fournisseurs/AddFournisseur.vue diff --git a/thanasoft-back/app/Http/Controllers/Api/ContactController.php b/thanasoft-back/app/Http/Controllers/Api/ContactController.php index 92b0cbc..493962b 100644 --- a/thanasoft-back/app/Http/Controllers/Api/ContactController.php +++ b/thanasoft-back/app/Http/Controllers/Api/ContactController.php @@ -187,4 +187,28 @@ class ContactController extends Controller ], 500); } } + + + + public function getContactsByFournisseur(string $fournisseurId): JsonResponse + { + try { + $intId = (int) $fournisseurId; + $contacts = $this->contactRepository->getByFournisseurId($intId); + return response()->json([ + 'data' => ContactResource::collection($contacts), + ], 200); + } catch (\Exception $e) { + Log::error('Error fetching contacts by fournisseur: ' . $e->getMessage(), [ + 'exception' => $e, + 'trace' => $e->getTraceAsString(), + 'fournisseur_id' => $fournisseurId, + ]); + + return response()->json([ + 'message' => 'Une erreur est survenue lors de la récupération des contacts du fournisseur.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } } diff --git a/thanasoft-back/app/Http/Requests/StoreContactRequest.php b/thanasoft-back/app/Http/Requests/StoreContactRequest.php index ad8cf09..26f719c 100644 --- a/thanasoft-back/app/Http/Requests/StoreContactRequest.php +++ b/thanasoft-back/app/Http/Requests/StoreContactRequest.php @@ -22,7 +22,8 @@ class StoreContactRequest extends FormRequest public function rules(): array { return [ - 'client_id' => 'required|exists:clients,id', + 'client_id' => 'nullable|exists:clients,id', + 'fournisseur_id' => 'nullable|exists:fournisseurs,id', 'first_name' => 'nullable|string|max:191', 'last_name' => 'nullable|string|max:191', 'email' => 'nullable|email|max:191', @@ -34,8 +35,8 @@ class StoreContactRequest extends FormRequest public function messages(): array { return [ - 'client_id.required' => 'Le client est obligatoire.', 'client_id.exists' => 'Le client sélectionné n\'existe pas.', + 'fournisseur_id.exists' => 'Le fournisseur sélectionné n\'existe pas.', 'first_name.string' => 'Le prénom doit être une chaîne de caractères.', 'first_name.max' => 'Le prénom ne peut pas dépasser 191 caractères.', 'last_name.string' => 'Le nom doit être une chaîne de caractères.', @@ -50,9 +51,17 @@ class StoreContactRequest extends FormRequest public function withValidator($validator) { $validator->after(function ($validator) { + // At least one of client_id or fournisseur_id must be provided + if (empty($this->client_id) && empty($this->fournisseur_id)) { + $validator->errors()->add( + 'general', + 'Le contact doit être associé à un client ou un fournisseur.' + ); + } + if (empty($this->first_name) && empty($this->last_name) && empty($this->email) && empty($this->phone)) { $validator->errors()->add( - 'general', + 'general', 'Au moins un champ (prénom, nom, email ou téléphone) doit être renseigné.' ); } diff --git a/thanasoft-back/app/Http/Requests/UpdateContactRequest.php b/thanasoft-back/app/Http/Requests/UpdateContactRequest.php index 7f65dd4..f960bf0 100644 --- a/thanasoft-back/app/Http/Requests/UpdateContactRequest.php +++ b/thanasoft-back/app/Http/Requests/UpdateContactRequest.php @@ -22,7 +22,8 @@ class UpdateContactRequest extends FormRequest public function rules(): array { return [ - 'client_id' => 'required|exists:clients,id', + 'client_id' => 'nullable|exists:clients,id', + 'fournisseur_id' => 'nullable|exists:fournisseurs,id', 'first_name' => 'nullable|string|max:191', 'last_name' => 'nullable|string|max:191', 'email' => 'nullable|email|max:191', @@ -34,8 +35,8 @@ class UpdateContactRequest extends FormRequest public function messages(): array { return [ - 'client_id.required' => 'Le client est obligatoire.', 'client_id.exists' => 'Le client sélectionné n\'existe pas.', + 'fournisseur_id.exists' => 'Le fournisseur sélectionné n\'existe pas.', 'first_name.string' => 'Le prénom doit être une chaîne de caractères.', 'first_name.max' => 'Le prénom ne peut pas dépasser 191 caractères.', 'last_name.string' => 'Le nom doit être une chaîne de caractères.', @@ -51,9 +52,17 @@ class UpdateContactRequest extends FormRequest public function withValidator($validator) { $validator->after(function ($validator) { + // At least one of client_id or fournisseur_id must be provided + if (empty($this->client_id) && empty($this->fournisseur_id)) { + $validator->errors()->add( + 'general', + 'Le contact doit être associé à un client ou un fournisseur.' + ); + } + if (empty($this->first_name) && empty($this->last_name) && empty($this->email) && empty($this->phone)) { $validator->errors()->add( - 'general', + 'general', 'Au moins un champ (prénom, nom, email ou téléphone) doit être renseigné.' ); } diff --git a/thanasoft-back/app/Http/Resources/Contact/ContactResource.php b/thanasoft-back/app/Http/Resources/Contact/ContactResource.php index 7cafebb..a377c23 100644 --- a/thanasoft-back/app/Http/Resources/Contact/ContactResource.php +++ b/thanasoft-back/app/Http/Resources/Contact/ContactResource.php @@ -18,6 +18,7 @@ class ContactResource extends JsonResource return [ 'id' => $this->id, 'client_id' => $this->client_id, + 'fournisseur_id' => $this->fournisseur_id, 'first_name' => $this->first_name, 'last_name' => $this->last_name, 'full_name' => $this->full_name, @@ -28,10 +29,18 @@ class ContactResource extends JsonResource 'updated_at' => $this->updated_at?->format('Y-m-d H:i:s'), // Relations - 'client' => $this->whenLoaded('client', [ - 'id' => $this->client->id, - 'name' => $this->client->name, - ]), + 'client' => $this->whenLoaded('client', function() { + return $this->client ? [ + 'id' => $this->client->id, + 'name' => $this->client->name, + ] : null; + }), + 'fournisseur' => $this->whenLoaded('fournisseur', function() { + return $this->fournisseur ? [ + 'id' => $this->fournisseur->id, + 'name' => $this->fournisseur->name, + ] : null; + }), ]; } diff --git a/thanasoft-back/app/Models/Contact.php b/thanasoft-back/app/Models/Contact.php index cfe21ea..5511f8f 100644 --- a/thanasoft-back/app/Models/Contact.php +++ b/thanasoft-back/app/Models/Contact.php @@ -16,7 +16,8 @@ class Contact extends Model 'position', 'notes', 'is_primary', - 'client_id' + 'client_id', + 'fournisseur_id' ]; protected $casts = [ @@ -28,6 +29,11 @@ class Contact extends Model return $this->belongsTo(Client::class); } + public function fournisseur(): BelongsTo + { + return $this->belongsTo(Fournisseur::class); + } + /** * Get the contact's full name. */ diff --git a/thanasoft-back/app/Repositories/ContactRepository.php b/thanasoft-back/app/Repositories/ContactRepository.php index 8b4295d..0e56545 100644 --- a/thanasoft-back/app/Repositories/ContactRepository.php +++ b/thanasoft-back/app/Repositories/ContactRepository.php @@ -62,4 +62,11 @@ class ContactRepository extends BaseRepository implements ContactRepositoryInter ->where('client_id', $clientId) ->get(); } + + public function getByFournisseurId(int $fournisseurId) + { + return $this->model->newQuery() + ->where('fournisseur_id', $fournisseurId) + ->get(); + } } diff --git a/thanasoft-back/app/Repositories/ContactRepositoryInterface.php b/thanasoft-back/app/Repositories/ContactRepositoryInterface.php index f8b4984..3eeb2db 100644 --- a/thanasoft-back/app/Repositories/ContactRepositoryInterface.php +++ b/thanasoft-back/app/Repositories/ContactRepositoryInterface.php @@ -9,4 +9,6 @@ interface ContactRepositoryInterface extends BaseRepositoryInterface function paginate(int $perPage = 15, array $filters = []); function getByClientId(int $clientId); + + function getByFournisseurId(int $fournisseurId); } diff --git a/thanasoft-back/database/migrations/2025_10_29_103700_add_fournisseur_to_contacts_table.php b/thanasoft-back/database/migrations/2025_10_29_103700_add_fournisseur_to_contacts_table.php new file mode 100644 index 0000000..52dd402 --- /dev/null +++ b/thanasoft-back/database/migrations/2025_10_29_103700_add_fournisseur_to_contacts_table.php @@ -0,0 +1,41 @@ +dropForeign(['client_id']); + $table->foreignId('client_id')->nullable()->change(); + $table->foreign('client_id')->references('id')->on('clients')->onDelete('set null'); + + // Add fournisseur_id + $table->foreignId('fournisseur_id')->nullable()->after('client_id')->constrained('fournisseurs')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('contacts', function (Blueprint $table) { + // Remove fournisseur_id + $table->dropForeign(['fournisseur_id']); + $table->dropColumn('fournisseur_id'); + + // Restore client_id to not nullable with cascade + $table->dropForeign(['client_id']); + $table->foreignId('client_id')->change(); + $table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade'); + }); + } +}; diff --git a/thanasoft-back/routes/api.php b/thanasoft-back/routes/api.php index 50ae280..63bdeff 100644 --- a/thanasoft-back/routes/api.php +++ b/thanasoft-back/routes/api.php @@ -53,4 +53,5 @@ Route::middleware('auth:sanctum')->group(function () { // Fournisseur management Route::get('/fournisseurs/searchBy', [FournisseurController::class, 'searchBy']); Route::apiResource('fournisseurs', FournisseurController::class); + Route::get('fournisseurs/{fournisseurId}/contacts', [ContactController::class, 'getContactsByFournisseur']); }); diff --git a/thanasoft-front/src/components/GlobalNotification.vue b/thanasoft-front/src/components/GlobalNotification.vue new file mode 100644 index 0000000..26e7d70 --- /dev/null +++ b/thanasoft-front/src/components/GlobalNotification.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/thanasoft-front/src/components/Organism/CRM/AddFournisseurPresentation.vue b/thanasoft-front/src/components/Organism/CRM/AddFournisseurPresentation.vue new file mode 100644 index 0000000..8a6ee5c --- /dev/null +++ b/thanasoft-front/src/components/Organism/CRM/AddFournisseurPresentation.vue @@ -0,0 +1,39 @@ + + diff --git a/thanasoft-front/src/components/Organism/CRM/FournisseurDetailPresentation.vue b/thanasoft-front/src/components/Organism/CRM/FournisseurDetailPresentation.vue index 19957e6..aeadbf2 100644 --- a/thanasoft-front/src/components/Organism/CRM/FournisseurDetailPresentation.vue +++ b/thanasoft-front/src/components/Organism/CRM/FournisseurDetailPresentation.vue @@ -23,7 +23,7 @@ :initials="getInitials(fournisseur.name)" :fournisseur-name="fournisseur.name" :fournisseur-type="fournisseur.type_label || 'Fournisseur'" - :contacts-count="contacts.length" + :contacts-count="filteredContactsCount" :locations-count="locations.length" :is-active="fournisseur.is_active" :active-tab="activeTab" @@ -41,10 +41,9 @@ /> diff --git a/thanasoft-front/src/components/molecules/form/NewClientForm.vue b/thanasoft-front/src/components/molecules/form/NewClientForm.vue index 56d8e26..4c76063 100644 --- a/thanasoft-front/src/components/molecules/form/NewClientForm.vue +++ b/thanasoft-front/src/components/molecules/form/NewClientForm.vue @@ -3,19 +3,6 @@
Nouveau Client

Informations du client

- - -
diff --git a/thanasoft-front/src/components/molecules/form/NewFournisseurForm.vue b/thanasoft-front/src/components/molecules/form/NewFournisseurForm.vue new file mode 100644 index 0000000..ea89bab --- /dev/null +++ b/thanasoft-front/src/components/molecules/form/NewFournisseurForm.vue @@ -0,0 +1,486 @@ + + + + + diff --git a/thanasoft-front/src/components/molecules/fournisseur/FournisseurContactModal.vue b/thanasoft-front/src/components/molecules/fournisseur/FournisseurContactModal.vue new file mode 100644 index 0000000..2ffe4dc --- /dev/null +++ b/thanasoft-front/src/components/molecules/fournisseur/FournisseurContactModal.vue @@ -0,0 +1,457 @@ + + + + + diff --git a/thanasoft-front/src/components/molecules/fournisseur/FournisseurContactsTab.vue b/thanasoft-front/src/components/molecules/fournisseur/FournisseurContactsTab.vue index b9efcdd..7683fd6 100644 --- a/thanasoft-front/src/components/molecules/fournisseur/FournisseurContactsTab.vue +++ b/thanasoft-front/src/components/molecules/fournisseur/FournisseurContactsTab.vue @@ -1,21 +1,148 @@ + + diff --git a/thanasoft-front/src/components/molecules/fournisseur/FournisseurInfoTab.vue b/thanasoft-front/src/components/molecules/fournisseur/FournisseurInfoTab.vue index bfb0312..3b5d928 100644 --- a/thanasoft-front/src/components/molecules/fournisseur/FournisseurInfoTab.vue +++ b/thanasoft-front/src/components/molecules/fournisseur/FournisseurInfoTab.vue @@ -1,21 +1,548 @@ + + diff --git a/thanasoft-front/src/components/molecules/fournisseur/FournisseurOverview.vue b/thanasoft-front/src/components/molecules/fournisseur/FournisseurOverview.vue index af1e7a8..7053980 100644 --- a/thanasoft-front/src/components/molecules/fournisseur/FournisseurOverview.vue +++ b/thanasoft-front/src/components/molecules/fournisseur/FournisseurOverview.vue @@ -56,7 +56,7 @@ title="Contacts récents" icon="fas fa-address-book text-info" > -