convoyForm
This commit is contained in:
parent
3e6ac4055c
commit
d8d2b68421
@ -5,9 +5,71 @@
|
||||
|
||||
<div class="multisteps-form__content">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 col-sm-6">
|
||||
<label class="form-label">ID défunt <span class="text-danger">*</span></label>
|
||||
<soft-input v-model="form.deceased_id" type="number" min="1" />
|
||||
<div class="col-12 col-sm-6 position-relative">
|
||||
<label class="form-label">Défunt <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input
|
||||
v-model="deceasedSearch"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Rechercher un défunt par nom"
|
||||
@input="handleDeceasedSearch"
|
||||
/>
|
||||
<button
|
||||
v-if="selectedDeceased"
|
||||
class="btn btn-outline-secondary mb-0"
|
||||
type="button"
|
||||
@click="clearSelectedDeceased"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="deceasedLoading"
|
||||
class="text-center py-2 position-absolute w-100 bg-white border rounded shadow-sm"
|
||||
style="z-index: 1000; top: 100%"
|
||||
>
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
||||
<span class="visually-hidden">Chargement...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="deceasedResults.length > 0 && showDeceasedResults"
|
||||
class="list-group position-absolute w-100 mt-1 shadow-lg"
|
||||
style="z-index: 1000; max-height: 300px; overflow-y: auto"
|
||||
>
|
||||
<button
|
||||
v-for="deceased in deceasedResults"
|
||||
:key="deceased.id"
|
||||
type="button"
|
||||
class="list-group-item list-group-item-action"
|
||||
@click="selectDeceased(deceased)"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="font-weight-bold text-sm">
|
||||
{{ [deceased.first_name, deceased.last_name].filter(Boolean).join(' ') || 'Défunt' }}
|
||||
</span>
|
||||
<span class="text-xs text-muted">
|
||||
{{ deceased.death_date || deceased.birth_date || 'Aucune date renseignée' }}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="deceasedSearch && !deceasedLoading && showDeceasedResults"
|
||||
class="position-absolute w-100 mt-1 p-3 bg-white border rounded shadow-sm text-center text-sm text-muted"
|
||||
style="z-index: 1000"
|
||||
>
|
||||
Aucun défunt trouvé.
|
||||
</div>
|
||||
|
||||
<div v-if="selectedDeceased" class="mt-2 small text-success">
|
||||
Sélectionné: {{ [selectedDeceased.first_name, selectedDeceased.last_name].filter(Boolean).join(' ') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-3 mt-sm-0">
|
||||
<label class="form-label">Titre de mission</label>
|
||||
@ -47,9 +109,69 @@
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 col-sm-6">
|
||||
<label class="form-label">Ville de départ</label>
|
||||
<soft-input v-model="form.departure_city" type="text" placeholder="Ex. Paris" />
|
||||
<div class="col-12 col-sm-6 position-relative">
|
||||
<label class="form-label">Lieu de départ</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input
|
||||
v-model="locationSearch"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Rechercher un lieu par nom, ville..."
|
||||
@input="handleLocationSearch"
|
||||
/>
|
||||
<button
|
||||
v-if="selectedLocation"
|
||||
class="btn btn-outline-secondary mb-0"
|
||||
type="button"
|
||||
@click="clearSelectedLocation"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="locationLoading"
|
||||
class="text-center py-2 position-absolute w-100 bg-white border rounded shadow-sm"
|
||||
style="z-index: 1000; top: 100%"
|
||||
>
|
||||
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
||||
<span class="visually-hidden">Chargement...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="locationResults.length > 0 && showLocationResults"
|
||||
class="list-group position-absolute w-100 mt-1 shadow-lg"
|
||||
style="z-index: 1000; max-height: 300px; overflow-y: auto"
|
||||
>
|
||||
<button
|
||||
v-for="location in locationResults"
|
||||
:key="location.id"
|
||||
type="button"
|
||||
class="list-group-item list-group-item-action"
|
||||
@click="selectLocation(location)"
|
||||
>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="font-weight-bold text-sm">{{ location.name || 'Lieu sans nom' }}</span>
|
||||
<span class="text-xs text-muted">
|
||||
{{ [location.address_line1, location.postal_code, location.city].filter(Boolean).join(', ') }}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="locationSearch && !locationLoading && showLocationResults"
|
||||
class="position-absolute w-100 mt-1 p-3 bg-white border rounded shadow-sm text-center text-sm text-muted"
|
||||
style="z-index: 1000"
|
||||
>
|
||||
Aucun lieu trouvé.
|
||||
</div>
|
||||
|
||||
<div v-if="selectedLocation" class="mt-2 small text-success">
|
||||
Sélectionné: {{ selectedLocation.name || 'Lieu' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-3 mt-sm-0">
|
||||
<label class="form-label">Email famille</label>
|
||||
@ -74,12 +196,27 @@ import { defineEmits, defineProps } from "vue";
|
||||
import { ref } from "vue";
|
||||
import SoftInput from "@/components/SoftInput.vue";
|
||||
import SoftButton from "@/components/SoftButton.vue";
|
||||
import { useClientLocationStore } from "@/stores/clientLocation";
|
||||
import { useDeceasedStore } from "@/stores/deceasedStore";
|
||||
|
||||
const props = defineProps({
|
||||
loading: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
const emit = defineEmits(["createConvoy"]);
|
||||
const clientLocationStore = useClientLocationStore();
|
||||
const deceasedStore = useDeceasedStore();
|
||||
const deceasedSearch = ref("");
|
||||
const deceasedResults = ref([]);
|
||||
const deceasedLoading = ref(false);
|
||||
const showDeceasedResults = ref(false);
|
||||
const selectedDeceased = ref(null);
|
||||
const locationSearch = ref("");
|
||||
const locationResults = ref([]);
|
||||
const locationLoading = ref(false);
|
||||
const showLocationResults = ref(false);
|
||||
const selectedLocation = ref(null);
|
||||
let debounceTimeout = null;
|
||||
|
||||
const defaultForm = () => ({
|
||||
deceased_id: "",
|
||||
@ -88,12 +225,110 @@ const defaultForm = () => ({
|
||||
transport_mode: "road",
|
||||
planned_start_at: "",
|
||||
estimated_end_at: "",
|
||||
departure_location_id: null,
|
||||
departure_name: "",
|
||||
departure_address: "",
|
||||
departure_city: "",
|
||||
departure_postal_code: "",
|
||||
departure_country_code: "",
|
||||
family_email: "",
|
||||
});
|
||||
|
||||
const form = ref(defaultForm());
|
||||
|
||||
const handleDeceasedSearch = () => {
|
||||
showDeceasedResults.value = true;
|
||||
|
||||
if (debounceTimeout) clearTimeout(debounceTimeout);
|
||||
|
||||
if (!deceasedSearch.value.trim()) {
|
||||
deceasedResults.value = [];
|
||||
showDeceasedResults.value = false;
|
||||
clearSelectedDeceased();
|
||||
return;
|
||||
}
|
||||
|
||||
deceasedLoading.value = true;
|
||||
|
||||
debounceTimeout = setTimeout(async () => {
|
||||
try {
|
||||
const results = await deceasedStore.searchDeceased(deceasedSearch.value);
|
||||
deceasedResults.value = results || [];
|
||||
} catch (error) {
|
||||
console.error("Error searching deceased:", error);
|
||||
deceasedResults.value = [];
|
||||
} finally {
|
||||
deceasedLoading.value = false;
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const selectDeceased = (deceased) => {
|
||||
selectedDeceased.value = deceased;
|
||||
deceasedSearch.value = [deceased.first_name, deceased.last_name].filter(Boolean).join(" ");
|
||||
form.value.deceased_id = deceased.id;
|
||||
deceasedResults.value = [];
|
||||
showDeceasedResults.value = false;
|
||||
};
|
||||
|
||||
const clearSelectedDeceased = () => {
|
||||
selectedDeceased.value = null;
|
||||
form.value.deceased_id = "";
|
||||
};
|
||||
|
||||
const handleLocationSearch = () => {
|
||||
showLocationResults.value = true;
|
||||
|
||||
if (debounceTimeout) clearTimeout(debounceTimeout);
|
||||
|
||||
if (!locationSearch.value.trim()) {
|
||||
locationResults.value = [];
|
||||
showLocationResults.value = false;
|
||||
clearSelectedLocation();
|
||||
return;
|
||||
}
|
||||
|
||||
locationLoading.value = true;
|
||||
|
||||
debounceTimeout = setTimeout(async () => {
|
||||
try {
|
||||
const response = await clientLocationStore.fetchClientLocations({
|
||||
search: locationSearch.value,
|
||||
per_page: 10,
|
||||
});
|
||||
locationResults.value = response.data || [];
|
||||
} catch (error) {
|
||||
console.error("Error searching locations:", error);
|
||||
locationResults.value = [];
|
||||
} finally {
|
||||
locationLoading.value = false;
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const selectLocation = (location) => {
|
||||
selectedLocation.value = location;
|
||||
locationSearch.value = location.name || [location.address_line1, location.city].filter(Boolean).join(", ");
|
||||
form.value.departure_location_id = location.id;
|
||||
form.value.departure_name = location.name || null;
|
||||
form.value.departure_address = location.address_line1 || null;
|
||||
form.value.departure_city = location.city || null;
|
||||
form.value.departure_postal_code = location.postal_code || null;
|
||||
form.value.departure_country_code = location.country_code || null;
|
||||
locationResults.value = [];
|
||||
showLocationResults.value = false;
|
||||
};
|
||||
|
||||
const clearSelectedLocation = () => {
|
||||
selectedLocation.value = null;
|
||||
form.value.departure_location_id = null;
|
||||
form.value.departure_name = "";
|
||||
form.value.departure_address = "";
|
||||
form.value.departure_city = "";
|
||||
form.value.departure_postal_code = "";
|
||||
form.value.departure_country_code = "";
|
||||
};
|
||||
|
||||
const submitForm = () => {
|
||||
emit("createConvoy", {
|
||||
deceased_id: Number(form.value.deceased_id),
|
||||
@ -102,12 +337,28 @@ const submitForm = () => {
|
||||
transport_mode: form.value.transport_mode,
|
||||
planned_start_at: form.value.planned_start_at,
|
||||
estimated_end_at: form.value.estimated_end_at || null,
|
||||
departure_location_selection_mode: "place",
|
||||
departure_location_id: form.value.departure_location_id || null,
|
||||
departure_name: form.value.departure_name || null,
|
||||
departure_address: form.value.departure_address || null,
|
||||
departure_city: form.value.departure_city || null,
|
||||
departure_postal_code: form.value.departure_postal_code || null,
|
||||
departure_country_code: form.value.departure_country_code || null,
|
||||
family_email: form.value.family_email || null,
|
||||
});
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
form.value = defaultForm();
|
||||
deceasedSearch.value = "";
|
||||
deceasedResults.value = [];
|
||||
deceasedLoading.value = false;
|
||||
showDeceasedResults.value = false;
|
||||
selectedDeceased.value = null;
|
||||
locationSearch.value = "";
|
||||
locationResults.value = [];
|
||||
locationLoading.value = false;
|
||||
showLocationResults.value = false;
|
||||
selectedLocation.value = null;
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -63,8 +63,31 @@ export const useDeceasedStore = defineStore("deceased", () => {
|
||||
success.value = false;
|
||||
};
|
||||
|
||||
const normalizeDeceased = (entry: Partial<Deceased> | null | undefined): Deceased | null => {
|
||||
if (!entry || typeof entry !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: entry.id,
|
||||
last_name: entry.last_name || "",
|
||||
first_name: entry.first_name || "",
|
||||
full_name: entry.full_name,
|
||||
birth_date: entry.birth_date,
|
||||
death_date: entry.death_date,
|
||||
place_of_death: entry.place_of_death,
|
||||
notes: entry.notes,
|
||||
documents_count: entry.documents_count,
|
||||
interventions_count: entry.interventions_count,
|
||||
created_at: entry.created_at,
|
||||
updated_at: entry.updated_at,
|
||||
};
|
||||
};
|
||||
|
||||
const setDeceased = (newDeceased: Deceased[]) => {
|
||||
deceased.value = newDeceased;
|
||||
deceased.value = (newDeceased || [])
|
||||
.map((entry) => normalizeDeceased(entry))
|
||||
.filter((entry): entry is Deceased => entry !== null);
|
||||
};
|
||||
|
||||
const setCurrentDeceased = (deceased: Deceased | null) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user