2025-11-12 16:44:12 +03:00

429 lines
11 KiB
Vue

<template>
<div class="modal-intervention-form">
<!-- Client Selection -->
<div class="mb-3">
<label class="form-label">
Client <span class="text-danger">*</span>
</label>
<modal-search
:search-action="searchClients"
:selected-item="selectedClient"
title="Rechercher un client"
search-label="Nom du client"
search-placeholder="Entrez le nom du client..."
item-key="id"
item-label="name"
:item-description="getClientDescription"
:item-meta="getClientMeta"
@select="handleClientSelect"
@confirm="handleClientConfirm"
/>
<div v-if="fieldErrors.client_id" class="invalid-feedback">
{{ fieldErrors.client_id }}
</div>
</div>
<!-- Selected Client Display -->
<div v-if="selectedClient" class="selected-display mb-3">
<div class="alert alert-success">
<div class="d-flex justify-content-between align-items-center">
<div>
<strong>{{ selectedClient.name }}</strong>
<div class="text-sm text-muted">
{{ getClientDescription(selectedClient) }}
</div>
</div>
<span class="badge bg-success">Sélectionné</span>
</div>
</div>
</div>
<!-- Deceased Selection -->
<div class="mb-3">
<label class="form-label">Personne décédée</label>
<!-- Selected Deceased Display -->
<div v-if="selectedDeceased" class="selected-display mb-2">
<div class="alert alert-info">
<div class="d-flex justify-content-between align-items-center">
<div>
<strong>{{ getDeceasedFullName(selectedDeceased) }}</strong>
<div class="text-sm text-muted">
{{
selectedDeceased.birth_date
? `Né(e) le ${formatDate(selectedDeceased.birth_date)}`
: "Date de naissance inconnue"
}}
</div>
</div>
<span class="badge bg-info">Pré-sélectionné</span>
</div>
</div>
</div>
<!-- Search for deceased if needed -->
<div v-if="!selectedDeceased">
<modal-search
:search-action="searchDeceased"
:selected-item="selectedDeceased"
title="Rechercher une personne décédée"
search-label="Nom de la personne"
search-placeholder="Entrez le nom de la personne..."
item-key="id"
:item-label="getDeceasedFullName"
:item-description="getDeceasedDescription"
:item-meta="getDeceasedMeta"
@select="handleDeceasedSelect"
@confirm="handleDeceasedConfirm"
/>
</div>
</div>
<!-- Intervention Type -->
<div class="mb-3">
<label class="form-label">
Type d'intervention <span class="text-danger">*</span>
</label>
<select
v-model="form.type"
class="form-select"
:class="{ 'is-invalid': fieldErrors.type }"
>
<option value="">Sélectionnez un type d'intervention</option>
<option value="thanatopraxie">Thanatopraxie</option>
<option value="toilette_mortuaire">Toilette mortuaire</option>
<option value="exhumation">Exhumation</option>
<option value="retrait_pacemaker">Retrait pacemaker</option>
<option value="retrait_bijoux">Retrait bijoux</option>
<option value="autre">Autre</option>
</select>
<div v-if="fieldErrors.type" class="invalid-feedback">
{{ fieldErrors.type }}
</div>
</div>
<!-- Date and Time -->
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">Date de l'intervention</label>
<input
v-model="form.scheduled_date"
type="date"
class="form-control"
:class="{ 'is-invalid': fieldErrors.scheduled_at }"
/>
</div>
<div class="col-md-6">
<label class="form-label">Heure de l'intervention</label>
<input
v-model="form.scheduled_time"
type="time"
class="form-control"
:class="{ 'is-invalid': fieldErrors.scheduled_at }"
/>
</div>
<div v-if="fieldErrors.scheduled_at" class="invalid-feedback">
{{ fieldErrors.scheduled_at }}
</div>
</div>
<!-- Duration and Status -->
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">Durée (minutes)</label>
<input
v-model="form.duration_min"
type="number"
class="form-control"
placeholder="ex. 90"
min="1"
/>
</div>
<div class="col-md-6">
<label class="form-label">Statut</label>
<select v-model="form.status" class="form-select">
<option value="">Sélectionnez un statut</option>
<option value="demande">Demande</option>
<option value="planifie">Planifié</option>
<option value="en_cours">En cours</option>
<option value="termine">Terminé</option>
<option value="annule">Annulé</option>
</select>
</div>
</div>
<!-- Order Giver -->
<div class="mb-3">
<label class="form-label">Donneur d'ordre</label>
<input
v-model="form.order_giver"
type="text"
class="form-control"
placeholder="Nom du donneur d'ordre"
/>
</div>
<!-- Notes -->
<div class="mb-3">
<label class="form-label">Notes et observations</label>
<textarea
v-model="form.notes"
class="form-control"
rows="3"
placeholder="Informations complémentaires, instructions spéciales..."
maxlength="2000"
></textarea>
</div>
<!-- Form Validation Errors -->
<div v-if="formValidationError" class="alert alert-warning">
<i class="fas fa-exclamation-triangle me-2"></i>
{{ formValidationError }}
</div>
</div>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { defineProps, defineEmits, defineExpose } from "vue";
import ModalSearch from "@/components/atoms/input/ModalSearch.vue";
const props = defineProps({
searchClients: {
type: Function,
required: true,
},
searchDeceased: {
type: Function,
required: true,
},
onClientSelect: {
type: Function,
default: null,
},
onDeceasedSelect: {
type: Function,
default: null,
},
selectedDeceased: {
type: Object,
default: null,
},
loading: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["create-intervention"]);
// Form data
const form = ref({
client_id: "",
deceased_id: "",
type: "",
scheduled_date: "",
scheduled_time: "",
duration_min: "",
status: "",
order_giver: "",
notes: "",
});
// Field errors
const fieldErrors = ref({});
// Selected items
const selectedClient = ref(null);
const selectedDeceased = ref(props.selectedDeceased);
// Store functions
const searchClients = async (params) => {
return await props.searchClients(params.query);
};
const searchDeceased = async (params) => {
return await props.searchDeceased(params.query);
};
// Client methods
const handleClientSelect = (client) => {
selectedClient.value = client;
};
const handleClientConfirm = (client) => {
selectedClient.value = client;
form.value.client_id = client.id.toString();
if (props.onClientSelect) {
props.onClientSelect(client);
}
};
const getClientDescription = (client) => {
return client.email || "Pas d'email";
};
const getClientMeta = (client) => {
return client.phone ? `Tél: ${client.phone}` : "Pas de téléphone";
};
// Deceased methods
const handleDeceasedSelect = (deceased) => {
selectedDeceased.value = deceased;
};
const handleDeceasedConfirm = (deceased) => {
selectedDeceased.value = deceased;
form.value.deceased_id = deceased.id.toString();
if (props.onDeceasedSelect) {
props.onDeceasedSelect(deceased);
}
};
const getDeceasedFullName = (deceased) => {
return `${deceased.last_name} ${deceased.first_name || ""}`.trim();
};
const getDeceasedDescription = (deceased) => {
return `Né(e) le ${formatDate(deceased.birth_date)}`;
};
const getDeceasedMeta = (deceased) => {
return `Décédé(e) le ${formatDate(deceased.death_date)}`;
};
const formatDate = (dateString) => {
if (!dateString) return "Date inconnue";
return new Date(dateString).toLocaleDateString("fr-FR");
};
// Form validation
const formValidationError = computed(() => {
if (!form.value.client_id) {
return "Le client est obligatoire";
}
if (!form.value.type) {
return "Le type d'intervention est obligatoire";
}
return null;
});
// Form submission
const submitForm = () => {
// Clear field errors
fieldErrors.value = {};
// Check for validation errors
if (formValidationError.value) {
return;
}
// Validate required fields and set field errors
if (!selectedClient.value) {
fieldErrors.value.client_id = "Le client est obligatoire";
return;
}
if (!form.value.type) {
fieldErrors.value.type = "Le type d'intervention est obligatoire";
return;
}
// Combine date and time into scheduled_at
const cleanedForm = { ...form.value };
if (cleanedForm.scheduled_date && cleanedForm.scheduled_time) {
cleanedForm.scheduled_at = `${cleanedForm.scheduled_date} ${cleanedForm.scheduled_time}:00`;
delete cleanedForm.scheduled_date;
delete cleanedForm.scheduled_time;
}
// Set IDs
cleanedForm.client_id = selectedClient.value.id;
if (selectedDeceased.value) {
cleanedForm.deceased_id = selectedDeceased.value.id;
}
// Convert string numbers to integers
if (cleanedForm.client_id) {
cleanedForm.client_id = parseInt(cleanedForm.client_id);
}
if (cleanedForm.deceased_id) {
cleanedForm.deceased_id = parseInt(cleanedForm.deceased_id);
}
if (cleanedForm.duration_min) {
cleanedForm.duration_min = parseInt(cleanedForm.duration_min);
}
// Emit the create-intervention event
emit("create-intervention", cleanedForm);
};
// Watch for pre-selected deceased from parent
watch(
() => props.selectedDeceased,
(newValue) => {
if (newValue) {
selectedDeceased.value = newValue;
form.value.deceased_id = newValue.id.toString();
if (props.onDeceasedSelect) {
props.onDeceasedSelect(newValue);
}
}
},
{ immediate: true }
);
// Expose submitForm function to parent component
defineExpose({
submitForm,
});
</script>
<style scoped>
.modal-intervention-form {
height: 100%;
overflow-y: auto;
padding: 1.5rem;
}
.selected-display .alert {
margin-bottom: 0;
}
.form-label {
font-weight: 600;
margin-bottom: 0.5rem;
}
.text-danger {
color: #f5365c;
}
.invalid-feedback {
display: block;
font-size: 0.875rem;
color: #dc3545;
margin-top: 0.25rem;
}
.form-select.is-invalid,
.form-control.is-invalid {
border-color: #dc3545;
padding-right: calc(1.5em + 0.75rem);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath d='M5.8 4.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right calc(0.375em + 0.1875rem) center;
background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
.alert-warning {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
}
.alert i {
font-size: 0.8rem;
}
</style>