311 lines
9.0 KiB
Vue
311 lines
9.0 KiB
Vue
<template>
|
|
<div class="multisteps-form__panel" :class="isActive ? activeClass : ''">
|
|
<h6 class="mb-3 text-dark font-weight-bold">Informations du Défunt</h6>
|
|
|
|
<div class="row mb-4">
|
|
<div class="col-12 d-flex justify-content-center">
|
|
<div class="btn-group" role="group">
|
|
<input
|
|
id="modeNew"
|
|
type="radio"
|
|
class="btn-check"
|
|
name="deceasedMode"
|
|
autocomplete="off"
|
|
:checked="!formData.is_existing"
|
|
@change="
|
|
formData.is_existing = false;
|
|
formData.id = null;
|
|
"
|
|
/>
|
|
<label class="btn btn-outline-primary" for="modeNew"
|
|
>Nouveau Défunt</label
|
|
>
|
|
|
|
<input
|
|
id="modeSearch"
|
|
type="radio"
|
|
class="btn-check"
|
|
name="deceasedMode"
|
|
autocomplete="off"
|
|
:checked="formData.is_existing"
|
|
@change="formData.is_existing = true"
|
|
/>
|
|
<label class="btn btn-outline-primary" for="modeSearch"
|
|
>Rechercher</label
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SEARCH MODE -->
|
|
<div v-if="formData.is_existing" class="row">
|
|
<div class="col-12 mb-3 position-relative">
|
|
<label class="form-label">Rechercher un défunt (Nom, Prénom)</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
|
<input
|
|
v-model="searchQuery"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': hasError('deceased_id') }"
|
|
placeholder="Tapez pour rechercher..."
|
|
@input="handleSearchInput"
|
|
/>
|
|
<button
|
|
v-if="formData.id"
|
|
class="btn btn-outline-secondary"
|
|
type="button"
|
|
@click="clearSelection"
|
|
>
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<div
|
|
v-if="getFieldError('deceased_id')"
|
|
class="invalid-feedback d-block"
|
|
>
|
|
{{ getFieldError("deceased_id") }}
|
|
</div>
|
|
|
|
<!-- 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"
|
|
>
|
|
<button
|
|
v-for="deceased in searchResults"
|
|
:key="deceased.id"
|
|
type="button"
|
|
class="list-group-item list-group-item-action"
|
|
@click="selectDeceased(deceased)"
|
|
>
|
|
<div class="d-flex w-100 justify-content-between">
|
|
<h6 class="mb-1">
|
|
{{ deceased.first_name }} {{ deceased.last_name }}
|
|
</h6>
|
|
<small
|
|
>{{ deceased.birth_date }} - {{ deceased.death_date }}</small
|
|
>
|
|
</div>
|
|
</button>
|
|
</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 class="list-group-item text-muted">Aucun résultat trouvé.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="formData.id" class="col-12">
|
|
<div class="alert alert-info">
|
|
<strong>Défunt sélectionné:</strong> {{ formData.first_name }}
|
|
{{ formData.last_name }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CREATE MODE -->
|
|
<div v-else class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Prénom</label>
|
|
<input
|
|
v-model="formData.first_name"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': hasError('first_name') }"
|
|
placeholder="Prénom du défunt"
|
|
maxlength="191"
|
|
/>
|
|
<div v-if="getFieldError('first_name')" class="invalid-feedback">
|
|
{{ getFieldError("first_name") }}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Nom *</label>
|
|
<input
|
|
v-model="formData.last_name"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': hasError('last_name') }"
|
|
placeholder="Nom du défunt"
|
|
required
|
|
maxlength="191"
|
|
/>
|
|
<div v-if="getFieldError('last_name')" class="invalid-feedback">
|
|
{{ getFieldError("last_name") }}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Date de naissance</label>
|
|
<input
|
|
v-model="formData.birth_date"
|
|
type="date"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': hasError('birth_date') }"
|
|
/>
|
|
<div v-if="getFieldError('birth_date')" class="invalid-feedback">
|
|
{{ getFieldError("birth_date") }}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Date de décès</label>
|
|
<input
|
|
v-model="formData.death_date"
|
|
type="date"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': hasError('death_date') }"
|
|
/>
|
|
<div v-if="getFieldError('death_date')" class="invalid-feedback">
|
|
{{ getFieldError("death_date") }}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Lieu de décès</label>
|
|
<input
|
|
v-model="formData.place_of_death"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': hasError('place_of_death') }"
|
|
placeholder="Lieu de décès"
|
|
maxlength="255"
|
|
/>
|
|
<div v-if="getFieldError('place_of_death')" class="invalid-feedback">
|
|
{{ getFieldError("place_of_death") }}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Genre</label>
|
|
<select v-model="formData.gender" class="form-select">
|
|
<option value="">Sélectionner</option>
|
|
<option value="male">Homme</option>
|
|
<option value="female">Femme</option>
|
|
<option value="other">Autre</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-12 mb-3">
|
|
<label class="form-label">Notes</label>
|
|
<textarea
|
|
v-model="formData.notes"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': hasError('notes') }"
|
|
rows="3"
|
|
placeholder="Notes supplémentaires..."
|
|
></textarea>
|
|
<div v-if="getFieldError('notes')" class="invalid-feedback">
|
|
{{ getFieldError("notes") }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-end mt-3">
|
|
<button
|
|
type="button"
|
|
class="btn bg-gradient-primary"
|
|
:disabled="validating"
|
|
@click="$emit('next')"
|
|
>
|
|
<span v-if="validating">
|
|
<i class="fas fa-spinner fa-spin me-2"></i>
|
|
Validation...
|
|
</span>
|
|
<span v-else>
|
|
Suivant
|
|
<i class="fas fa-arrow-right ms-2"></i>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { defineProps, defineEmits, ref, watch } from "vue";
|
|
import DeceasedService from "@/services/deceased";
|
|
|
|
const props = defineProps({
|
|
formData: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
isActive: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
activeClass: {
|
|
type: String,
|
|
default: "js-active",
|
|
},
|
|
errors: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
validating: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(["next"]);
|
|
|
|
// Search state
|
|
const searchQuery = ref("");
|
|
const searchResults = ref([]);
|
|
const isSearching = ref(false);
|
|
const showResults = ref(false);
|
|
|
|
// Debounce search
|
|
let searchTimeout;
|
|
const handleSearchInput = () => {
|
|
if (searchTimeout) clearTimeout(searchTimeout);
|
|
|
|
if (searchQuery.value.length < 2) {
|
|
searchResults.value = [];
|
|
showResults.value = false;
|
|
return;
|
|
}
|
|
|
|
isSearching.value = true;
|
|
searchTimeout = setTimeout(async () => {
|
|
try {
|
|
const results = await DeceasedService.searchDeceased(searchQuery.value);
|
|
searchResults.value = results;
|
|
showResults.value = true;
|
|
} catch (e) {
|
|
console.error("Search failed", e);
|
|
} finally {
|
|
isSearching.value = false;
|
|
}
|
|
}, 300);
|
|
};
|
|
|
|
const selectDeceased = (deceased) => {
|
|
props.formData.id = deceased.id;
|
|
props.formData.first_name = deceased.first_name;
|
|
props.formData.last_name = deceased.last_name;
|
|
searchQuery.value = `${deceased.first_name || ""} ${deceased.last_name}`;
|
|
showResults.value = false;
|
|
};
|
|
|
|
const clearSelection = () => {
|
|
props.formData.id = null;
|
|
searchQuery.value = "";
|
|
searchResults.value = [];
|
|
};
|
|
|
|
// Error helpers using props
|
|
const getFieldError = (field) => {
|
|
const error = props.errors.find((err) => err.field === field);
|
|
return error ? error.message : "";
|
|
};
|
|
|
|
const hasError = (field) => {
|
|
return props.errors.some((err) => err.field === field);
|
|
};
|
|
</script>
|