Planning et agenda: nouveau flux de création et formulaires de demande
This commit is contained in:
parent
a9a2429b67
commit
083f78673e
@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
<div class="modal-body p-3 md:p-6">
|
<div class="modal-body p-3 md:p-6">
|
||||||
<form class="space-y-4 md:space-y-6" @submit.prevent="handleSubmit">
|
<form class="space-y-4 md:space-y-6" @submit.prevent="handleSubmit">
|
||||||
|
|
||||||
<!-- Global Errors -->
|
<!-- Global Errors -->
|
||||||
<div
|
<div
|
||||||
v-if="globalErrors.length"
|
v-if="globalErrors.length"
|
||||||
@ -43,14 +42,22 @@
|
|||||||
<!-- ROW 1: Défunt -->
|
<!-- ROW 1: Défunt -->
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
<div
|
||||||
<label class="form-label fw-bold small text-dark mb-0">Défunt *</label>
|
class="d-flex justify-content-between align-items-center mb-1"
|
||||||
|
>
|
||||||
|
<label class="form-label fw-bold small text-dark mb-0"
|
||||||
|
>Défunt *</label
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-outline-primary py-0 px-2"
|
class="btn btn-sm btn-outline-primary py-0 px-2"
|
||||||
@click="toggleDeceasedMode"
|
@click="toggleDeceasedMode"
|
||||||
>
|
>
|
||||||
{{ deceasedForm.is_existing ? "+ Créer défunt" : "Rechercher défunt" }}
|
{{
|
||||||
|
deceasedForm.is_existing
|
||||||
|
? "+ Créer défunt"
|
||||||
|
: "Rechercher défunt"
|
||||||
|
}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -69,15 +76,27 @@
|
|||||||
@input="handleDeceasedSearch"
|
@input="handleDeceasedSearch"
|
||||||
@focus="showDeceasedResults = true"
|
@focus="showDeceasedResults = true"
|
||||||
/>
|
/>
|
||||||
<button v-if="deceasedForm.id" class="btn btn-outline-secondary" type="button" @click="clearDeceasedSelection">
|
<button
|
||||||
|
v-if="deceasedForm.id"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="clearDeceasedSelection"
|
||||||
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getFieldError('deceased_id')" class="invalid-feedback d-block">
|
<div
|
||||||
|
v-if="getFieldError('deceased_id')"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
|
>
|
||||||
{{ getFieldError("deceased_id") }}
|
{{ getFieldError("deceased_id") }}
|
||||||
</div>
|
</div>
|
||||||
<!-- Results -->
|
<!-- Results -->
|
||||||
<div v-if="showDeceasedResults && deceasedSearchResults.length" class="list-group position-absolute w-100 shadow mt-1" style="z-index: 1050; max-height: 200px; overflow-y: auto;">
|
<div
|
||||||
|
v-if="showDeceasedResults && deceasedSearchResults.length"
|
||||||
|
class="list-group position-absolute w-100 shadow mt-1"
|
||||||
|
style="z-index: 1050; max-height: 200px; overflow-y: auto"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="d in deceasedSearchResults"
|
v-for="d in deceasedSearchResults"
|
||||||
:key="d.id"
|
:key="d.id"
|
||||||
@ -86,8 +105,12 @@
|
|||||||
@click="selectDeceased(d)"
|
@click="selectDeceased(d)"
|
||||||
>
|
>
|
||||||
<div class="d-flex w-100 justify-content-between">
|
<div class="d-flex w-100 justify-content-between">
|
||||||
<h6 class="mb-1 text-sm">{{ d.first_name }} {{ d.last_name }}</h6>
|
<h6 class="mb-1 text-sm">
|
||||||
<small class="text-xs text-muted">{{ d.birth_date }} - {{ d.death_date }}</small>
|
{{ d.first_name }} {{ d.last_name }}
|
||||||
|
</h6>
|
||||||
|
<small class="text-xs text-muted"
|
||||||
|
>{{ d.birth_date }} - {{ d.death_date }}</small
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -97,23 +120,53 @@
|
|||||||
<div v-else class="card card-body bg-light border-0 p-3">
|
<div v-else class="card card-body bg-light border-0 p-3">
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label small text-muted mb-1">Nom *</label>
|
<label class="form-label small text-muted mb-1"
|
||||||
<input v-model="deceasedForm.last_name" class="form-control form-control-sm" :class="{ 'is-invalid': hasError('deceased.last_name') }">
|
>Nom *</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="deceasedForm.last_name"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
:class="{
|
||||||
|
'is-invalid': hasError('deceased.last_name'),
|
||||||
|
}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label small text-muted mb-1">Prénom</label>
|
<label class="form-label small text-muted mb-1"
|
||||||
<input v-model="deceasedForm.first_name" class="form-control form-control-sm">
|
>Prénom</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="deceasedForm.first_name"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label small text-muted mb-1">Date Naissance</label>
|
<label class="form-label small text-muted mb-1"
|
||||||
<input v-model="deceasedForm.birth_date" type="date" class="form-control form-control-sm">
|
>Date Naissance</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="deceasedForm.birth_date"
|
||||||
|
type="date"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label small text-muted mb-1">Date Décès</label>
|
<label class="form-label small text-muted mb-1"
|
||||||
<input v-model="deceasedForm.death_date" type="date" class="form-control form-control-sm">
|
>Date Décès</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-model="deceasedForm.death_date"
|
||||||
|
type="date"
|
||||||
|
class="form-control form-control-sm"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12" v-if="getFieldError('deceased.last_name')">
|
<div
|
||||||
<small class="text-danger">{{ getFieldError('deceased.last_name') }}</small>
|
v-if="getFieldError('deceased.last_name')"
|
||||||
|
class="col-12"
|
||||||
|
>
|
||||||
|
<small class="text-danger">{{
|
||||||
|
getFieldError("deceased.last_name")
|
||||||
|
}}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -123,7 +176,9 @@
|
|||||||
<!-- ROW 2: Client -->
|
<!-- ROW 2: Client -->
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-12 position-relative">
|
<div class="col-12 position-relative">
|
||||||
<label class="form-label fw-bold small text-dark">Client (Donneur d'ordre) *</label>
|
<label class="form-label fw-bold small text-dark"
|
||||||
|
>Client (Donneur d'ordre) *</label
|
||||||
|
>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text bg-white border-end-0">
|
<span class="input-group-text bg-white border-end-0">
|
||||||
<i class="fas fa-user text-muted"></i>
|
<i class="fas fa-user text-muted"></i>
|
||||||
@ -137,15 +192,27 @@
|
|||||||
@input="handleClientSearch"
|
@input="handleClientSearch"
|
||||||
@focus="showClientResults = true"
|
@focus="showClientResults = true"
|
||||||
/>
|
/>
|
||||||
<button v-if="selectedClient" class="btn btn-outline-secondary" type="button" @click="clearClientSelection">
|
<button
|
||||||
|
v-if="selectedClient"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="clearClientSelection"
|
||||||
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getFieldError('client')" class="invalid-feedback d-block">
|
<div
|
||||||
|
v-if="getFieldError('client')"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
|
>
|
||||||
{{ getFieldError("client") }}
|
{{ getFieldError("client") }}
|
||||||
</div>
|
</div>
|
||||||
<!-- Results -->
|
<!-- Results -->
|
||||||
<div v-if="showClientResults && clientSearchResults.length" class="list-group position-absolute w-100 shadow mt-1" style="z-index: 1050; max-height: 200px; overflow-y: auto;">
|
<div
|
||||||
|
v-if="showClientResults && clientSearchResults.length"
|
||||||
|
class="list-group position-absolute w-100 shadow mt-1"
|
||||||
|
style="z-index: 1050; max-height: 200px; overflow-y: auto"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="c in clientSearchResults"
|
v-for="c in clientSearchResults"
|
||||||
:key="c.id"
|
:key="c.id"
|
||||||
@ -165,20 +232,27 @@
|
|||||||
<!-- ROW 3: Date et Type -->
|
<!-- ROW 3: Date et Type -->
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-md-12 mb-3">
|
<div class="col-md-12 mb-3">
|
||||||
<label class="form-label fw-bold small text-dark">Date et heure *</label>
|
<label class="form-label fw-bold small text-dark"
|
||||||
|
>Date et heure *</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
v-model="interventionForm.scheduled_at"
|
v-model="interventionForm.scheduled_at"
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:class="{ 'is-invalid': hasError('scheduled_at') }"
|
:class="{ 'is-invalid': hasError('scheduled_at') }"
|
||||||
required
|
required
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="getFieldError('scheduled_at')"
|
||||||
|
class="invalid-feedback"
|
||||||
>
|
>
|
||||||
<div v-if="getFieldError('scheduled_at')" class="invalid-feedback">
|
|
||||||
{{ getFieldError("scheduled_at") }}
|
{{ getFieldError("scheduled_at") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-12 position-relative">
|
<div class="col-md-12 position-relative">
|
||||||
<label class="form-label fw-bold small text-dark">Type de soin *</label>
|
<label class="form-label fw-bold small text-dark"
|
||||||
|
>Type de soin *</label
|
||||||
|
>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text bg-white border-end-0">
|
<span class="input-group-text bg-white border-end-0">
|
||||||
<i class="fas fa-medkit text-muted"></i>
|
<i class="fas fa-medkit text-muted"></i>
|
||||||
@ -190,17 +264,29 @@
|
|||||||
:class="{ 'is-invalid': hasError('product_id') }"
|
:class="{ 'is-invalid': hasError('product_id') }"
|
||||||
placeholder="Rechercher un type de soin..."
|
placeholder="Rechercher un type de soin..."
|
||||||
@input="handleProductSearch"
|
@input="handleProductSearch"
|
||||||
@focus="showProductResults = true"
|
@focus="handleProductFocus"
|
||||||
/>
|
/>
|
||||||
<button v-if="productForm.product_id" class="btn btn-outline-secondary" type="button" @click="clearProductSelection">
|
<button
|
||||||
|
v-if="productForm.product_id"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="clearProductSelection"
|
||||||
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getFieldError('product_id')" class="invalid-feedback d-block">
|
<div
|
||||||
|
v-if="getFieldError('product_id')"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
|
>
|
||||||
{{ getFieldError("product_id") }}
|
{{ getFieldError("product_id") }}
|
||||||
</div>
|
</div>
|
||||||
<!-- Results -->
|
<!-- Results -->
|
||||||
<div v-if="showProductResults && productSearchResults.length" class="list-group position-absolute w-100 shadow mt-1" style="z-index: 1050; max-height: 200px; overflow-y: auto;">
|
<div
|
||||||
|
v-if="showProductResults && productSearchResults.length"
|
||||||
|
class="list-group position-absolute w-100 shadow mt-1"
|
||||||
|
style="z-index: 1050; max-height: 200px; overflow-y: auto"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="p in productSearchResults"
|
v-for="p in productSearchResults"
|
||||||
:key="p.id"
|
:key="p.id"
|
||||||
@ -210,7 +296,9 @@
|
|||||||
>
|
>
|
||||||
<div class="d-flex w-100 justify-content-between">
|
<div class="d-flex w-100 justify-content-between">
|
||||||
<h6 class="mb-1 text-sm">{{ p.nom }}</h6>
|
<h6 class="mb-1 text-sm">{{ p.nom }}</h6>
|
||||||
<small class="text-xs text-muted">{{ p.price ? `${p.price}€` : '' }}</small>
|
<small class="text-xs text-muted">{{
|
||||||
|
p.price ? `${p.price}€` : ""
|
||||||
|
}}</small>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -220,27 +308,47 @@
|
|||||||
<!-- ROW 4: Intervenant -->
|
<!-- ROW 4: Intervenant -->
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label fw-bold small text-dark">Intervenant *</label>
|
<label class="form-label fw-bold small text-dark"
|
||||||
|
>Intervenant *</label
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-2">
|
<div class="col-md-6 mb-2">
|
||||||
<label class="form-label text-xs text-muted mb-1">Salarié Thanatopracteur</label>
|
<label class="form-label text-xs text-muted mb-1"
|
||||||
<select v-model="interventionForm.assigned_practitioner_id" class="form-select" :class="{ 'is-invalid': hasError('assigned_practitioner_id') }">
|
>Salarié Thanatopracteur</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
v-model="interventionForm.assigned_practitioner_id"
|
||||||
|
class="form-select"
|
||||||
|
:class="{
|
||||||
|
'is-invalid': hasError('assigned_practitioner_id'),
|
||||||
|
}"
|
||||||
|
>
|
||||||
<option value="">Sélectionner un salarié</option>
|
<option value="">Sélectionner un salarié</option>
|
||||||
<option
|
<option
|
||||||
v-for="practitioner in practitioners"
|
v-for="practitioner in practitioners"
|
||||||
:key="practitioner.id"
|
:key="practitioner.id"
|
||||||
:value="practitioner.id"
|
:value="practitioner.id"
|
||||||
>
|
>
|
||||||
{{ practitioner.employee?.first_name }} {{ practitioner.employee?.last_name }}
|
{{ practitioner.employee?.first_name }}
|
||||||
|
{{ practitioner.employee?.last_name }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<div v-if="getFieldError('assigned_practitioner_id')" class="invalid-feedback d-block">
|
<div
|
||||||
|
v-if="getFieldError('assigned_practitioner_id')"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
|
>
|
||||||
{{ getFieldError("assigned_practitioner_id") }}
|
{{ getFieldError("assigned_practitioner_id") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-2">
|
<div class="col-md-6 mb-2">
|
||||||
<label class="form-label text-xs text-muted mb-1">OU Sous-traitant</label>
|
<label class="form-label text-xs text-muted mb-1"
|
||||||
<select class="form-select bg-light border-0" disabled title="Bientôt disponible">
|
>OU Sous-traitant</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
class="form-select bg-light border-0"
|
||||||
|
disabled
|
||||||
|
title="Bientôt disponible"
|
||||||
|
>
|
||||||
<option value="">Sélectionner un sous-traitant</option>
|
<option value="">Sélectionner un sous-traitant</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -249,7 +357,9 @@
|
|||||||
<!-- ROW 5: Numéro Prescription -->
|
<!-- ROW 5: Numéro Prescription -->
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label fw-bold small text-dark">Numéro de prescription médicale</label>
|
<label class="form-label fw-bold small text-dark"
|
||||||
|
>Numéro de prescription médicale</label
|
||||||
|
>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text bg-white border-end-0">
|
<span class="input-group-text bg-white border-end-0">
|
||||||
<i class="fas fa-prescription text-muted"></i>
|
<i class="fas fa-prescription text-muted"></i>
|
||||||
@ -259,26 +369,42 @@
|
|||||||
type="text"
|
type="text"
|
||||||
class="form-control border-start-0"
|
class="form-control border-start-0"
|
||||||
placeholder="Ex: RX-2024-001 (si fourni par la famille)"
|
placeholder="Ex: RX-2024-001 (si fourni par la famille)"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
title="Activer la saisie vocale"
|
||||||
|
@click="toggleVoiceInput('medical_prescription_number')"
|
||||||
>
|
>
|
||||||
<button class="btn btn-outline-secondary" type="button" title="Activer la saisie vocale" @click="toggleVoiceInput('medical_prescription_number')">
|
|
||||||
<i class="fas fa-microphone"></i>
|
<i class="fas fa-microphone"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-xs text-muted mt-1">📋 Optionnel - À ajouter si fourni par la famille ou dans les documents reçus</p>
|
<p class="text-xs text-muted mt-1">
|
||||||
|
📋 Optionnel - À ajouter si fourni par la famille ou dans les
|
||||||
|
documents reçus
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ROW 6: Lieu -->
|
<!-- ROW 6: Lieu -->
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1">
|
<div
|
||||||
<label class="form-label fw-bold small text-dark mb-0">Lieu d'intervention</label>
|
class="d-flex justify-content-between align-items-center mb-1"
|
||||||
|
>
|
||||||
|
<label class="form-label fw-bold small text-dark mb-0"
|
||||||
|
>Lieu d'intervention</label
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-outline-primary py-0 px-2"
|
class="btn btn-sm btn-outline-primary py-0 px-2"
|
||||||
@click="toggleLocationMode"
|
@click="toggleLocationMode"
|
||||||
>
|
>
|
||||||
{{ locationForm.is_existing ? "+ Nouveau lieu" : "Rechercher lieu" }}
|
{{
|
||||||
|
locationForm.is_existing
|
||||||
|
? "+ Nouveau lieu"
|
||||||
|
: "Rechercher lieu"
|
||||||
|
}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -292,17 +418,29 @@
|
|||||||
v-model="locationSearchQuery"
|
v-model="locationSearchQuery"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control border-start-0"
|
class="form-control border-start-0"
|
||||||
:class="{ 'is-invalid': hasError('location.name') && !locationForm.id }"
|
:class="{
|
||||||
|
'is-invalid':
|
||||||
|
hasError('location.name') && !locationForm.id,
|
||||||
|
}"
|
||||||
placeholder="Rechercher un lieu..."
|
placeholder="Rechercher un lieu..."
|
||||||
@input="handleLocationSearch"
|
@input="handleLocationSearch"
|
||||||
@focus="showLocationResults = true"
|
@focus="showLocationResults = true"
|
||||||
/>
|
/>
|
||||||
<button v-if="locationForm.id" class="btn btn-outline-secondary" type="button" @click="clearLocationSelection">
|
<button
|
||||||
|
v-if="locationForm.id"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="clearLocationSelection"
|
||||||
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- Results -->
|
<!-- Results -->
|
||||||
<div v-if="showLocationResults && locationSearchResults.length" class="list-group position-absolute w-100 shadow mt-1" style="z-index: 1050; max-height: 200px; overflow-y: auto;">
|
<div
|
||||||
|
v-if="showLocationResults && locationSearchResults.length"
|
||||||
|
class="list-group position-absolute w-100 shadow mt-1"
|
||||||
|
style="z-index: 1050; max-height: 200px; overflow-y: auto"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="loc in locationSearchResults"
|
v-for="loc in locationSearchResults"
|
||||||
:key="loc.id"
|
:key="loc.id"
|
||||||
@ -316,7 +454,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getFieldError('location.name') && !locationForm.id" class="invalid-feedback d-block">
|
<div
|
||||||
|
v-if="getFieldError('location.name') && !locationForm.id"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
|
>
|
||||||
{{ getFieldError("location.name") }}
|
{{ getFieldError("location.name") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -325,24 +466,31 @@
|
|||||||
<div v-else class="card card-body bg-light border-0 p-3">
|
<div v-else class="card card-body bg-light border-0 p-3">
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label small text-muted mb-1">Nom du lieu *</label>
|
<label class="form-label small text-muted mb-1"
|
||||||
|
>Nom du lieu *</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
v-model="locationForm.name"
|
v-model="locationForm.name"
|
||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm"
|
||||||
placeholder="Ex: Domicile, Eglise St-Pierre..."
|
placeholder="Ex: Domicile, Eglise St-Pierre..."
|
||||||
:class="{ 'is-invalid': hasError('location.name') }"
|
:class="{ 'is-invalid': hasError('location.name') }"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="getFieldError('location.name')"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
>
|
>
|
||||||
<div v-if="getFieldError('location.name')" class="invalid-feedback d-block">
|
|
||||||
{{ getFieldError("location.name") }}
|
{{ getFieldError("location.name") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label small text-muted mb-1">Ville</label>
|
<label class="form-label small text-muted mb-1"
|
||||||
|
>Ville</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
v-model="locationForm.city"
|
v-model="locationForm.city"
|
||||||
class="form-control form-control-sm"
|
class="form-control form-control-sm"
|
||||||
placeholder="Ex: Paris"
|
placeholder="Ex: Paris"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -367,9 +515,13 @@
|
|||||||
<!-- ROW 8: Observations -->
|
<!-- ROW 8: Observations -->
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label fw-bold small text-dark">Observations techniques</label>
|
<label class="form-label fw-bold small text-dark"
|
||||||
|
>Observations techniques</label
|
||||||
|
>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text bg-white align-items-start border-end-0 pt-2">
|
<span
|
||||||
|
class="input-group-text bg-white align-items-start border-end-0 pt-2"
|
||||||
|
>
|
||||||
<i class="fas fa-sticky-note text-muted"></i>
|
<i class="fas fa-sticky-note text-muted"></i>
|
||||||
</span>
|
</span>
|
||||||
<textarea
|
<textarea
|
||||||
@ -378,7 +530,12 @@
|
|||||||
rows="4"
|
rows="4"
|
||||||
placeholder="Notes et observations..."
|
placeholder="Notes et observations..."
|
||||||
></textarea>
|
></textarea>
|
||||||
<button class="btn btn-outline-secondary align-items-start pt-2" type="button" title="Activer la saisie vocale" @click="toggleVoiceInput('notes')">
|
<button
|
||||||
|
class="btn btn-outline-secondary align-items-start pt-2"
|
||||||
|
type="button"
|
||||||
|
title="Activer la saisie vocale"
|
||||||
|
@click="toggleVoiceInput('notes')"
|
||||||
|
>
|
||||||
<i class="fas fa-microphone"></i>
|
<i class="fas fa-microphone"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -386,19 +543,40 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Footer Actions -->
|
<!-- Footer Actions -->
|
||||||
<div class="border-top pt-3 d-flex flex-column flex-md-row justify-content-between gap-2">
|
<div
|
||||||
<button type="button" class="btn btn-outline-success d-flex align-items-center justify-content-center gap-2" disabled>
|
class="border-top pt-3 d-flex flex-column flex-md-row justify-content-between gap-2"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-success d-flex align-items-center justify-content-center gap-2"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
<i class="fas fa-envelope"></i> Récupérer docs emails
|
<i class="fas fa-envelope"></i> Récupérer docs emails
|
||||||
</button>
|
</button>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button type="button" class="btn btn-outline-secondary w-100 w-md-auto" data-bs-dismiss="modal" :disabled="submitting">Annuler</button>
|
<button
|
||||||
<button type="submit" class="btn btn-primary w-100 w-md-auto text-white" :disabled="submitting">
|
type="button"
|
||||||
<span v-if="submitting" class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
class="btn btn-outline-secondary w-100 w-md-auto"
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
:disabled="submitting"
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary w-100 w-md-auto text-white"
|
||||||
|
:disabled="submitting"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="submitting"
|
||||||
|
class="spinner-border spinner-border-sm me-2"
|
||||||
|
role="status"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
{{ isEditing ? "Mettre à jour" : "Créer l'intervention" }}
|
{{ isEditing ? "Mettre à jour" : "Créer l'intervention" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -414,7 +592,7 @@ import {
|
|||||||
defineEmits,
|
defineEmits,
|
||||||
defineExpose,
|
defineExpose,
|
||||||
onMounted,
|
onMounted,
|
||||||
nextTick
|
nextTick,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import DeceasedService from "@/services/deceased";
|
import DeceasedService from "@/services/deceased";
|
||||||
@ -470,12 +648,13 @@ let clientSearchTimeout;
|
|||||||
|
|
||||||
// Product
|
// Product
|
||||||
const productForm = ref({
|
const productForm = ref({
|
||||||
product_id: null
|
product_id: null,
|
||||||
});
|
});
|
||||||
const productSearchQuery = ref("");
|
const productSearchQuery = ref("");
|
||||||
const productSearchResults = ref([]);
|
const productSearchResults = ref([]);
|
||||||
const allProducts = ref([]);
|
const allProducts = ref([]);
|
||||||
const showProductResults = ref(false);
|
const showProductResults = ref(false);
|
||||||
|
let productSearchTimeout;
|
||||||
|
|
||||||
// Location - FIXED: Start with search mode (true = search existing, false = create new)
|
// Location - FIXED: Start with search mode (true = search existing, false = create new)
|
||||||
const locationForm = ref({
|
const locationForm = ref({
|
||||||
@ -521,10 +700,17 @@ onMounted(async () => {
|
|||||||
const pService = new ProductService();
|
const pService = new ProductService();
|
||||||
const response = await pService.getAllProducts({
|
const response = await pService.getAllProducts({
|
||||||
per_page: 100,
|
per_page: 100,
|
||||||
is_intervention: true
|
is_intervention: true,
|
||||||
});
|
});
|
||||||
allProducts.value = response.data;
|
|
||||||
productSearchResults.value = allProducts.value;
|
const products = Array.isArray(response?.data)
|
||||||
|
? response.data
|
||||||
|
: Array.isArray(response?.data?.data)
|
||||||
|
? response.data.data
|
||||||
|
: [];
|
||||||
|
|
||||||
|
allProducts.value = products;
|
||||||
|
productSearchResults.value = products;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to load products", e);
|
console.error("Failed to load products", e);
|
||||||
}
|
}
|
||||||
@ -561,20 +747,24 @@ const handleDeceasedSearch = () => {
|
|||||||
}
|
}
|
||||||
deceasedSearchTimeout = setTimeout(async () => {
|
deceasedSearchTimeout = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const results = await DeceasedService.searchDeceased(deceasedSearchQuery.value);
|
const results = await DeceasedService.searchDeceased(
|
||||||
|
deceasedSearchQuery.value
|
||||||
|
);
|
||||||
deceasedSearchResults.value = results;
|
deceasedSearchResults.value = results;
|
||||||
} catch(e) { console.error(e); }
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectDeceased = (d) => {
|
const selectDeceased = (d) => {
|
||||||
deceasedForm.value.id = d.id;
|
deceasedForm.value.id = d.id;
|
||||||
deceasedSearchQuery.value = `${d.first_name || ''} ${d.last_name}`;
|
deceasedSearchQuery.value = `${d.first_name || ""} ${d.last_name}`;
|
||||||
deceasedSearchResults.value = [];
|
deceasedSearchResults.value = [];
|
||||||
showDeceasedResults.value = false;
|
showDeceasedResults.value = false;
|
||||||
|
|
||||||
// Clear any error for deceased
|
// Clear any error for deceased
|
||||||
errors.value = errors.value.filter(e => e.field !== 'deceased_id');
|
errors.value = errors.value.filter((e) => e.field !== "deceased_id");
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearDeceasedSelection = () => {
|
const clearDeceasedSelection = () => {
|
||||||
@ -587,9 +777,8 @@ const clearDeceasedSelection = () => {
|
|||||||
deceasedSearchResults.value = [];
|
deceasedSearchResults.value = [];
|
||||||
|
|
||||||
// Clear errors
|
// Clear errors
|
||||||
errors.value = errors.value.filter(e =>
|
errors.value = errors.value.filter(
|
||||||
e.field !== 'deceased_id' &&
|
(e) => e.field !== "deceased_id" && e.field !== "deceased.last_name"
|
||||||
e.field !== 'deceased.last_name'
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -602,20 +791,24 @@ const handleClientSearch = () => {
|
|||||||
}
|
}
|
||||||
clientSearchTimeout = setTimeout(async () => {
|
clientSearchTimeout = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const results = await ClientService.searchClients(clientSearchQuery.value);
|
const results = await ClientService.searchClients(
|
||||||
|
clientSearchQuery.value
|
||||||
|
);
|
||||||
clientSearchResults.value = results;
|
clientSearchResults.value = results;
|
||||||
} catch(e) { console.error(e); }
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectClient = (c) => {
|
const selectClient = (c) => {
|
||||||
selectedClient.value = c;
|
selectedClient.value = c;
|
||||||
clientSearchQuery.value = c.name + (c.email ? ` (${c.email})` : '');
|
clientSearchQuery.value = c.name + (c.email ? ` (${c.email})` : "");
|
||||||
clientSearchResults.value = [];
|
clientSearchResults.value = [];
|
||||||
showClientResults.value = false;
|
showClientResults.value = false;
|
||||||
|
|
||||||
// Clear any error for client
|
// Clear any error for client
|
||||||
errors.value = errors.value.filter(e => e.field !== 'client');
|
errors.value = errors.value.filter((e) => e.field !== "client");
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearClientSelection = () => {
|
const clearClientSelection = () => {
|
||||||
@ -624,19 +817,45 @@ const clearClientSelection = () => {
|
|||||||
clientSearchResults.value = [];
|
clientSearchResults.value = [];
|
||||||
|
|
||||||
// Clear error
|
// Clear error
|
||||||
errors.value = errors.value.filter(e => e.field !== 'client');
|
errors.value = errors.value.filter((e) => e.field !== "client");
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- PRODUCT LOGIC ---
|
// --- PRODUCT LOGIC ---
|
||||||
const handleProductSearch = () => {
|
const handleProductSearch = () => {
|
||||||
const query = productSearchQuery.value.toLowerCase();
|
showProductResults.value = true;
|
||||||
if (!query) {
|
|
||||||
productSearchResults.value = allProducts.value;
|
if (productSearchTimeout) clearTimeout(productSearchTimeout);
|
||||||
return;
|
|
||||||
|
productSearchTimeout = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const pService = new ProductService();
|
||||||
|
const response = await pService.getAllProducts({
|
||||||
|
per_page: 100,
|
||||||
|
is_intervention: true,
|
||||||
|
search: productSearchQuery.value || undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const products = Array.isArray(response?.data)
|
||||||
|
? response.data
|
||||||
|
: Array.isArray(response?.data?.data)
|
||||||
|
? response.data.data
|
||||||
|
: [];
|
||||||
|
|
||||||
|
productSearchResults.value = products;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to search intervention products", e);
|
||||||
|
productSearchResults.value = [];
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleProductFocus = () => {
|
||||||
|
showProductResults.value = true;
|
||||||
|
if (!productSearchQuery.value) {
|
||||||
|
productSearchResults.value = allProducts.value;
|
||||||
|
} else {
|
||||||
|
handleProductSearch();
|
||||||
}
|
}
|
||||||
productSearchResults.value = allProducts.value.filter(p =>
|
|
||||||
p.nom.toLowerCase().includes(query)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectProduct = (p) => {
|
const selectProduct = (p) => {
|
||||||
@ -645,10 +864,10 @@ const selectProduct = (p) => {
|
|||||||
showProductResults.value = false;
|
showProductResults.value = false;
|
||||||
|
|
||||||
// Clear error
|
// Clear error
|
||||||
errors.value = errors.value.filter(e => e.field !== 'product_id');
|
errors.value = errors.value.filter((e) => e.field !== "product_id");
|
||||||
|
|
||||||
if (!interventionForm.value.type) {
|
if (!interventionForm.value.type) {
|
||||||
interventionForm.value.type = 'thanatopraxie';
|
interventionForm.value.type = "thanatopraxie";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -658,7 +877,7 @@ const clearProductSelection = () => {
|
|||||||
productSearchResults.value = allProducts.value;
|
productSearchResults.value = allProducts.value;
|
||||||
|
|
||||||
// Clear error
|
// Clear error
|
||||||
errors.value = errors.value.filter(e => e.field !== 'product_id');
|
errors.value = errors.value.filter((e) => e.field !== "product_id");
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- LOCATION LOGIC --- (FIXED)
|
// --- LOCATION LOGIC --- (FIXED)
|
||||||
@ -676,7 +895,7 @@ const toggleLocationMode = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear location errors
|
// Clear location errors
|
||||||
errors.value = errors.value.filter(e => e.field === 'location.name');
|
errors.value = errors.value.filter((e) => e.field === "location.name");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLocationSearch = () => {
|
const handleLocationSearch = () => {
|
||||||
@ -689,10 +908,12 @@ const handleLocationSearch = () => {
|
|||||||
try {
|
try {
|
||||||
const response = await ClientLocationService.getAllClientLocations({
|
const response = await ClientLocationService.getAllClientLocations({
|
||||||
search: locationSearchQuery.value,
|
search: locationSearchQuery.value,
|
||||||
per_page: 10
|
per_page: 10,
|
||||||
});
|
});
|
||||||
locationSearchResults.value = response.data;
|
locationSearchResults.value = response.data;
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -707,7 +928,7 @@ const selectLocation = (loc) => {
|
|||||||
locationSearchResults.value = [];
|
locationSearchResults.value = [];
|
||||||
|
|
||||||
// Clear error
|
// Clear error
|
||||||
errors.value = errors.value.filter(e => e.field !== 'location.name');
|
errors.value = errors.value.filter((e) => e.field !== "location.name");
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearLocationSelection = () => {
|
const clearLocationSelection = () => {
|
||||||
@ -719,7 +940,7 @@ const clearLocationSelection = () => {
|
|||||||
showLocationResults.value = false;
|
showLocationResults.value = false;
|
||||||
|
|
||||||
// Clear error
|
// Clear error
|
||||||
errors.value = errors.value.filter(e => e.field !== 'location.name');
|
errors.value = errors.value.filter((e) => e.field !== "location.name");
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- VOICE INPUT ---
|
// --- VOICE INPUT ---
|
||||||
@ -729,8 +950,9 @@ const toggleVoiceInput = (field) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// --- VALIDATION ---
|
// --- VALIDATION ---
|
||||||
const hasError = (field) => errors.value.some(e => e.field === field);
|
const hasError = (field) => errors.value.some((e) => e.field === field);
|
||||||
const getFieldError = (field) => errors.value.find(e => e.field === field)?.message || "";
|
const getFieldError = (field) =>
|
||||||
|
errors.value.find((e) => e.field === field)?.message || "";
|
||||||
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
errors.value = [];
|
errors.value = [];
|
||||||
@ -739,47 +961,65 @@ const validate = () => {
|
|||||||
// Deceased
|
// Deceased
|
||||||
if (deceasedForm.value.is_existing) {
|
if (deceasedForm.value.is_existing) {
|
||||||
if (!deceasedForm.value.id) {
|
if (!deceasedForm.value.id) {
|
||||||
errors.value.push({ field: 'deceased_id', message: 'Veuillez sélectionner un défunt.' });
|
errors.value.push({
|
||||||
|
field: "deceased_id",
|
||||||
|
message: "Veuillez sélectionner un défunt.",
|
||||||
|
});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!deceasedForm.value.last_name) {
|
if (!deceasedForm.value.last_name) {
|
||||||
errors.value.push({ field: 'deceased.last_name', message: 'Le nom du défunt est requis.' });
|
errors.value.push({
|
||||||
|
field: "deceased.last_name",
|
||||||
|
message: "Le nom du défunt est requis.",
|
||||||
|
});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
if (!selectedClient.value) {
|
if (!selectedClient.value) {
|
||||||
errors.value.push({ field: 'client', message: "Veuillez sélectionner un client." });
|
errors.value.push({
|
||||||
|
field: "client",
|
||||||
|
message: "Veuillez sélectionner un client.",
|
||||||
|
});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intervention fields
|
// Intervention fields
|
||||||
if (!interventionForm.value.scheduled_at) {
|
if (!interventionForm.value.scheduled_at) {
|
||||||
errors.value.push({ field: 'scheduled_at', message: "Date obligatoire." });
|
errors.value.push({ field: "scheduled_at", message: "Date obligatoire." });
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!productForm.value.product_id) {
|
if (!productForm.value.product_id) {
|
||||||
errors.value.push({ field: 'product_id', message: "Type de soin requis." });
|
errors.value.push({ field: "product_id", message: "Type de soin requis." });
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!interventionForm.value.assigned_practitioner_id) {
|
if (!interventionForm.value.assigned_practitioner_id) {
|
||||||
errors.value.push({ field: 'assigned_practitioner_id', message: "Intervenant requis." });
|
errors.value.push({
|
||||||
|
field: "assigned_practitioner_id",
|
||||||
|
message: "Intervenant requis.",
|
||||||
|
});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Location
|
// Location
|
||||||
if (locationForm.value.is_existing) {
|
if (locationForm.value.is_existing) {
|
||||||
if (!locationForm.value.id) {
|
if (!locationForm.value.id) {
|
||||||
errors.value.push({ field: 'location.name', message: "Veuillez sélectionner un lieu." });
|
errors.value.push({
|
||||||
|
field: "location.name",
|
||||||
|
message: "Veuillez sélectionner un lieu.",
|
||||||
|
});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!locationForm.value.name) {
|
if (!locationForm.value.name) {
|
||||||
errors.value.push({ field: 'location.name', message: "Le nom du lieu est requis." });
|
errors.value.push({
|
||||||
|
field: "location.name",
|
||||||
|
message: "Le nom du lieu est requis.",
|
||||||
|
});
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -805,30 +1045,63 @@ const handleSubmit = async () => {
|
|||||||
if (deceasedForm.value.is_existing && deceasedForm.value.id) {
|
if (deceasedForm.value.is_existing && deceasedForm.value.id) {
|
||||||
formData.append("deceased_id", deceasedForm.value.id);
|
formData.append("deceased_id", deceasedForm.value.id);
|
||||||
} else {
|
} else {
|
||||||
formData.append(`deceased[first_name]`, deceasedForm.value.first_name || "");
|
formData.append(
|
||||||
formData.append(`deceased[last_name]`, deceasedForm.value.last_name || "");
|
`deceased[first_name]`,
|
||||||
if (deceasedForm.value.birth_date) formData.append(`deceased[birth_date]`, deceasedForm.value.birth_date);
|
deceasedForm.value.first_name || ""
|
||||||
if (deceasedForm.value.death_date) formData.append(`deceased[death_date]`, deceasedForm.value.death_date);
|
);
|
||||||
|
formData.append(
|
||||||
|
`deceased[last_name]`,
|
||||||
|
deceasedForm.value.last_name || ""
|
||||||
|
);
|
||||||
|
if (deceasedForm.value.birth_date)
|
||||||
|
formData.append(`deceased[birth_date]`, deceasedForm.value.birth_date);
|
||||||
|
if (deceasedForm.value.death_date)
|
||||||
|
formData.append(`deceased[death_date]`, deceasedForm.value.death_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
if (selectedClient.value) {
|
if (selectedClient.value) {
|
||||||
formData.append("client_id", selectedClient.value.id);
|
formData.append("client_id", selectedClient.value.id);
|
||||||
|
|
||||||
if (selectedClient.value.name) formData.append("client[name]", selectedClient.value.name);
|
if (selectedClient.value.name)
|
||||||
if (selectedClient.value.email) formData.append("client[email]", selectedClient.value.email);
|
formData.append("client[name]", selectedClient.value.name);
|
||||||
if (selectedClient.value.phone) formData.append("client[phone]", selectedClient.value.phone);
|
if (selectedClient.value.email)
|
||||||
|
formData.append("client[email]", selectedClient.value.email);
|
||||||
|
if (selectedClient.value.phone)
|
||||||
|
formData.append("client[phone]", selectedClient.value.phone);
|
||||||
|
|
||||||
if (selectedClient.value.billing_address) {
|
if (selectedClient.value.billing_address) {
|
||||||
if (selectedClient.value.billing_address.line1) formData.append("client[billing_address_line1]", selectedClient.value.billing_address.line1);
|
if (selectedClient.value.billing_address.line1)
|
||||||
if (selectedClient.value.billing_address.line2) formData.append("client[billing_address_line2]", selectedClient.value.billing_address.line2);
|
formData.append(
|
||||||
if (selectedClient.value.billing_address.postal_code) formData.append("client[billing_postal_code]", selectedClient.value.billing_address.postal_code);
|
"client[billing_address_line1]",
|
||||||
if (selectedClient.value.billing_address.city) formData.append("client[billing_city]", selectedClient.value.billing_address.city);
|
selectedClient.value.billing_address.line1
|
||||||
if (selectedClient.value.billing_address.country_code) formData.append("client[billing_country_code]", selectedClient.value.billing_address.country_code);
|
);
|
||||||
|
if (selectedClient.value.billing_address.line2)
|
||||||
|
formData.append(
|
||||||
|
"client[billing_address_line2]",
|
||||||
|
selectedClient.value.billing_address.line2
|
||||||
|
);
|
||||||
|
if (selectedClient.value.billing_address.postal_code)
|
||||||
|
formData.append(
|
||||||
|
"client[billing_postal_code]",
|
||||||
|
selectedClient.value.billing_address.postal_code
|
||||||
|
);
|
||||||
|
if (selectedClient.value.billing_address.city)
|
||||||
|
formData.append(
|
||||||
|
"client[billing_city]",
|
||||||
|
selectedClient.value.billing_address.city
|
||||||
|
);
|
||||||
|
if (selectedClient.value.billing_address.country_code)
|
||||||
|
formData.append(
|
||||||
|
"client[billing_country_code]",
|
||||||
|
selectedClient.value.billing_address.country_code
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedClient.value.vat_number) formData.append("client[vat_number]", selectedClient.value.vat_number);
|
if (selectedClient.value.vat_number)
|
||||||
if (selectedClient.value.siret) formData.append("client[siret]", selectedClient.value.siret);
|
formData.append("client[vat_number]", selectedClient.value.vat_number);
|
||||||
|
if (selectedClient.value.siret)
|
||||||
|
formData.append("client[siret]", selectedClient.value.siret);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Location
|
// Location
|
||||||
@ -836,7 +1109,8 @@ const handleSubmit = async () => {
|
|||||||
formData.append("location_id", locationForm.value.id);
|
formData.append("location_id", locationForm.value.id);
|
||||||
} else {
|
} else {
|
||||||
formData.append("location[name]", locationForm.value.name || "");
|
formData.append("location[name]", locationForm.value.name || "");
|
||||||
if (locationForm.value.city) formData.append("location[city]", locationForm.value.city);
|
if (locationForm.value.city)
|
||||||
|
formData.append("location[city]", locationForm.value.city);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Product
|
// Product
|
||||||
@ -859,7 +1133,7 @@ const handleSubmit = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
emit("submit", formData);
|
emit("submit", formData);
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
globalErrors.value.push("Erreur lors de la préparation du formulaire.");
|
globalErrors.value.push("Erreur lors de la préparation du formulaire.");
|
||||||
} finally {
|
} finally {
|
||||||
@ -868,17 +1142,25 @@ const handleSubmit = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Watch for changes to clear errors
|
// Watch for changes to clear errors
|
||||||
watch(() => locationForm.value.name, () => {
|
watch(
|
||||||
if (getFieldError('location.name')) {
|
() => locationForm.value.name,
|
||||||
errors.value = errors.value.filter(e => e.field !== 'location.name');
|
() => {
|
||||||
|
if (getFieldError("location.name")) {
|
||||||
|
errors.value = errors.value.filter((e) => e.field !== "location.name");
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
watch(() => interventionForm.value.assigned_practitioner_id, () => {
|
watch(
|
||||||
if (getFieldError('assigned_practitioner_id')) {
|
() => interventionForm.value.assigned_practitioner_id,
|
||||||
errors.value = errors.value.filter(e => e.field !== 'assigned_practitioner_id');
|
() => {
|
||||||
|
if (getFieldError("assigned_practitioner_id")) {
|
||||||
|
errors.value = errors.value.filter(
|
||||||
|
(e) => e.field !== "assigned_practitioner_id"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show,
|
show,
|
||||||
|
|||||||
@ -6,26 +6,33 @@
|
|||||||
<div class="col-12 d-flex justify-content-center">
|
<div class="col-12 d-flex justify-content-center">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<input
|
<input
|
||||||
|
id="modeNew"
|
||||||
type="radio"
|
type="radio"
|
||||||
class="btn-check"
|
class="btn-check"
|
||||||
name="deceasedMode"
|
name="deceasedMode"
|
||||||
id="modeNew"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:checked="!formData.is_existing"
|
:checked="!formData.is_existing"
|
||||||
@change="formData.is_existing = false; formData.id = null"
|
@change="
|
||||||
|
formData.is_existing = false;
|
||||||
|
formData.id = null;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<label class="btn btn-outline-primary" for="modeNew"
|
||||||
|
>Nouveau Défunt</label
|
||||||
>
|
>
|
||||||
<label class="btn btn-outline-primary" for="modeNew">Nouveau Défunt</label>
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
id="modeSearch"
|
||||||
type="radio"
|
type="radio"
|
||||||
class="btn-check"
|
class="btn-check"
|
||||||
name="deceasedMode"
|
name="deceasedMode"
|
||||||
id="modeSearch"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:checked="formData.is_existing"
|
:checked="formData.is_existing"
|
||||||
@change="formData.is_existing = true"
|
@change="formData.is_existing = true"
|
||||||
|
/>
|
||||||
|
<label class="btn btn-outline-primary" for="modeSearch"
|
||||||
|
>Rechercher</label
|
||||||
>
|
>
|
||||||
<label class="btn btn-outline-primary" for="modeSearch">Rechercher</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -44,16 +51,28 @@
|
|||||||
placeholder="Tapez pour rechercher..."
|
placeholder="Tapez pour rechercher..."
|
||||||
@input="handleSearchInput"
|
@input="handleSearchInput"
|
||||||
/>
|
/>
|
||||||
<button v-if="formData.id" class="btn btn-outline-secondary" type="button" @click="clearSelection">
|
<button
|
||||||
|
v-if="formData.id"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="clearSelection"
|
||||||
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getFieldError('deceased_id')" class="invalid-feedback d-block">
|
<div
|
||||||
|
v-if="getFieldError('deceased_id')"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
|
>
|
||||||
{{ getFieldError("deceased_id") }}
|
{{ getFieldError("deceased_id") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dropdown Results -->
|
<!-- Dropdown Results -->
|
||||||
<div v-if="showResults && searchResults.length" class="list-group position-absolute w-100 shadow" style="z-index: 1000; max-height: 200px; overflow-y: auto;">
|
<div
|
||||||
|
v-if="showResults && searchResults.length"
|
||||||
|
class="list-group position-absolute w-100 shadow"
|
||||||
|
style="z-index: 1000; max-height: 200px; overflow-y: auto"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="deceased in searchResults"
|
v-for="deceased in searchResults"
|
||||||
:key="deceased.id"
|
:key="deceased.id"
|
||||||
@ -62,19 +81,33 @@
|
|||||||
@click="selectDeceased(deceased)"
|
@click="selectDeceased(deceased)"
|
||||||
>
|
>
|
||||||
<div class="d-flex w-100 justify-content-between">
|
<div class="d-flex w-100 justify-content-between">
|
||||||
<h6 class="mb-1">{{ deceased.first_name }} {{ deceased.last_name }}</h6>
|
<h6 class="mb-1">
|
||||||
<small>{{ deceased.birth_date }} - {{ deceased.death_date }}</small>
|
{{ deceased.first_name }} {{ deceased.last_name }}
|
||||||
|
</h6>
|
||||||
|
<small
|
||||||
|
>{{ deceased.birth_date }} - {{ deceased.death_date }}</small
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showResults && searchResults.length === 0 && searchQuery.length >= 2 && !isSearching" class="list-group position-absolute w-100 shadow" style="z-index: 1000;">
|
<div
|
||||||
|
v-if="
|
||||||
|
showResults &&
|
||||||
|
searchResults.length === 0 &&
|
||||||
|
searchQuery.length >= 2 &&
|
||||||
|
!isSearching
|
||||||
|
"
|
||||||
|
class="list-group position-absolute w-100 shadow"
|
||||||
|
style="z-index: 1000"
|
||||||
|
>
|
||||||
<div class="list-group-item text-muted">Aucun résultat trouvé.</div>
|
<div class="list-group-item text-muted">Aucun résultat trouvé.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="formData.id" class="col-12">
|
<div v-if="formData.id" class="col-12">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<strong>Défunt sélectionné:</strong> {{ formData.first_name }} {{ formData.last_name }}
|
<strong>Défunt sélectionné:</strong> {{ formData.first_name }}
|
||||||
|
{{ formData.last_name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,26 +6,33 @@
|
|||||||
<div class="col-12 d-flex justify-content-center">
|
<div class="col-12 d-flex justify-content-center">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<input
|
<input
|
||||||
|
id="locModeNew"
|
||||||
type="radio"
|
type="radio"
|
||||||
class="btn-check"
|
class="btn-check"
|
||||||
name="locationMode"
|
name="locationMode"
|
||||||
id="locModeNew"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:checked="!formData.is_existing"
|
:checked="!formData.is_existing"
|
||||||
@change="formData.is_existing = false; formData.id = null"
|
@change="
|
||||||
|
formData.is_existing = false;
|
||||||
|
formData.id = null;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<label class="btn btn-outline-primary" for="locModeNew"
|
||||||
|
>Nouveau Lieu</label
|
||||||
>
|
>
|
||||||
<label class="btn btn-outline-primary" for="locModeNew">Nouveau Lieu</label>
|
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
id="locModeSearch"
|
||||||
type="radio"
|
type="radio"
|
||||||
class="btn-check"
|
class="btn-check"
|
||||||
name="locationMode"
|
name="locationMode"
|
||||||
id="locModeSearch"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:checked="formData.is_existing"
|
:checked="formData.is_existing"
|
||||||
@change="formData.is_existing = true"
|
@change="formData.is_existing = true"
|
||||||
|
/>
|
||||||
|
<label class="btn btn-outline-primary" for="locModeSearch"
|
||||||
|
>Rechercher</label
|
||||||
>
|
>
|
||||||
<label class="btn btn-outline-primary" for="locModeSearch">Rechercher</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -44,16 +51,28 @@
|
|||||||
placeholder="Hôpital, Funérarium, Ville..."
|
placeholder="Hôpital, Funérarium, Ville..."
|
||||||
@input="handleSearchInput"
|
@input="handleSearchInput"
|
||||||
/>
|
/>
|
||||||
<button v-if="formData.id" class="btn btn-outline-secondary" type="button" @click="clearSelection">
|
<button
|
||||||
|
v-if="formData.id"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="clearSelection"
|
||||||
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="getFieldError('location_id')" class="invalid-feedback d-block">
|
<div
|
||||||
|
v-if="getFieldError('location_id')"
|
||||||
|
class="invalid-feedback d-block"
|
||||||
|
>
|
||||||
{{ getFieldError("location_id") }}
|
{{ getFieldError("location_id") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dropdown Results -->
|
<!-- Dropdown Results -->
|
||||||
<div v-if="showResults && searchResults.length" class="list-group position-absolute w-100 shadow" style="z-index: 1000; max-height: 200px; overflow-y: auto;">
|
<div
|
||||||
|
v-if="showResults && searchResults.length"
|
||||||
|
class="list-group position-absolute w-100 shadow"
|
||||||
|
style="z-index: 1000; max-height: 200px; overflow-y: auto"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="loc in searchResults"
|
v-for="loc in searchResults"
|
||||||
:key="loc.id"
|
:key="loc.id"
|
||||||
@ -62,21 +81,33 @@
|
|||||||
@click="selectLocation(loc)"
|
@click="selectLocation(loc)"
|
||||||
>
|
>
|
||||||
<div class="d-flex w-100 justify-content-between">
|
<div class="d-flex w-100 justify-content-between">
|
||||||
<h6 class="mb-1">{{ loc.name || 'Lieu sans nom' }}</h6>
|
<h6 class="mb-1">{{ loc.name || "Lieu sans nom" }}</h6>
|
||||||
<small>{{ loc.city }}</small>
|
<small>{{ loc.city }}</small>
|
||||||
</div>
|
</div>
|
||||||
<small class="text-muted">{{ loc.address_line1 }}</small>
|
<small class="text-muted">{{ loc.address_line1 }}</small>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showResults && searchResults.length === 0 && searchQuery.length >= 2 && !isSearching" class="list-group position-absolute w-100 shadow" style="z-index: 1000;">
|
<div
|
||||||
|
v-if="
|
||||||
|
showResults &&
|
||||||
|
searchResults.length === 0 &&
|
||||||
|
searchQuery.length >= 2 &&
|
||||||
|
!isSearching
|
||||||
|
"
|
||||||
|
class="list-group position-absolute w-100 shadow"
|
||||||
|
style="z-index: 1000"
|
||||||
|
>
|
||||||
<div class="list-group-item text-muted">Aucun résultat trouvé.</div>
|
<div class="list-group-item text-muted">Aucun résultat trouvé.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="formData.id" class="col-12">
|
<div v-if="formData.id" class="col-12">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<strong>Lieu sélectionné:</strong> {{ formData.name }} <br/>
|
<strong>Lieu sélectionné:</strong> {{ formData.name }} <br />
|
||||||
<small>{{ formData.address }}, {{ formData.postal_code }} {{ formData.city }}</small>
|
<small
|
||||||
|
>{{ formData.address }}, {{ formData.postal_code }}
|
||||||
|
{{ formData.city }}</small
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -247,7 +278,7 @@ const handleSearchInput = () => {
|
|||||||
// Use getAllClientLocations with search param
|
// Use getAllClientLocations with search param
|
||||||
const response = await ClientLocationService.getAllClientLocations({
|
const response = await ClientLocationService.getAllClientLocations({
|
||||||
search: searchQuery.value,
|
search: searchQuery.value,
|
||||||
per_page: 10
|
per_page: 10,
|
||||||
});
|
});
|
||||||
searchResults.value = response.data;
|
searchResults.value = response.data;
|
||||||
showResults.value = true;
|
showResults.value = true;
|
||||||
@ -280,7 +311,6 @@ const clearSelection = () => {
|
|||||||
searchResults.value = [];
|
searchResults.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getFieldError = (field) => {
|
const getFieldError = (field) => {
|
||||||
const error = props.errors.find((err) => err.field === field);
|
const error = props.errors.find((err) => err.field === field);
|
||||||
return error ? error.message : "";
|
return error ? error.message : "";
|
||||||
|
|||||||
@ -16,7 +16,12 @@
|
|||||||
@input="handleSearchInput"
|
@input="handleSearchInput"
|
||||||
@focus="showResults = true"
|
@focus="showResults = true"
|
||||||
/>
|
/>
|
||||||
<button v-if="formData.product_id" class="btn btn-outline-secondary" type="button" @click="clearSelection">
|
<button
|
||||||
|
v-if="formData.product_id"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="clearSelection"
|
||||||
|
>
|
||||||
<i class="fas fa-times"></i>
|
<i class="fas fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -29,13 +34,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dropdown Results -->
|
<!-- Dropdown Results -->
|
||||||
<div v-if="showResults" class="list-group position-absolute w-100 shadow" style="z-index: 1000; max-height: 250px; overflow-y: auto;">
|
<div
|
||||||
|
v-if="showResults"
|
||||||
|
class="list-group position-absolute w-100 shadow"
|
||||||
|
style="z-index: 1000; max-height: 250px; overflow-y: auto"
|
||||||
|
>
|
||||||
<div v-if="loading" class="list-group-item text-center">
|
<div v-if="loading" class="list-group-item text-center">
|
||||||
<div class="spinner-border spinner-border-sm text-primary" role="status"></div>
|
<div
|
||||||
|
class="spinner-border spinner-border-sm text-primary"
|
||||||
|
role="status"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
v-else
|
|
||||||
v-for="product in searchResults"
|
v-for="product in searchResults"
|
||||||
|
v-else
|
||||||
:key="product.id"
|
:key="product.id"
|
||||||
type="button"
|
type="button"
|
||||||
class="list-group-item list-group-item-action"
|
class="list-group-item list-group-item-action"
|
||||||
@ -47,14 +59,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<small class="text-muted">{{ product.description }}</small>
|
<small class="text-muted">{{ product.description }}</small>
|
||||||
</button>
|
</button>
|
||||||
<div v-if="!loading && searchResults.length === 0" class="list-group-item text-muted">Aucun résultat trouvé.</div>
|
<div
|
||||||
|
v-if="!loading && searchResults.length === 0"
|
||||||
|
class="list-group-item text-muted"
|
||||||
|
>
|
||||||
|
Aucun résultat trouvé.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Selected Product Display -->
|
<!-- Selected Product Display -->
|
||||||
<div v-if="formData.product_id && selectedProductDisplay" class="alert alert-info mt-3">
|
<div
|
||||||
|
v-if="formData.product_id && selectedProductDisplay"
|
||||||
|
class="alert alert-info mt-3"
|
||||||
|
>
|
||||||
<strong>Soin sélectionné:</strong> {{ selectedProductDisplay }}
|
<strong>Soin sélectionné:</strong> {{ selectedProductDisplay }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -134,7 +153,8 @@ const filterProducts = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const query = searchQuery.value.toLowerCase();
|
const query = searchQuery.value.toLowerCase();
|
||||||
searchResults.value = allProducts.value.filter(p =>
|
searchResults.value = allProducts.value.filter(
|
||||||
|
(p) =>
|
||||||
p.nom.toLowerCase().includes(query) ||
|
p.nom.toLowerCase().includes(query) ||
|
||||||
(p.reference && p.reference.toLowerCase().includes(query)) ||
|
(p.reference && p.reference.toLowerCase().includes(query)) ||
|
||||||
(p.description && p.description.toLowerCase().includes(query))
|
(p.description && p.description.toLowerCase().includes(query))
|
||||||
@ -155,7 +175,7 @@ onMounted(async () => {
|
|||||||
// Based on previous code, it seems supported.
|
// Based on previous code, it seems supported.
|
||||||
const response = await service.getAllProducts({
|
const response = await service.getAllProducts({
|
||||||
per_page: 100, // Fetch enough
|
per_page: 100, // Fetch enough
|
||||||
is_intervention: true // Assuming backend handles this param as before
|
is_intervention: true, // Assuming backend handles this param as before
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check response structure. product.ts says ProductListResponse { data: Product[] ... }
|
// Check response structure. product.ts says ProductListResponse { data: Product[] ... }
|
||||||
|
|||||||
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="modal fade"
|
||||||
|
:class="{ show: show, 'd-block': show }"
|
||||||
|
tabindex="-1"
|
||||||
|
role="dialog"
|
||||||
|
aria-labelledby="planningNewRequestModalLabel"
|
||||||
|
:aria-hidden="!show"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 id="planningNewRequestModalLabel" class="modal-title">Nouvelle demande</h5>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" @click="$emit('close')"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<p v-if="!creationType" class="text-sm text-muted mb-3">Choisissez le type à créer :</p>
|
||||||
|
<p v-else class="text-sm text-muted mb-3">{{ creationTypeTitle }}</p>
|
||||||
|
|
||||||
|
<planning-creation-type-selector
|
||||||
|
v-if="!creationType"
|
||||||
|
@select-type="$emit('select-type', $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<planning-leave-request-form
|
||||||
|
v-else-if="creationType === 'leave'"
|
||||||
|
:form="leaveForm"
|
||||||
|
:collaborators="collaborators"
|
||||||
|
@update:form="$emit('update:leave-form', $event)"
|
||||||
|
@submit="$emit('submit-leave')"
|
||||||
|
@back="$emit('reset-type')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<planning-event-form
|
||||||
|
v-else-if="creationType === 'event'"
|
||||||
|
:form="eventForm"
|
||||||
|
@update:form="$emit('update:event-form', $event)"
|
||||||
|
@submit="$emit('submit-event')"
|
||||||
|
@back="$emit('reset-type')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="show" class="modal-backdrop fade show" @click="$emit('close')"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import PlanningCreationTypeSelector from "@/components/molecules/Planning/PlanningCreationTypeSelector.vue";
|
||||||
|
import PlanningLeaveRequestForm from "@/components/molecules/Planning/PlanningLeaveRequestForm.vue";
|
||||||
|
import PlanningEventForm from "@/components/molecules/Planning/PlanningEventForm.vue";
|
||||||
|
|
||||||
|
import { defineProps, defineEmits } from "vue";
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
creationType: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
creationTypeTitle: {
|
||||||
|
type: String,
|
||||||
|
default: "Nouvelle demande",
|
||||||
|
},
|
||||||
|
collaborators: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
leaveForm: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
eventForm: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineEmits([
|
||||||
|
"close",
|
||||||
|
"select-type",
|
||||||
|
"reset-type",
|
||||||
|
"submit-leave",
|
||||||
|
"submit-event",
|
||||||
|
"update:leave-form",
|
||||||
|
"update:event-form",
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
|
||||||
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<planning-template>
|
<planning-template>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex flex-column gap-3">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<div>
|
<p class="text-sm text-secondary mb-0">
|
||||||
<h1 class="display-4 font-bold bg-gradient-indigo-text mb-1">Planning</h1>
|
{{ interventionCount }} intervention(s) (mes tâches)
|
||||||
<p class="text-sm text-secondary">{{ interventionCount }} intervention(s) (mes tâches)</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- Date Navigator Mobile Position could go here if needed, but keeping simple for now -->
|
<div
|
||||||
</div>
|
class="d-flex flex-column flex-md-row gap-2 w-100 w-md-auto align-items-center justify-content-md-end"
|
||||||
<div class="d-flex flex-column flex-md-row gap-3 w-100 w-md-auto align-items-center">
|
>
|
||||||
<!-- Date Navigator -->
|
<!-- Date Navigator -->
|
||||||
<planning-date-navigator
|
<planning-date-navigator
|
||||||
:current-date="currentDate"
|
:current-date="currentDate"
|
||||||
@ -16,16 +16,26 @@
|
|||||||
@next-week="$emit('next-week')"
|
@next-week="$emit('next-week')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2 flex-wrap justify-content-end">
|
||||||
<planning-action-button variant="secondary" size="sm" @click="$emit('refresh')">
|
<soft-button
|
||||||
<template #icon><i class="fas fa-sync-alt"></i></template>
|
color="secondary"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
@click="$emit('refresh')"
|
||||||
|
>
|
||||||
|
<i class="fas fa-sync-alt me-1"></i>
|
||||||
<span class="d-none d-lg-inline">Actualiser</span>
|
<span class="d-none d-lg-inline">Actualiser</span>
|
||||||
</planning-action-button>
|
</soft-button>
|
||||||
<planning-action-button variant="primary" size="sm" @click="$emit('new-request')">
|
<soft-button
|
||||||
<template #icon><i class="fas fa-plus"></i></template>
|
color="info"
|
||||||
|
variant="gradient"
|
||||||
|
size="sm"
|
||||||
|
@click="$emit('new-request')"
|
||||||
|
>
|
||||||
|
<i class="fas fa-plus me-1"></i>
|
||||||
<span class="d-none d-lg-inline">Nouvelle demande</span>
|
<span class="d-none d-lg-inline">Nouvelle demande</span>
|
||||||
<span class="d-lg-none">Nouveau</span>
|
<span class="d-lg-none">Nouveau</span>
|
||||||
</planning-action-button>
|
</soft-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -76,7 +86,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, defineProps, defineEmits } from "vue";
|
import { ref, watch, defineProps, defineEmits } from "vue";
|
||||||
import PlanningTemplate from "@/components/templates/Planning/PlanningTemplate.vue";
|
import PlanningTemplate from "@/components/templates/Planning/PlanningTemplate.vue";
|
||||||
import PlanningActionButton from "@/components/atoms/Planning/PlanningActionButton.vue";
|
|
||||||
import PlanningViewToggles from "@/components/molecules/Planning/PlanningViewToggles.vue";
|
import PlanningViewToggles from "@/components/molecules/Planning/PlanningViewToggles.vue";
|
||||||
import PlanningLegend from "@/components/molecules/Planning/PlanningLegend.vue";
|
import PlanningLegend from "@/components/molecules/Planning/PlanningLegend.vue";
|
||||||
import PlanningCollaboratorsSidebar from "@/components/molecules/Planning/PlanningCollaboratorsSidebar.vue";
|
import PlanningCollaboratorsSidebar from "@/components/molecules/Planning/PlanningCollaboratorsSidebar.vue";
|
||||||
@ -84,28 +93,29 @@ import PlanningWeekGrid from "@/components/molecules/Planning/PlanningWeekGrid.v
|
|||||||
import PlanningList from "@/components/molecules/Planning/PlanningList.vue";
|
import PlanningList from "@/components/molecules/Planning/PlanningList.vue";
|
||||||
import PlanningKanban from "@/components/molecules/Planning/PlanningKanban.vue";
|
import PlanningKanban from "@/components/molecules/Planning/PlanningKanban.vue";
|
||||||
import PlanningDateNavigator from "@/components/molecules/Planning/PlanningDateNavigator.vue";
|
import PlanningDateNavigator from "@/components/molecules/Planning/PlanningDateNavigator.vue";
|
||||||
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
interventionCount: {
|
interventionCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
collaborators: {
|
collaborators: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
},
|
},
|
||||||
interventions: {
|
interventions: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
},
|
},
|
||||||
currentDate: {
|
currentDate: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: () => new Date()
|
default: () => new Date(),
|
||||||
},
|
},
|
||||||
activeView: {
|
activeView: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "grille"
|
default: "grille",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@ -116,18 +126,24 @@ const emit = defineEmits([
|
|||||||
"prev-week",
|
"prev-week",
|
||||||
"next-week",
|
"next-week",
|
||||||
"edit-intervention",
|
"edit-intervention",
|
||||||
"update-status"
|
"update-status",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const localActiveView = ref(props.activeView);
|
const localActiveView = ref(props.activeView);
|
||||||
|
|
||||||
watch(() => props.activeView, (newVal) => {
|
watch(
|
||||||
|
() => props.activeView,
|
||||||
|
(newVal) => {
|
||||||
localActiveView.value = newVal;
|
localActiveView.value = newVal;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
watch(() => localActiveView.value, (newVal) => {
|
watch(
|
||||||
|
() => localActiveView.value,
|
||||||
|
(newVal) => {
|
||||||
emit("update:activeView", newVal);
|
emit("update:activeView", newVal);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const handleCellClick = (info) => {
|
const handleCellClick = (info) => {
|
||||||
emit("cell-click", info);
|
emit("cell-click", info);
|
||||||
@ -143,28 +159,6 @@ const handleUpdateStatus = (payload) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.font-bold {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-gradient-indigo-text {
|
|
||||||
background: linear-gradient(to right, #2563eb, #4f46e5, #9333ea);
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
background-clip: text;
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.display-4 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
|
||||||
.display-4 {
|
|
||||||
font-size: 1.875rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-secondary {
|
.text-secondary {
|
||||||
color: #64748b !important;
|
color: #64748b !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
class="btn d-inline-flex align-items-center justify-content-center gap-2 border-0 shadow-sm transition-all"
|
class="btn d-inline-flex align-items-center justify-content-center gap-2 border-0 shadow-sm transition-all"
|
||||||
:class="[
|
:class="[
|
||||||
variant === 'primary' ? 'btn-primary-gradient' : 'btn-outline-indigo',
|
variant === 'primary' ? 'btn-primary-gradient' : 'btn-outline-indigo',
|
||||||
size === 'sm' ? 'btn-sm py-1 px-3' : 'py-2 px-4'
|
size === 'sm' ? 'btn-sm py-1 px-3' : 'py-2 px-4',
|
||||||
]"
|
]"
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
>
|
>
|
||||||
@ -20,16 +20,16 @@ import { defineProps, defineEmits } from "vue";
|
|||||||
defineProps({
|
defineProps({
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "secondary" // primary or secondary
|
default: "secondary", // primary or secondary
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "md"
|
default: "md",
|
||||||
},
|
},
|
||||||
hideTextOnMobile: {
|
hideTextOnMobile: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(["click"]);
|
defineEmits(["click"]);
|
||||||
|
|||||||
@ -13,7 +13,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-3 card-body">
|
<div class="p-3 card-body">
|
||||||
<div ref="calendarEl" :id="calendarId" data-toggle="widget-calendar"></div>
|
<div
|
||||||
|
:id="calendarId"
|
||||||
|
ref="calendarEl"
|
||||||
|
data-toggle="widget-calendar"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,17 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="card border-0 shadow-lg rounded-xl h-100 sidebar-card" :class="{ 'collapsed': isCollapsed }">
|
<div
|
||||||
|
class="card border-0 shadow-lg rounded-xl h-100 sidebar-card"
|
||||||
|
:class="{ collapsed: isCollapsed }"
|
||||||
|
>
|
||||||
<div class="card-header bg-white border-0 pb-0">
|
<div class="card-header bg-white border-0 pb-0">
|
||||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<i class="fas fa-users text-indigo-600"></i>
|
<i class="fas fa-users text-indigo-600"></i>
|
||||||
<h3 class="text-xs font-semibold mb-0">👥 Collaborateurs ({{ count }})</h3>
|
<h3 class="text-xs font-semibold mb-0">
|
||||||
|
👥 Collaborateurs ({{ count }})
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body pt-0">
|
<div class="card-body pt-0">
|
||||||
<div v-if="count === 0" class="text-center text-secondary py-4 text-xs">
|
<div v-if="count === 0" class="text-center text-secondary py-4 text-xs">
|
||||||
<p class="mb-2">Aucun collaborateur</p>
|
<p class="mb-2">Aucun collaborateur</p>
|
||||||
<p class="text-muted opacity-60">💡 Allouez une couleur depuis la page Salariés</p>
|
<p class="text-muted opacity-60">
|
||||||
|
💡 Allouez une couleur depuis la page Salariés
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<slot v-else></slot>
|
<slot v-else></slot>
|
||||||
</div>
|
</div>
|
||||||
@ -26,12 +33,12 @@ import { defineProps } from "vue";
|
|||||||
defineProps({
|
defineProps({
|
||||||
count: {
|
count: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
isCollapsed: {
|
isCollapsed: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<soft-button color="info" variant="gradient" @click="$emit('select-type', 'intervention')">
|
||||||
|
<i class="fas fa-briefcase-medical me-2"></i>
|
||||||
|
Créer une intervention
|
||||||
|
</soft-button>
|
||||||
|
|
||||||
|
<soft-button color="warning" variant="gradient" @click="$emit('select-type', 'leave')">
|
||||||
|
<i class="fas fa-umbrella-beach me-2"></i>
|
||||||
|
Demande de congé employé
|
||||||
|
</soft-button>
|
||||||
|
|
||||||
|
<soft-button color="success" variant="gradient" @click="$emit('select-type', 'event')">
|
||||||
|
<i class="fas fa-calendar-plus me-2"></i>
|
||||||
|
Créer un événement
|
||||||
|
</soft-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineEmits } from "vue";
|
||||||
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
|
|
||||||
|
defineEmits(["select-type"]);
|
||||||
|
</script>
|
||||||
|
|
||||||
@ -1,31 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="d-flex align-items-center bg-white shadow-sm rounded-lg p-1 border">
|
<div
|
||||||
<button
|
class="d-flex align-items-center bg-white shadow-sm rounded-lg p-1 border"
|
||||||
class="btn btn-sm btn-icon mb-0 shadow-none border-0 hover:bg-gray-100 rounded-md"
|
>
|
||||||
|
<soft-button
|
||||||
|
color="secondary"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
class="btn-icon-soft"
|
||||||
@click="$emit('prev-week')"
|
@click="$emit('prev-week')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-chevron-left text-secondary text-xs"></i>
|
<i class="fas fa-chevron-left text-secondary text-xs"></i>
|
||||||
</button>
|
</soft-button>
|
||||||
<div class="px-3 py-1 text-sm font-semibold text-dark whitespace-nowrap min-w-140 text-center">
|
<div
|
||||||
|
class="px-3 py-1 text-sm font-semibold text-dark whitespace-nowrap min-w-140 text-center"
|
||||||
|
>
|
||||||
{{ dateRangeDisplay }}
|
{{ dateRangeDisplay }}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<soft-button
|
||||||
class="btn btn-sm btn-icon mb-0 shadow-none border-0 hover:bg-gray-100 rounded-md"
|
color="secondary"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
class="btn-icon-soft"
|
||||||
@click="$emit('next-week')"
|
@click="$emit('next-week')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-chevron-right text-secondary text-xs"></i>
|
<i class="fas fa-chevron-right text-secondary text-xs"></i>
|
||||||
</button>
|
</soft-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, defineProps, defineEmits } from "vue";
|
import { computed, defineProps, defineEmits } from "vue";
|
||||||
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
currentDate: {
|
currentDate: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: () => new Date()
|
default: () => new Date(),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(["prev-week", "next-week"]);
|
defineEmits(["prev-week", "next-week"]);
|
||||||
@ -43,11 +54,15 @@ const dateRangeDisplay = computed(() => {
|
|||||||
const sunday = new Date(monday);
|
const sunday = new Date(monday);
|
||||||
sunday.setDate(monday.getDate() + 6);
|
sunday.setDate(monday.getDate() + 6);
|
||||||
|
|
||||||
const options = { day: 'numeric', month: 'short' };
|
const options = { day: "numeric", month: "short" };
|
||||||
const startStr = monday.toLocaleDateString('fr-FR', options);
|
const startStr = monday.toLocaleDateString("fr-FR", options);
|
||||||
|
|
||||||
// If same month, don't repeat month in start date (optional refinement, sticking to simple for now)
|
// If same month, don't repeat month in start date (optional refinement, sticking to simple for now)
|
||||||
const endStr = sunday.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric' });
|
const endStr = sunday.toLocaleDateString("fr-FR", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
|
||||||
return `${startStr} - ${endStr}`;
|
return `${startStr} - ${endStr}`;
|
||||||
});
|
});
|
||||||
@ -58,10 +73,6 @@ const dateRangeDisplay = computed(() => {
|
|||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rounded-md {
|
|
||||||
border-radius: 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-xs {
|
.text-xs {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
@ -78,16 +89,14 @@ const dateRangeDisplay = computed(() => {
|
|||||||
min-width: 140px;
|
min-width: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hover\:bg-gray-100:hover {
|
.btn-icon-soft {
|
||||||
background-color: #f3f4f6;
|
width: 30px;
|
||||||
}
|
height: 30px;
|
||||||
|
min-width: 30px;
|
||||||
.btn-icon {
|
padding: 0 !important;
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
border-radius: 0.375rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<form class="d-grid gap-2" @submit.prevent="$emit('submit')">
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Titre</label>
|
||||||
|
<soft-input
|
||||||
|
:model-value="form.title"
|
||||||
|
placeholder="Réunion équipe"
|
||||||
|
@update:model-value="updateField('title', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Date et heure</label>
|
||||||
|
<soft-input
|
||||||
|
:model-value="form.date"
|
||||||
|
type="datetime-local"
|
||||||
|
@update:model-value="updateField('date', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Lieu</label>
|
||||||
|
<soft-input
|
||||||
|
:model-value="form.location"
|
||||||
|
placeholder="Salle principale"
|
||||||
|
@update:model-value="updateField('location', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Description</label>
|
||||||
|
<input
|
||||||
|
:value="form.description"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
placeholder="Détails de l'événement"
|
||||||
|
@input="updateField('description', $event.target.value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 justify-content-end pt-2">
|
||||||
|
<soft-button color="secondary" variant="outline" @click="$emit('back')">Retour</soft-button>
|
||||||
|
<soft-button color="success" variant="gradient" type="submit">Enregistrer</soft-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
|
import SoftInput from "@/components/SoftInput.vue";
|
||||||
|
import { defineEmits, defineProps } from "vue";
|
||||||
|
defineProps({
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:form", "submit", "back"]);
|
||||||
|
|
||||||
|
const updateField = (field, value) => {
|
||||||
|
emit("update:form", { field, value });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="planning-kanban-container mt-3">
|
<div class="planning-kanban-container mt-3">
|
||||||
<div class="py-2 min-vh-100 d-inline-flex" style="overflow-x: auto">
|
<div class="planning-kanban-scroll py-2">
|
||||||
<div id="planningKanban"></div>
|
<div id="planningKanban"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -92,7 +92,7 @@ const initKanban = () => {
|
|||||||
kanbanInstance = new jKanban({
|
kanbanInstance = new jKanban({
|
||||||
element: "#planningKanban",
|
element: "#planningKanban",
|
||||||
gutter: "10px",
|
gutter: "10px",
|
||||||
widthBoard: "300px",
|
widthBoard: "360px",
|
||||||
responsivePercentage: false,
|
responsivePercentage: false,
|
||||||
dragItems: true,
|
dragItems: true,
|
||||||
boards: boards,
|
boards: boards,
|
||||||
@ -153,12 +153,18 @@ watch(() => props.interventions, () => {
|
|||||||
/* Global styles for jKanban overrides */
|
/* Global styles for jKanban overrides */
|
||||||
.kanban-container {
|
.kanban-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: visible !important;
|
||||||
|
overflow-y: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kanban-board {
|
.kanban-board {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
|
width: 360px !important;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kanban-board-header {
|
.kanban-board-header {
|
||||||
@ -183,9 +189,27 @@ watch(() => props.interventions, () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.kanban-drag {
|
.kanban-drag {
|
||||||
min-height: 500px;
|
min-height: 100%;
|
||||||
background-color: #f1f5f9; /* slate-100 */
|
background-color: #f1f5f9; /* slate-100 */
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.planning-kanban-container {
|
||||||
|
width: 100%;
|
||||||
|
min-height: calc(100vh - 220px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.planning-kanban-scroll {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 220px);
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#planningKanban {
|
||||||
|
width: 100%;
|
||||||
|
min-width: max-content;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<form class="d-grid gap-2" @submit.prevent="$emit('submit')">
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Employé</label>
|
||||||
|
<select :value="form.employee" class="form-select" @change="updateField('employee', $event.target.value)">
|
||||||
|
<option value="" disabled>Choisir un employé</option>
|
||||||
|
<option v-for="collab in collaborators" :key="collab.id" :value="collab.name">
|
||||||
|
{{ collab.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="form-label">Du</label>
|
||||||
|
<soft-input :model-value="form.startDate" type="date" @update:model-value="updateField('startDate', $event)" />
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="form-label">Au</label>
|
||||||
|
<soft-input :model-value="form.endDate" type="date" @update:model-value="updateField('endDate', $event)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Motif</label>
|
||||||
|
<soft-input
|
||||||
|
:model-value="form.reason"
|
||||||
|
placeholder="Congé annuel"
|
||||||
|
@update:model-value="updateField('reason', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 justify-content-end pt-2">
|
||||||
|
<soft-button color="secondary" variant="outline" @click="$emit('back')">Retour</soft-button>
|
||||||
|
<soft-button color="warning" variant="gradient" type="submit">Enregistrer</soft-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
|
import SoftInput from "@/components/SoftInput.vue";
|
||||||
|
import { defineEmits, defineProps } from "vue";
|
||||||
|
defineProps({
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
collaborators: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:form", "submit", "back"]);
|
||||||
|
|
||||||
|
const updateField = (field, value) => {
|
||||||
|
emit("update:form", { field, value });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
@ -7,9 +7,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body pt-2">
|
<div class="card-body pt-2">
|
||||||
<div class="d-flex flex-wrap gap-3">
|
<div class="d-flex flex-wrap gap-3">
|
||||||
<div v-for="item in legend" :key="item.label" class="d-flex align-items-center gap-2">
|
<div
|
||||||
|
v-for="item in legend"
|
||||||
|
:key="item.label"
|
||||||
|
class="d-flex align-items-center gap-2"
|
||||||
|
>
|
||||||
<span class="text-sm">{{ item.emoji }}</span>
|
<span class="text-sm">{{ item.emoji }}</span>
|
||||||
<div class="legend-bar rounded-pill" :style="{ backgroundColor: item.color }"></div>
|
<div
|
||||||
|
class="legend-bar rounded-pill"
|
||||||
|
:style="{ backgroundColor: item.color }"
|
||||||
|
></div>
|
||||||
<span class="text-xs text-secondary">{{ item.label }}</span>
|
<span class="text-xs text-secondary">{{ item.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -23,7 +30,7 @@ const legend = [
|
|||||||
{ emoji: "🏥", label: "Maladie", color: "#ef4444" },
|
{ emoji: "🏥", label: "Maladie", color: "#ef4444" },
|
||||||
{ emoji: "📚", label: "Formation", color: "#8b5cf6" },
|
{ emoji: "📚", label: "Formation", color: "#8b5cf6" },
|
||||||
{ emoji: "💼", label: "Sans solde", color: "#6b7280" },
|
{ emoji: "💼", label: "Sans solde", color: "#6b7280" },
|
||||||
{ emoji: "🚑", label: "Accident travail", color: "#dc2626" }
|
{ emoji: "🚑", label: "Accident travail", color: "#dc2626" },
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -5,56 +5,111 @@
|
|||||||
<table class="table align-items-center mb-0">
|
<table class="table align-items-center mb-0">
|
||||||
<thead class="bg-gray-50">
|
<thead class="bg-gray-50">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-4">Date & Heure</th>
|
<th
|
||||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Type</th>
|
class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-4"
|
||||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Défunt / Client</th>
|
>
|
||||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Collaborateur</th>
|
Date & Heure
|
||||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Statut</th>
|
</th>
|
||||||
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Actions</th>
|
<th
|
||||||
|
class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2"
|
||||||
|
>
|
||||||
|
Type
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2"
|
||||||
|
>
|
||||||
|
Défunt / Client
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2"
|
||||||
|
>
|
||||||
|
Collaborateur
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2"
|
||||||
|
>
|
||||||
|
Statut
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2"
|
||||||
|
>
|
||||||
|
Actions
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-if="interventions.length === 0">
|
<tr v-if="interventions.length === 0">
|
||||||
<td colspan="6" class="text-center py-5">
|
<td colspan="6" class="text-center py-5">
|
||||||
<span class="text-muted text-sm">Aucune intervention pour cette période</span>
|
<span class="text-muted text-sm"
|
||||||
|
>Aucune intervention pour cette période</span
|
||||||
|
>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-for="intervention in interventions" :key="intervention.id" class="hover-row transition-all">
|
<tr
|
||||||
|
v-for="intervention in interventions"
|
||||||
|
:key="intervention.id"
|
||||||
|
class="hover-row transition-all"
|
||||||
|
>
|
||||||
<td class="ps-4">
|
<td class="ps-4">
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<h6 class="mb-0 text-sm font-weight-bold">{{ formatDate(intervention.date) }}</h6>
|
<h6 class="mb-0 text-sm font-weight-bold">
|
||||||
<span class="text-xs text-secondary">{{ formatTime(intervention.date) }}</span>
|
{{ formatDate(intervention.date) }}
|
||||||
|
</h6>
|
||||||
|
<span class="text-xs text-secondary">{{
|
||||||
|
formatTime(intervention.date)
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="badge badge-sm bg-gradient-light text-dark mb-0 d-flex align-items-center gap-1 border">
|
<span
|
||||||
<span class="width-8-px height-8-px rounded-circle me-1" :style="{ backgroundColor: getTypeColor(intervention.type) }"></span>
|
class="badge badge-sm bg-gradient-light text-dark mb-0 d-flex align-items-center gap-1 border"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="width-8-px height-8-px rounded-circle me-1"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: getTypeColor(intervention.type),
|
||||||
|
}"
|
||||||
|
></span>
|
||||||
{{ intervention.type }}
|
{{ intervention.type }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<h6 class="mb-0 text-sm">{{ intervention.deceased || 'Non spécifié' }}</h6>
|
<h6 class="mb-0 text-sm">
|
||||||
<span class="text-xs text-secondary">Client: {{ intervention.client || '-' }}</span>
|
{{ intervention.deceased || "Non spécifié" }}
|
||||||
|
</h6>
|
||||||
|
<span class="text-xs text-secondary"
|
||||||
|
>Client: {{ intervention.client || "-" }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="avatar avatar-xs me-2 bg-gradient-primary rounded-circle text-white d-flex align-items-center justify-content-center text-xxs">
|
<div
|
||||||
|
class="avatar avatar-xs me-2 bg-gradient-primary rounded-circle text-white d-flex align-items-center justify-content-center text-xxs"
|
||||||
|
>
|
||||||
{{ getInitials(intervention.collaborator) }}
|
{{ getInitials(intervention.collaborator) }}
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm font-weight-bold text-secondary">{{ intervention.collaborator }}</span>
|
<span class="text-sm font-weight-bold text-secondary">{{
|
||||||
|
intervention.collaborator
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge badge-sm" :class="getStatusBadgeClass(intervention.status)">
|
<span
|
||||||
|
class="badge badge-sm"
|
||||||
|
:class="getStatusBadgeClass(intervention.status)"
|
||||||
|
>
|
||||||
{{ intervention.status }}
|
{{ intervention.status }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-link text-secondary mb-0 px-2" @click="$emit('edit', intervention)">
|
<button
|
||||||
|
class="btn btn-link text-secondary mb-0 px-2"
|
||||||
|
@click="$emit('edit', intervention)"
|
||||||
|
>
|
||||||
<i class="fas fa-edit text-xs"></i>
|
<i class="fas fa-edit text-xs"></i>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
@ -72,45 +127,57 @@ import { defineProps, defineEmits } from "vue";
|
|||||||
defineProps({
|
defineProps({
|
||||||
interventions: {
|
interventions: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(["edit"]);
|
defineEmits(["edit"]);
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
if (!dateString) return "-";
|
if (!dateString) return "-";
|
||||||
return new Date(dateString).toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'short' });
|
return new Date(dateString).toLocaleDateString("fr-FR", {
|
||||||
|
weekday: "long",
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatTime = (dateString) => {
|
const formatTime = (dateString) => {
|
||||||
if (!dateString) return "-";
|
if (!dateString) return "-";
|
||||||
return new Date(dateString).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });
|
return new Date(dateString).toLocaleTimeString("fr-FR", {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getInitials = (name) => {
|
const getInitials = (name) => {
|
||||||
if (!name) return "?";
|
if (!name) return "?";
|
||||||
return name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase();
|
return name
|
||||||
|
.split(" ")
|
||||||
|
.map((n) => n[0])
|
||||||
|
.join("")
|
||||||
|
.substring(0, 2)
|
||||||
|
.toUpperCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTypeColor = (type) => {
|
const getTypeColor = (type) => {
|
||||||
const colors = {
|
const colors = {
|
||||||
'Soin': '#3b82f6',
|
Soin: "#3b82f6",
|
||||||
'Transport': '#10b981',
|
Transport: "#10b981",
|
||||||
'Mise en bière': '#f59e0b',
|
"Mise en bière": "#f59e0b",
|
||||||
'Cérémonie': '#8b5cf6'
|
Cérémonie: "#8b5cf6",
|
||||||
};
|
};
|
||||||
return colors[type] || '#6b7280';
|
return colors[type] || "#6b7280";
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusBadgeClass = (status) => {
|
const getStatusBadgeClass = (status) => {
|
||||||
const map = {
|
const map = {
|
||||||
'Confirmé': 'bg-gradient-info',
|
Confirmé: "bg-gradient-info",
|
||||||
'Terminé': 'bg-gradient-success',
|
Terminé: "bg-gradient-success",
|
||||||
'En attente': 'bg-gradient-warning',
|
"En attente": "bg-gradient-warning",
|
||||||
'Annulé': 'bg-gradient-danger'
|
Annulé: "bg-gradient-danger",
|
||||||
};
|
};
|
||||||
return map[status] || 'bg-gradient-secondary';
|
return map[status] || "bg-gradient-secondary";
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,28 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button
|
<soft-button
|
||||||
v-for="view in views"
|
v-for="view in views"
|
||||||
:key="view.id"
|
:key="view.id"
|
||||||
class="btn d-inline-flex align-items-center justify-content-center gap-2 border-0 shadow-sm transition-all"
|
class="d-inline-flex align-items-center justify-content-center gap-2 transition-all"
|
||||||
:class="[
|
:color="activeView === view.id ? 'info' : 'secondary'"
|
||||||
activeView === view.id ? 'btn-active' : 'btn-inactive'
|
:variant="activeView === view.id ? 'gradient' : 'outline'"
|
||||||
]"
|
size="sm"
|
||||||
@click="$emit('update:activeView', view.id)"
|
@click="$emit('update:activeView', view.id)"
|
||||||
>
|
>
|
||||||
<i :class="view.icon"></i>
|
<i :class="view.icon"></i>
|
||||||
<span class="d-none d-sm-inline">{{ view.label }}</span>
|
<span class="d-none d-sm-inline">{{ view.label }}</span>
|
||||||
</button>
|
</soft-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from "vue";
|
import { defineProps, defineEmits } from "vue";
|
||||||
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
activeView: {
|
activeView: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "grille"
|
default: "grille",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
defineEmits(["update:activeView"]);
|
defineEmits(["update:activeView"]);
|
||||||
@ -30,35 +31,16 @@ defineEmits(["update:activeView"]);
|
|||||||
const views = [
|
const views = [
|
||||||
{ id: "liste", label: "Liste", icon: "fas fa-list", color: "gray" },
|
{ id: "liste", label: "Liste", icon: "fas fa-list", color: "gray" },
|
||||||
{ id: "kanban", label: "Kanban", icon: "fas fa-columns", color: "gray" },
|
{ id: "kanban", label: "Kanban", icon: "fas fa-columns", color: "gray" },
|
||||||
{ id: "grille", label: "Grille", icon: "fas fa-calendar-alt", color: "indigo" }
|
{
|
||||||
|
id: "grille",
|
||||||
|
label: "Grille",
|
||||||
|
icon: "fas fa-calendar-alt",
|
||||||
|
color: "indigo",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.btn {
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-active {
|
|
||||||
background-color: #4f46e5;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-inactive {
|
|
||||||
background-color: white;
|
|
||||||
color: #64748b;
|
|
||||||
border: 1px solid #e2e8f0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-inactive:hover {
|
|
||||||
background-color: #f8fafc;
|
|
||||||
color: #1e293b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.transition-all {
|
.transition-all {
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="calendar-grid-container card h-100 border-0 shadow-sm rounded-xl">
|
<div class="calendar-grid-container card h-100 border-0 shadow-sm rounded-xl">
|
||||||
<div class="card-body p-3 h-100">
|
<div class="card-body p-3 h-100">
|
||||||
<div ref="calendarEl" id="fullCalendarGrid" class="h-100"></div>
|
<div id="fullCalendarGrid" ref="calendarEl" class="h-100"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -16,12 +16,12 @@ import frLocale from "@fullcalendar/core/locales/fr";
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
startDate: {
|
startDate: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: () => new Date()
|
default: () => new Date(),
|
||||||
},
|
},
|
||||||
interventions: {
|
interventions: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["cell-click", "edit"]);
|
const emit = defineEmits(["cell-click", "edit"]);
|
||||||
@ -49,7 +49,7 @@ const initializeCalendar = () => {
|
|||||||
contentHeight: "auto",
|
contentHeight: "auto",
|
||||||
expandRows: true,
|
expandRows: true,
|
||||||
stickyHeaderDates: true,
|
stickyHeaderDates: true,
|
||||||
dayHeaderFormat: { weekday: 'long', day: 'numeric', month: 'short' }, // "Lundi 12 janv."
|
dayHeaderFormat: { weekday: "long", day: "numeric", month: "short" }, // "Lundi 12 janv."
|
||||||
events: mapEvents(props.interventions),
|
events: mapEvents(props.interventions),
|
||||||
eventClick: (info) => {
|
eventClick: (info) => {
|
||||||
const originalEvent = info.event.extendedProps.originalData;
|
const originalEvent = info.event.extendedProps.originalData;
|
||||||
@ -61,22 +61,22 @@ const initializeCalendar = () => {
|
|||||||
},
|
},
|
||||||
// Styling customization via class names injection if needed
|
// Styling customization via class names injection if needed
|
||||||
eventClassNames: (arg) => {
|
eventClassNames: (arg) => {
|
||||||
return ['shadow-sm', 'border-0'];
|
return ["shadow-sm", "border-0"];
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
calendar.render();
|
calendar.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapEvents = (interventions) => {
|
const mapEvents = (interventions) => {
|
||||||
return interventions.map(i => {
|
return interventions.map((i) => {
|
||||||
// Map props.interventions structure to FullCalendar event object
|
// Map props.interventions structure to FullCalendar event object
|
||||||
// Assuming intervention has: id, date (ISO string), title (or type), status, color
|
// Assuming intervention has: id, date (ISO string), title (or type), status, color
|
||||||
const typeColors = {
|
const typeColors = {
|
||||||
'Soin': '#3b82f6',
|
Soin: "#3b82f6",
|
||||||
'Transport': '#10b981',
|
Transport: "#10b981",
|
||||||
'Mise en bière': '#f59e0b',
|
"Mise en bière": "#f59e0b",
|
||||||
'Cérémonie': '#8b5cf6'
|
Cérémonie: "#8b5cf6",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default duration 1 hour if not specified
|
// Default duration 1 hour if not specified
|
||||||
@ -88,12 +88,12 @@ const mapEvents = (interventions) => {
|
|||||||
title: i.deceased ? `${i.type} - ${i.deceased}` : i.type,
|
title: i.deceased ? `${i.type} - ${i.deceased}` : i.type,
|
||||||
start: i.date,
|
start: i.date,
|
||||||
end: i.end || end, // Use provided end or default
|
end: i.end || end, // Use provided end or default
|
||||||
backgroundColor: typeColors[i.type] || '#6b7280',
|
backgroundColor: typeColors[i.type] || "#6b7280",
|
||||||
borderColor: typeColors[i.type] || '#6b7280',
|
borderColor: typeColors[i.type] || "#6b7280",
|
||||||
textColor: '#ffffff',
|
textColor: "#ffffff",
|
||||||
extendedProps: {
|
extendedProps: {
|
||||||
originalData: i
|
originalData: i,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -102,19 +102,25 @@ onMounted(() => {
|
|||||||
initializeCalendar();
|
initializeCalendar();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => props.startDate, (newDate) => {
|
watch(
|
||||||
|
() => props.startDate,
|
||||||
|
(newDate) => {
|
||||||
if (calendar) {
|
if (calendar) {
|
||||||
calendar.gotoDate(newDate);
|
calendar.gotoDate(newDate);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
watch(() => props.interventions, (newInterventions) => {
|
watch(
|
||||||
|
() => props.interventions,
|
||||||
|
(newInterventions) => {
|
||||||
if (calendar) {
|
if (calendar) {
|
||||||
calendar.removeAllEvents();
|
calendar.removeAllEvents();
|
||||||
calendar.addEventSource(mapEvents(newInterventions));
|
calendar.addEventSource(mapEvents(newInterventions));
|
||||||
}
|
}
|
||||||
}, { deep: true });
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -147,7 +153,8 @@ watch(() => props.interventions, (newInterventions) => {
|
|||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.fc td), :deep(.fc th) {
|
:deep(.fc td),
|
||||||
|
:deep(.fc th) {
|
||||||
border-color: #e9ecef;
|
border-color: #e9ecef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="planning-container p-2 p-md-4">
|
<div class="planning-container p-2 p-md-4">
|
||||||
<div class="container-max mx-auto">
|
<div class="container-max mx-auto h-100">
|
||||||
<!-- Header Section -->
|
<!-- Header Section -->
|
||||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-4 gap-3">
|
<div
|
||||||
|
class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-4 gap-3"
|
||||||
|
>
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -11,7 +13,7 @@
|
|||||||
<slot name="view-toggles"></slot>
|
<slot name="view-toggles"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper h-100">
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<!-- Sidebar & Legend Column (Left on desktop) -->
|
<!-- Sidebar & Legend Column (Left on desktop) -->
|
||||||
<!-- <div class="col-12 col-xl-3 order-2 order-xl-1">
|
<!-- <div class="col-12 col-xl-3 order-2 order-xl-1">
|
||||||
@ -44,11 +46,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container-max {
|
.container-max {
|
||||||
|
width: 100%;
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
|
min-height: calc(100vh - 2rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
min-height: calc(100vh - 12rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gap-3 {
|
.gap-3 {
|
||||||
|
|||||||
@ -477,17 +477,20 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: "/fournisseurs/factures",
|
path: "/fournisseurs/factures",
|
||||||
name: "Factures fournisseurs",
|
name: "Factures fournisseurs",
|
||||||
component: () => import("@/views/pages/Fournisseurs/FactureFournisseurList.vue"),
|
component: () =>
|
||||||
|
import("@/views/pages/Fournisseurs/FactureFournisseurList.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/fournisseurs/factures/new",
|
path: "/fournisseurs/factures/new",
|
||||||
name: "Nouvelle Facture Fournisseur",
|
name: "Nouvelle Facture Fournisseur",
|
||||||
component: () => import("@/views/pages/Fournisseurs/NewFactureFournisseur.vue"),
|
component: () =>
|
||||||
|
import("@/views/pages/Fournisseurs/NewFactureFournisseur.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/fournisseurs/factures/:id",
|
path: "/fournisseurs/factures/:id",
|
||||||
name: "Facture Fournisseur Details",
|
name: "Facture Fournisseur Details",
|
||||||
component: () => import("@/views/pages/Fournisseurs/FactureFournisseurDetail.vue"),
|
component: () =>
|
||||||
|
import("@/views/pages/Fournisseurs/FactureFournisseurDetail.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/fournisseurs/statistiques",
|
path: "/fournisseurs/statistiques",
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<planning-presentation
|
<planning-presentation
|
||||||
|
v-model:activeView="activeView"
|
||||||
:intervention-count="interventions.length"
|
:intervention-count="interventions.length"
|
||||||
:collaborators="collaborators"
|
:collaborators="collaborators"
|
||||||
:interventions="interventions"
|
:interventions="interventions"
|
||||||
:current-date="currentDate"
|
:current-date="currentDate"
|
||||||
v-model:activeView="activeView"
|
|
||||||
@refresh="handleRefresh"
|
@refresh="handleRefresh"
|
||||||
@new-request="handleNewRequest"
|
@new-request="handleNewRequest"
|
||||||
@cell-click="handleCellClick"
|
@cell-click="handleCellClick"
|
||||||
@ -14,18 +14,82 @@
|
|||||||
@edit-intervention="handleEditIntervention"
|
@edit-intervention="handleEditIntervention"
|
||||||
@update-status="handleUpdateStatus"
|
@update-status="handleUpdateStatus"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<planning-new-request-modal
|
||||||
|
:show="showNewRequestModal"
|
||||||
|
:creation-type="creationType"
|
||||||
|
:creation-type-title="creationTypeTitle"
|
||||||
|
:collaborators="collaborators"
|
||||||
|
:leave-form="leaveForm"
|
||||||
|
:event-form="eventForm"
|
||||||
|
@close="closeNewRequestModal"
|
||||||
|
@select-type="selectCreationType"
|
||||||
|
@reset-type="resetType"
|
||||||
|
@submit-leave="submitLeave"
|
||||||
|
@submit-event="submitEvent"
|
||||||
|
@update:leave-form="updateLeaveForm"
|
||||||
|
@update:event-form="updateEventForm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<intervention-multi-step-modal
|
||||||
|
ref="interventionModalRef"
|
||||||
|
:is-editing="false"
|
||||||
|
:practitioners="practitioners"
|
||||||
|
:initial-date="initialInterventionDate"
|
||||||
|
@submit="handleInterventionSubmit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted, computed } from "vue";
|
||||||
import PlanningPresentation from "@/components/Organism/Planning/PlanningPresentation.vue";
|
import PlanningPresentation from "@/components/Organism/Planning/PlanningPresentation.vue";
|
||||||
|
import PlanningNewRequestModal from "@/components/Organism/Planning/PlanningNewRequestModal.vue";
|
||||||
|
import InterventionMultiStepModal from "@/components/Organism/Agenda/InterventionMultiStepModal.vue";
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const interventions = ref([]);
|
const interventions = ref([]);
|
||||||
const collaborators = ref([]);
|
const collaborators = ref([]);
|
||||||
const currentDate = ref(new Date());
|
const currentDate = ref(new Date());
|
||||||
const activeView = ref("grille");
|
const activeView = ref("grille");
|
||||||
|
const showNewRequestModal = ref(false);
|
||||||
|
const creationType = ref("");
|
||||||
|
const interventionModalRef = ref(null);
|
||||||
|
const initialInterventionDate = ref("");
|
||||||
|
|
||||||
|
const leaveForm = ref({
|
||||||
|
employee: "",
|
||||||
|
startDate: "",
|
||||||
|
endDate: "",
|
||||||
|
reason: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const eventForm = ref({
|
||||||
|
title: "",
|
||||||
|
date: "",
|
||||||
|
location: "",
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const creationTypeTitle = computed(() => {
|
||||||
|
if (creationType.value === "intervention") return "Nouvelle intervention";
|
||||||
|
if (creationType.value === "leave") return "Demande de congé";
|
||||||
|
if (creationType.value === "event") return "Nouvel événement";
|
||||||
|
return "Nouvelle demande";
|
||||||
|
});
|
||||||
|
|
||||||
|
const practitioners = computed(() =>
|
||||||
|
collaborators.value.map((collab) => {
|
||||||
|
const [firstName = collab.name, ...rest] = collab.name.split(" ");
|
||||||
|
return {
|
||||||
|
id: collab.id,
|
||||||
|
employee: {
|
||||||
|
first_name: firstName,
|
||||||
|
last_name: rest.join(" "),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@ -38,7 +102,7 @@ const fetchData = async () => {
|
|||||||
collaborators.value = [
|
collaborators.value = [
|
||||||
{ id: 1, name: "Jean Dupont" },
|
{ id: 1, name: "Jean Dupont" },
|
||||||
{ id: 2, name: "Marie Curie" },
|
{ id: 2, name: "Marie Curie" },
|
||||||
{ id: 3, name: "Lucas Martin" }
|
{ id: 3, name: "Lucas Martin" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Generate some random interventions around current date
|
// Generate some random interventions around current date
|
||||||
@ -53,7 +117,7 @@ const fetchData = async () => {
|
|||||||
deceased: "M. Martin",
|
deceased: "M. Martin",
|
||||||
client: "Pompes Funèbres Générales",
|
client: "Pompes Funèbres Générales",
|
||||||
collaborator: "Jean Dupont",
|
collaborator: "Jean Dupont",
|
||||||
status: "Confirmé"
|
status: "Confirmé",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 102,
|
id: 102,
|
||||||
@ -62,7 +126,7 @@ const fetchData = async () => {
|
|||||||
deceased: "Mme. Dubois",
|
deceased: "Mme. Dubois",
|
||||||
client: "Roc Eclerc",
|
client: "Roc Eclerc",
|
||||||
collaborator: "Marie Curie",
|
collaborator: "Marie Curie",
|
||||||
status: "En cours"
|
status: "En cours",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 103,
|
id: 103,
|
||||||
@ -71,7 +135,7 @@ const fetchData = async () => {
|
|||||||
deceased: "M. Lefebvre",
|
deceased: "M. Lefebvre",
|
||||||
client: "PF Locales",
|
client: "PF Locales",
|
||||||
collaborator: "Lucas Martin",
|
collaborator: "Lucas Martin",
|
||||||
status: "En attente"
|
status: "En attente",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 104,
|
id: 104,
|
||||||
@ -80,7 +144,7 @@ const fetchData = async () => {
|
|||||||
deceased: "Mme. Petit",
|
deceased: "Mme. Petit",
|
||||||
client: "Famille Petit",
|
client: "Famille Petit",
|
||||||
collaborator: "Jean Dupont",
|
collaborator: "Jean Dupont",
|
||||||
status: "Terminé"
|
status: "Terminé",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 105,
|
id: 105,
|
||||||
@ -89,8 +153,8 @@ const fetchData = async () => {
|
|||||||
deceased: "Inconnu",
|
deceased: "Inconnu",
|
||||||
client: "Police",
|
client: "Police",
|
||||||
collaborator: "Marie Curie",
|
collaborator: "Marie Curie",
|
||||||
status: "Annulé"
|
status: "Annulé",
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -99,7 +163,119 @@ const handleRefresh = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleNewRequest = () => {
|
const handleNewRequest = () => {
|
||||||
console.log("New request");
|
creationType.value = "";
|
||||||
|
showNewRequestModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeNewRequestModal = () => {
|
||||||
|
creationType.value = "";
|
||||||
|
showNewRequestModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectCreationType = (type) => {
|
||||||
|
if (type === "intervention") {
|
||||||
|
creationType.value = "";
|
||||||
|
showNewRequestModal.value = false;
|
||||||
|
const now = new Date();
|
||||||
|
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
|
||||||
|
initialInterventionDate.value = now.toISOString().slice(0, 16);
|
||||||
|
interventionModalRef.value?.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
creationType.value = type;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetType = () => {
|
||||||
|
creationType.value = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInterventionSubmit = (formData) => {
|
||||||
|
const practitioner = practitioners.value.find(
|
||||||
|
(p) => p.id?.toString() === formData.assigned_practitioner_id?.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
const collaboratorName = practitioner
|
||||||
|
? `${practitioner.employee?.first_name || ""} ${
|
||||||
|
practitioner.employee?.last_name || ""
|
||||||
|
}`.trim()
|
||||||
|
: collaborators.value[0]?.name || "-";
|
||||||
|
|
||||||
|
interventions.value.unshift({
|
||||||
|
id: Date.now(),
|
||||||
|
date: formData.scheduled_at
|
||||||
|
? new Date(formData.scheduled_at).toISOString()
|
||||||
|
: new Date().toISOString(),
|
||||||
|
type: formData.product_name || formData.type || "Intervention",
|
||||||
|
deceased: formData.deceased_name || "Défunt",
|
||||||
|
client: formData.client_name || "Client",
|
||||||
|
collaborator: collaboratorName,
|
||||||
|
status: "En attente",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitLeave = () => {
|
||||||
|
if (!leaveForm.value.employee || !leaveForm.value.startDate) {
|
||||||
|
alert("Veuillez remplir les champs obligatoires de la demande de congé.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
interventions.value.unshift({
|
||||||
|
id: Date.now() + 1,
|
||||||
|
date: new Date(`${leaveForm.value.startDate}T09:00`).toISOString(),
|
||||||
|
type: "Congé",
|
||||||
|
deceased: `Congé: ${leaveForm.value.employee}`,
|
||||||
|
client: leaveForm.value.reason || "Demande de congé",
|
||||||
|
collaborator: leaveForm.value.employee,
|
||||||
|
status: "En attente",
|
||||||
|
});
|
||||||
|
|
||||||
|
leaveForm.value = {
|
||||||
|
employee: "",
|
||||||
|
startDate: "",
|
||||||
|
endDate: "",
|
||||||
|
reason: "",
|
||||||
|
};
|
||||||
|
closeNewRequestModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLeaveForm = ({ field, value }) => {
|
||||||
|
leaveForm.value = {
|
||||||
|
...leaveForm.value,
|
||||||
|
[field]: value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitEvent = () => {
|
||||||
|
if (!eventForm.value.title || !eventForm.value.date) {
|
||||||
|
alert("Veuillez remplir les champs obligatoires de l'événement.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
interventions.value.unshift({
|
||||||
|
id: Date.now() + 2,
|
||||||
|
date: new Date(eventForm.value.date).toISOString(),
|
||||||
|
type: "Événement",
|
||||||
|
deceased: eventForm.value.title,
|
||||||
|
client: eventForm.value.location || eventForm.value.description || "Événement interne",
|
||||||
|
collaborator: "Équipe",
|
||||||
|
status: "Confirmé",
|
||||||
|
});
|
||||||
|
|
||||||
|
eventForm.value = {
|
||||||
|
title: "",
|
||||||
|
date: "",
|
||||||
|
location: "",
|
||||||
|
description: "",
|
||||||
|
};
|
||||||
|
closeNewRequestModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateEventForm = ({ field, value }) => {
|
||||||
|
eventForm.value = {
|
||||||
|
...eventForm.value,
|
||||||
|
[field]: value,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCellClick = (info) => {
|
const handleCellClick = (info) => {
|
||||||
@ -118,11 +294,15 @@ const handlePrevWeek = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdateStatus = (payload) => {
|
const handleUpdateStatus = (payload) => {
|
||||||
const intervention = interventions.value.find(i => i.id.toString() === payload.id);
|
const intervention = interventions.value.find(
|
||||||
|
(i) => i.id.toString() === payload.id
|
||||||
|
);
|
||||||
if (intervention) {
|
if (intervention) {
|
||||||
intervention.status = payload.status;
|
intervention.status = payload.status;
|
||||||
// In a real app, we would make an API call here to persist the change
|
// In a real app, we would make an API call here to persist the change
|
||||||
console.log(`Updated status of intervention ${payload.id} to ${payload.status}`);
|
console.log(
|
||||||
|
`Updated status of intervention ${payload.id} to ${payload.status}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user