-
-
-
- {{ getFieldError("scheduled_at") }}
-
+
+
+
+
+ {{ getFieldError("scheduled_at") }}
-
-
-
-
-
-
-
-
-
-
- {{ getFieldError("product_id") }}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {{ getFieldError("product_id") }}
+
+
+
+
+
+
-
-
-
-
-
-
-
- {{ getFieldError("assigned_practitioner_id") }}
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {{ getFieldError("assigned_practitioner_id") }}
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
📋 Optionnel - À ajouter si fourni par la famille ou dans les documents reçus
-
+
+
+
+
+
+
+
+
+
+
+ 📋 Optionnel - À ajouter si fourni par la famille ou dans les
+ documents reçus
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ getFieldError("location.name") }}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getFieldError("location.name") }}
+
+
-
-
-
-
-
-
-
- {{ getFieldError("location.name") }}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {{ getFieldError("location.name") }}
+
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -414,7 +592,7 @@ import {
defineEmits,
defineExpose,
onMounted,
- nextTick
+ nextTick,
} from "vue";
import { Modal } from "bootstrap";
import DeceasedService from "@/services/deceased";
@@ -470,19 +648,20 @@ let clientSearchTimeout;
// Product
const productForm = ref({
- product_id: null
+ product_id: null,
});
const productSearchQuery = ref("");
const productSearchResults = ref([]);
const allProducts = ref([]);
const showProductResults = ref(false);
+let productSearchTimeout;
// Location - FIXED: Start with search mode (true = search existing, false = create new)
const locationForm = ref({
- id: null,
- is_existing: true, // Start with search mode
- name: "",
- city: "",
+ id: null,
+ is_existing: true, // Start with search mode
+ name: "",
+ city: "",
});
const locationSearchQuery = ref("");
const locationSearchResults = ref([]);
@@ -496,38 +675,45 @@ const interventionForm = ref({
assigned_practitioner_id: "",
notes: "",
medical_prescription_number: "",
- order_giver: "",
+ order_giver: "",
type: "thanatopraxie",
});
// --- METHODS ---
onMounted(async () => {
- if (modalRef.value) {
- modalInstance = new Modal(modalRef.value);
- modalRef.value.addEventListener("hidden.bs.modal", () => {
- emit("close");
- });
- }
-
- if (props.initialDate) {
- interventionForm.value.scheduled_at = props.initialDate;
- } else {
- const now = new Date();
- now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
- interventionForm.value.scheduled_at = now.toISOString().slice(0, 16);
- }
+ if (modalRef.value) {
+ modalInstance = new Modal(modalRef.value);
+ modalRef.value.addEventListener("hidden.bs.modal", () => {
+ emit("close");
+ });
+ }
- try {
- const pService = new ProductService();
- const response = await pService.getAllProducts({
- per_page: 100,
- is_intervention: true
- });
- allProducts.value = response.data;
- productSearchResults.value = allProducts.value;
- } catch (e) {
- console.error("Failed to load products", e);
- }
+ if (props.initialDate) {
+ interventionForm.value.scheduled_at = props.initialDate;
+ } else {
+ const now = new Date();
+ now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
+ interventionForm.value.scheduled_at = now.toISOString().slice(0, 16);
+ }
+
+ try {
+ const pService = new ProductService();
+ const response = await pService.getAllProducts({
+ per_page: 100,
+ is_intervention: true,
+ });
+
+ const products = Array.isArray(response?.data)
+ ? response.data
+ : Array.isArray(response?.data?.data)
+ ? response.data.data
+ : [];
+
+ allProducts.value = products;
+ productSearchResults.value = products;
+ } catch (e) {
+ console.error("Failed to load products", e);
+ }
});
const show = () => {
@@ -544,341 +730,437 @@ const hide = () => {
// --- DECEASED LOGIC ---
const toggleDeceasedMode = () => {
- deceasedForm.value.is_existing = !deceasedForm.value.is_existing;
- if (!deceasedForm.value.is_existing) {
- clearDeceasedSelection();
- } else {
- deceasedSearchQuery.value = "";
- deceasedSearchResults.value = [];
- }
+ deceasedForm.value.is_existing = !deceasedForm.value.is_existing;
+ if (!deceasedForm.value.is_existing) {
+ clearDeceasedSelection();
+ } else {
+ deceasedSearchQuery.value = "";
+ deceasedSearchResults.value = [];
+ }
};
const handleDeceasedSearch = () => {
- if (deceasedSearchTimeout) clearTimeout(deceasedSearchTimeout);
- if (deceasedSearchQuery.value.length < 2) {
- deceasedSearchResults.value = [];
- return;
+ if (deceasedSearchTimeout) clearTimeout(deceasedSearchTimeout);
+ if (deceasedSearchQuery.value.length < 2) {
+ deceasedSearchResults.value = [];
+ return;
+ }
+ deceasedSearchTimeout = setTimeout(async () => {
+ try {
+ const results = await DeceasedService.searchDeceased(
+ deceasedSearchQuery.value
+ );
+ deceasedSearchResults.value = results;
+ } catch (e) {
+ console.error(e);
}
- deceasedSearchTimeout = setTimeout(async () => {
- try {
- const results = await DeceasedService.searchDeceased(deceasedSearchQuery.value);
- deceasedSearchResults.value = results;
- } catch(e) { console.error(e); }
- }, 300);
+ }, 300);
};
const selectDeceased = (d) => {
- deceasedForm.value.id = d.id;
- deceasedSearchQuery.value = `${d.first_name || ''} ${d.last_name}`;
- deceasedSearchResults.value = [];
- showDeceasedResults.value = false;
-
- // Clear any error for deceased
- errors.value = errors.value.filter(e => e.field !== 'deceased_id');
+ deceasedForm.value.id = d.id;
+ deceasedSearchQuery.value = `${d.first_name || ""} ${d.last_name}`;
+ deceasedSearchResults.value = [];
+ showDeceasedResults.value = false;
+
+ // Clear any error for deceased
+ errors.value = errors.value.filter((e) => e.field !== "deceased_id");
};
const clearDeceasedSelection = () => {
- deceasedForm.value.id = null;
- deceasedSearchQuery.value = "";
- deceasedForm.value.first_name = "";
- deceasedForm.value.last_name = "";
- deceasedForm.value.birth_date = "";
- deceasedForm.value.death_date = "";
- deceasedSearchResults.value = [];
-
- // Clear errors
- errors.value = errors.value.filter(e =>
- e.field !== 'deceased_id' &&
- e.field !== 'deceased.last_name'
- );
+ deceasedForm.value.id = null;
+ deceasedSearchQuery.value = "";
+ deceasedForm.value.first_name = "";
+ deceasedForm.value.last_name = "";
+ deceasedForm.value.birth_date = "";
+ deceasedForm.value.death_date = "";
+ deceasedSearchResults.value = [];
+
+ // Clear errors
+ errors.value = errors.value.filter(
+ (e) => e.field !== "deceased_id" && e.field !== "deceased.last_name"
+ );
};
// --- CLIENT LOGIC ---
const handleClientSearch = () => {
- if (clientSearchTimeout) clearTimeout(clientSearchTimeout);
- if (clientSearchQuery.value.length < 2) {
- clientSearchResults.value = [];
- return;
+ if (clientSearchTimeout) clearTimeout(clientSearchTimeout);
+ if (clientSearchQuery.value.length < 2) {
+ clientSearchResults.value = [];
+ return;
+ }
+ clientSearchTimeout = setTimeout(async () => {
+ try {
+ const results = await ClientService.searchClients(
+ clientSearchQuery.value
+ );
+ clientSearchResults.value = results;
+ } catch (e) {
+ console.error(e);
}
- clientSearchTimeout = setTimeout(async () => {
- try {
- const results = await ClientService.searchClients(clientSearchQuery.value);
- clientSearchResults.value = results;
- } catch(e) { console.error(e); }
- }, 300);
+ }, 300);
};
const selectClient = (c) => {
- selectedClient.value = c;
- clientSearchQuery.value = c.name + (c.email ? ` (${c.email})` : '');
- clientSearchResults.value = [];
- showClientResults.value = false;
-
- // Clear any error for client
- errors.value = errors.value.filter(e => e.field !== 'client');
+ selectedClient.value = c;
+ clientSearchQuery.value = c.name + (c.email ? ` (${c.email})` : "");
+ clientSearchResults.value = [];
+ showClientResults.value = false;
+
+ // Clear any error for client
+ errors.value = errors.value.filter((e) => e.field !== "client");
};
const clearClientSelection = () => {
- selectedClient.value = null;
- clientSearchQuery.value = "";
- clientSearchResults.value = [];
-
- // Clear error
- errors.value = errors.value.filter(e => e.field !== 'client');
+ selectedClient.value = null;
+ clientSearchQuery.value = "";
+ clientSearchResults.value = [];
+
+ // Clear error
+ errors.value = errors.value.filter((e) => e.field !== "client");
};
// --- PRODUCT LOGIC ---
const handleProductSearch = () => {
- const query = productSearchQuery.value.toLowerCase();
- if (!query) {
- productSearchResults.value = allProducts.value;
- return;
+ showProductResults.value = true;
+
+ if (productSearchTimeout) clearTimeout(productSearchTimeout);
+
+ 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 = [];
}
- productSearchResults.value = allProducts.value.filter(p =>
- p.nom.toLowerCase().includes(query)
- );
+ }, 250);
+};
+
+const handleProductFocus = () => {
+ showProductResults.value = true;
+ if (!productSearchQuery.value) {
+ productSearchResults.value = allProducts.value;
+ } else {
+ handleProductSearch();
+ }
};
const selectProduct = (p) => {
- productForm.value.product_id = p.id;
- productSearchQuery.value = p.nom;
- showProductResults.value = false;
-
- // Clear error
- errors.value = errors.value.filter(e => e.field !== 'product_id');
-
- if (!interventionForm.value.type) {
- interventionForm.value.type = 'thanatopraxie';
- }
+ productForm.value.product_id = p.id;
+ productSearchQuery.value = p.nom;
+ showProductResults.value = false;
+
+ // Clear error
+ errors.value = errors.value.filter((e) => e.field !== "product_id");
+
+ if (!interventionForm.value.type) {
+ interventionForm.value.type = "thanatopraxie";
+ }
};
const clearProductSelection = () => {
- productForm.value.product_id = null;
- productSearchQuery.value = "";
- productSearchResults.value = allProducts.value;
-
- // Clear error
- errors.value = errors.value.filter(e => e.field !== 'product_id');
+ productForm.value.product_id = null;
+ productSearchQuery.value = "";
+ productSearchResults.value = allProducts.value;
+
+ // Clear error
+ errors.value = errors.value.filter((e) => e.field !== "product_id");
};
// --- LOCATION LOGIC --- (FIXED)
const toggleLocationMode = () => {
- locationForm.value.is_existing = !locationForm.value.is_existing;
- if (!locationForm.value.is_existing) {
- // Switching to create mode: clear search selection
- clearLocationSelection();
- } else {
- // Switching to search mode: clear create fields
- locationForm.value.name = "";
- locationForm.value.city = "";
- locationSearchQuery.value = "";
- locationSearchResults.value = [];
- }
-
- // Clear location errors
- errors.value = errors.value.filter(e => e.field === 'location.name');
-};
-
-const handleLocationSearch = () => {
- if (locationSearchTimeout) clearTimeout(locationSearchTimeout);
- if (locationSearchQuery.value.length < 2) {
- locationSearchResults.value = [];
- return;
- }
- locationSearchTimeout = setTimeout(async () => {
- try {
- const response = await ClientLocationService.getAllClientLocations({
- search: locationSearchQuery.value,
- per_page: 10
- });
- locationSearchResults.value = response.data;
- } catch (e) { console.error(e); }
- }, 300);
-};
-
-const selectLocation = (loc) => {
- locationForm.value.id = loc.id;
- locationForm.value.name = loc.name;
- locationForm.value.city = loc.city || "";
- let display = loc.name || "";
- if (loc.city) display += ` (${loc.city})`;
- locationSearchQuery.value = display;
- showLocationResults.value = false;
- locationSearchResults.value = [];
-
- // Clear error
- errors.value = errors.value.filter(e => e.field !== 'location.name');
-};
-
-const clearLocationSelection = () => {
- locationForm.value.id = null;
+ locationForm.value.is_existing = !locationForm.value.is_existing;
+ if (!locationForm.value.is_existing) {
+ // Switching to create mode: clear search selection
+ clearLocationSelection();
+ } else {
+ // Switching to search mode: clear create fields
locationForm.value.name = "";
locationForm.value.city = "";
locationSearchQuery.value = "";
locationSearchResults.value = [];
- showLocationResults.value = false;
-
- // Clear error
- errors.value = errors.value.filter(e => e.field !== 'location.name');
+ }
+
+ // Clear location errors
+ errors.value = errors.value.filter((e) => e.field === "location.name");
+};
+
+const handleLocationSearch = () => {
+ if (locationSearchTimeout) clearTimeout(locationSearchTimeout);
+ if (locationSearchQuery.value.length < 2) {
+ locationSearchResults.value = [];
+ return;
+ }
+ locationSearchTimeout = setTimeout(async () => {
+ try {
+ const response = await ClientLocationService.getAllClientLocations({
+ search: locationSearchQuery.value,
+ per_page: 10,
+ });
+ locationSearchResults.value = response.data;
+ } catch (e) {
+ console.error(e);
+ }
+ }, 300);
+};
+
+const selectLocation = (loc) => {
+ locationForm.value.id = loc.id;
+ locationForm.value.name = loc.name;
+ locationForm.value.city = loc.city || "";
+ let display = loc.name || "";
+ if (loc.city) display += ` (${loc.city})`;
+ locationSearchQuery.value = display;
+ showLocationResults.value = false;
+ locationSearchResults.value = [];
+
+ // Clear error
+ errors.value = errors.value.filter((e) => e.field !== "location.name");
+};
+
+const clearLocationSelection = () => {
+ locationForm.value.id = null;
+ locationForm.value.name = "";
+ locationForm.value.city = "";
+ locationSearchQuery.value = "";
+ locationSearchResults.value = [];
+ showLocationResults.value = false;
+
+ // Clear error
+ errors.value = errors.value.filter((e) => e.field !== "location.name");
};
// --- VOICE INPUT ---
const toggleVoiceInput = (field) => {
- alert(`Saisie vocale pour ${field} - Fonctionnalité à implémenter`);
- // You would implement actual voice recognition here
+ alert(`Saisie vocale pour ${field} - Fonctionnalité à implémenter`);
+ // You would implement actual voice recognition here
};
// --- VALIDATION ---
-const hasError = (field) => errors.value.some(e => e.field === field);
-const getFieldError = (field) => errors.value.find(e => e.field === field)?.message || "";
+const hasError = (field) => errors.value.some((e) => e.field === field);
+const getFieldError = (field) =>
+ errors.value.find((e) => e.field === field)?.message || "";
const validate = () => {
- errors.value = [];
- let isValid = true;
-
- // Deceased
- if (deceasedForm.value.is_existing) {
- if (!deceasedForm.value.id) {
- errors.value.push({ field: 'deceased_id', message: 'Veuillez sélectionner un défunt.' });
- isValid = false;
- }
- } else {
- if (!deceasedForm.value.last_name) {
- errors.value.push({ field: 'deceased.last_name', message: 'Le nom du défunt est requis.' });
- isValid = false;
- }
- }
+ errors.value = [];
+ let isValid = true;
- // Client
- if (!selectedClient.value) {
- errors.value.push({ field: 'client', message: "Veuillez sélectionner un client." });
- isValid = false;
+ // Deceased
+ if (deceasedForm.value.is_existing) {
+ if (!deceasedForm.value.id) {
+ errors.value.push({
+ field: "deceased_id",
+ message: "Veuillez sélectionner un défunt.",
+ });
+ isValid = false;
}
+ } else {
+ if (!deceasedForm.value.last_name) {
+ errors.value.push({
+ field: "deceased.last_name",
+ message: "Le nom du défunt est requis.",
+ });
+ isValid = false;
+ }
+ }
- // Intervention fields
- if (!interventionForm.value.scheduled_at) {
- errors.value.push({ field: 'scheduled_at', message: "Date obligatoire." });
- isValid = false;
- }
-
- if (!productForm.value.product_id) {
- errors.value.push({ field: 'product_id', message: "Type de soin requis." });
- isValid = false;
- }
-
- if (!interventionForm.value.assigned_practitioner_id) {
- errors.value.push({ field: 'assigned_practitioner_id', message: "Intervenant requis." });
- isValid = false;
- }
+ // Client
+ if (!selectedClient.value) {
+ errors.value.push({
+ field: "client",
+ message: "Veuillez sélectionner un client.",
+ });
+ isValid = false;
+ }
- // Location
- if (locationForm.value.is_existing) {
- if (!locationForm.value.id) {
- errors.value.push({ field: 'location.name', message: "Veuillez sélectionner un lieu." });
- isValid = false;
- }
- } else {
- if (!locationForm.value.name) {
- errors.value.push({ field: 'location.name', message: "Le nom du lieu est requis." });
- isValid = false;
- }
+ // Intervention fields
+ if (!interventionForm.value.scheduled_at) {
+ errors.value.push({ field: "scheduled_at", message: "Date obligatoire." });
+ isValid = false;
+ }
+
+ if (!productForm.value.product_id) {
+ errors.value.push({ field: "product_id", message: "Type de soin requis." });
+ isValid = false;
+ }
+
+ if (!interventionForm.value.assigned_practitioner_id) {
+ errors.value.push({
+ field: "assigned_practitioner_id",
+ message: "Intervenant requis.",
+ });
+ isValid = false;
+ }
+
+ // Location
+ if (locationForm.value.is_existing) {
+ if (!locationForm.value.id) {
+ errors.value.push({
+ field: "location.name",
+ message: "Veuillez sélectionner un lieu.",
+ });
+ isValid = false;
}
-
- return isValid;
+ } else {
+ if (!locationForm.value.name) {
+ errors.value.push({
+ field: "location.name",
+ message: "Le nom du lieu est requis.",
+ });
+ isValid = false;
+ }
+ }
+
+ return isValid;
};
// --- SUBMIT ---
const handleSubmit = async () => {
- if (submitting.value) return;
-
- if (!validate()) {
- return;
+ if (submitting.value) return;
+
+ if (!validate()) {
+ return;
+ }
+
+ submitting.value = true;
+ globalErrors.value = [];
+
+ try {
+ const formData = new FormData();
+
+ // Deceased
+ if (deceasedForm.value.is_existing && deceasedForm.value.id) {
+ formData.append("deceased_id", deceasedForm.value.id);
+ } else {
+ formData.append(
+ `deceased[first_name]`,
+ deceasedForm.value.first_name || ""
+ );
+ 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);
}
- submitting.value = true;
- globalErrors.value = [];
-
- try {
- const formData = new FormData();
-
- // Deceased
- if (deceasedForm.value.is_existing && deceasedForm.value.id) {
- formData.append("deceased_id", deceasedForm.value.id);
- } else {
- formData.append(`deceased[first_name]`, deceasedForm.value.first_name || "");
- 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
+ if (selectedClient.value) {
+ formData.append("client_id", selectedClient.value.id);
- // Client
- if (selectedClient.value) {
- formData.append("client_id", selectedClient.value.id);
-
- if (selectedClient.value.name) formData.append("client[name]", selectedClient.value.name);
- 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.line1) formData.append("client[billing_address_line1]", selectedClient.value.billing_address.line1);
- 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.siret) formData.append("client[siret]", selectedClient.value.siret);
- }
+ if (selectedClient.value.name)
+ formData.append("client[name]", selectedClient.value.name);
+ if (selectedClient.value.email)
+ formData.append("client[email]", selectedClient.value.email);
+ if (selectedClient.value.phone)
+ formData.append("client[phone]", selectedClient.value.phone);
- // Location
- if (locationForm.value.is_existing && locationForm.value.id) {
- formData.append("location_id", locationForm.value.id);
- } else {
- formData.append("location[name]", locationForm.value.name || "");
- if (locationForm.value.city) formData.append("location[city]", locationForm.value.city);
- }
+ 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.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
+ );
+ }
- // Product
- if (productForm.value.product_id) {
- formData.append("product_id", productForm.value.product_id);
- }
-
- // Intervention
- Object.keys(interventionForm.value).forEach((key) => {
- if (interventionForm.value[key] != null) {
- let value = interventionForm.value[key];
- if (key === "scheduled_at" && value) {
- value = value.replace("T", " ");
- if (value.length === 16) {
- value += ":00";
- }
- }
- formData.append(`intervention[${key}]`, value);
- }
- });
-
- emit("submit", formData);
- } catch(e) {
- console.error(e);
- globalErrors.value.push("Erreur lors de la préparation du formulaire.");
- } finally {
- submitting.value = false;
+ if (selectedClient.value.vat_number)
+ formData.append("client[vat_number]", selectedClient.value.vat_number);
+ if (selectedClient.value.siret)
+ formData.append("client[siret]", selectedClient.value.siret);
}
+
+ // Location
+ if (locationForm.value.is_existing && locationForm.value.id) {
+ formData.append("location_id", locationForm.value.id);
+ } else {
+ formData.append("location[name]", locationForm.value.name || "");
+ if (locationForm.value.city)
+ formData.append("location[city]", locationForm.value.city);
+ }
+
+ // Product
+ if (productForm.value.product_id) {
+ formData.append("product_id", productForm.value.product_id);
+ }
+
+ // Intervention
+ Object.keys(interventionForm.value).forEach((key) => {
+ if (interventionForm.value[key] != null) {
+ let value = interventionForm.value[key];
+ if (key === "scheduled_at" && value) {
+ value = value.replace("T", " ");
+ if (value.length === 16) {
+ value += ":00";
+ }
+ }
+ formData.append(`intervention[${key}]`, value);
+ }
+ });
+
+ emit("submit", formData);
+ } catch (e) {
+ console.error(e);
+ globalErrors.value.push("Erreur lors de la préparation du formulaire.");
+ } finally {
+ submitting.value = false;
+ }
};
// Watch for changes to clear errors
-watch(() => locationForm.value.name, () => {
- if (getFieldError('location.name')) {
- errors.value = errors.value.filter(e => e.field !== 'location.name');
+watch(
+ () => locationForm.value.name,
+ () => {
+ if (getFieldError("location.name")) {
+ errors.value = errors.value.filter((e) => e.field !== "location.name");
}
-});
+ }
+);
-watch(() => interventionForm.value.assigned_practitioner_id, () => {
- if (getFieldError('assigned_practitioner_id')) {
- errors.value = errors.value.filter(e => e.field !== 'assigned_practitioner_id');
+watch(
+ () => interventionForm.value.assigned_practitioner_id,
+ () => {
+ if (getFieldError("assigned_practitioner_id")) {
+ errors.value = errors.value.filter(
+ (e) => e.field !== "assigned_practitioner_id"
+ );
}
-});
+ }
+);
defineExpose({
show,
@@ -888,15 +1170,15 @@ defineExpose({
\ No newline at end of file
+
diff --git a/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepDeceased.vue b/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepDeceased.vue
index ccab123..73150fd 100644
--- a/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepDeceased.vue
+++ b/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepDeceased.vue
@@ -3,80 +3,113 @@
Informations du Défunt
-
-
-
-
-
-
-
-
-
- {{ getFieldError("deceased_id") }}
-
-
-
-
-
-
-
-
Aucun résultat trouvé.
-
+
+
+
+
+
+
-
-
-
- Défunt sélectionné: {{ formData.first_name }} {{ formData.last_name }}
-
+
+ {{ getFieldError("deceased_id") }}
+
+
+
+
+
+
+
Aucun résultat trouvé.
+
+
+
+
+
+ Défunt sélectionné: {{ formData.first_name }}
+ {{ formData.last_name }}
+
+
@@ -230,7 +263,7 @@ const showResults = ref(false);
let searchTimeout;
const handleSearchInput = () => {
if (searchTimeout) clearTimeout(searchTimeout);
-
+
if (searchQuery.value.length < 2) {
searchResults.value = [];
showResults.value = false;
@@ -260,9 +293,9 @@ const selectDeceased = (deceased) => {
};
const clearSelection = () => {
- props.formData.id = null;
- searchQuery.value = "";
- searchResults.value = [];
+ props.formData.id = null;
+ searchQuery.value = "";
+ searchResults.value = [];
};
// Error helpers using props
diff --git a/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepLocation.vue b/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepLocation.vue
index 17c19ad..eaf60e0 100644
--- a/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepLocation.vue
+++ b/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepLocation.vue
@@ -3,82 +3,113 @@
Lieu de l'intervention
-
-
-
-
-
-
-
-
-
- {{ getFieldError("location_id") }}
-
-
-
-
-
-
-
-
Aucun résultat trouvé.
-
+
+
+
+
+
+
+
+
+ {{ getFieldError("location_id") }}
-
-
-
Lieu sélectionné: {{ formData.name }}
-
{{ formData.address }}, {{ formData.postal_code }} {{ formData.city }}
+
+
+
+
+
Aucun résultat trouvé.
+
+
+
+
+
+ Lieu sélectionné: {{ formData.name }}
+ {{ formData.address }}, {{ formData.postal_code }}
+ {{ formData.city }}
+
+
@@ -233,54 +264,53 @@ const showResults = ref(false);
// Debounce search
let searchTimeout;
const handleSearchInput = () => {
- if (searchTimeout) clearTimeout(searchTimeout);
+ if (searchTimeout) clearTimeout(searchTimeout);
- if (searchQuery.value.length < 2) {
- searchResults.value = [];
- showResults.value = false;
- return;
+ if (searchQuery.value.length < 2) {
+ searchResults.value = [];
+ showResults.value = false;
+ return;
+ }
+
+ isSearching.value = true;
+ searchTimeout = setTimeout(async () => {
+ try {
+ // Use getAllClientLocations with search param
+ const response = await ClientLocationService.getAllClientLocations({
+ search: searchQuery.value,
+ per_page: 10,
+ });
+ searchResults.value = response.data;
+ showResults.value = true;
+ } catch (e) {
+ console.error("Location search failed", e);
+ } finally {
+ isSearching.value = false;
}
-
- isSearching.value = true;
- searchTimeout = setTimeout(async () => {
- try {
- // Use getAllClientLocations with search param
- const response = await ClientLocationService.getAllClientLocations({
- search: searchQuery.value,
- per_page: 10
- });
- searchResults.value = response.data;
- showResults.value = true;
- } catch (e) {
- console.error("Location search failed", e);
- } finally {
- isSearching.value = false;
- }
- }, 300);
+ }, 300);
};
const selectLocation = (location) => {
- props.formData.id = location.id;
- props.formData.name = location.name;
- props.formData.address = location.address_line1; // Map address_line1 to address
- props.formData.city = location.city;
- props.formData.postal_code = location.postal_code;
- props.formData.country_code = location.country_code;
-
- // Construct display string
- let display = location.name || "";
- if (location.city) display += ` (${location.city})`;
- searchQuery.value = display;
- showResults.value = false;
+ props.formData.id = location.id;
+ props.formData.name = location.name;
+ props.formData.address = location.address_line1; // Map address_line1 to address
+ props.formData.city = location.city;
+ props.formData.postal_code = location.postal_code;
+ props.formData.country_code = location.country_code;
+
+ // Construct display string
+ let display = location.name || "";
+ if (location.city) display += ` (${location.city})`;
+ searchQuery.value = display;
+ showResults.value = false;
};
const clearSelection = () => {
- props.formData.id = null;
- searchQuery.value = "";
- searchResults.value = [];
+ props.formData.id = null;
+ searchQuery.value = "";
+ searchResults.value = [];
};
-
const getFieldError = (field) => {
const error = props.errors.find((err) => err.field === field);
return error ? error.message : "";
diff --git a/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepProductSelection.vue b/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepProductSelection.vue
index fb57d5d..03227df 100644
--- a/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepProductSelection.vue
+++ b/thanasoft-front/src/components/Organism/Agenda/WizardSteps/StepProductSelection.vue
@@ -6,21 +6,26 @@
-
-
-
+
+
+
-
+
-
-
-
+
+
+
+
+ Aucun résultat trouvé.
+
-
-
Soin sélectionné: {{ selectedProductDisplay }}
+
+ Soin sélectionné: {{ selectedProductDisplay }}
-
@@ -113,11 +132,11 @@ defineEmits(["next", "prev"]);
const productService = new ProductService();
const instanceService = new ProductService(); // Fix instantiation if needed or use static
-// Actually services are usually exported as objects or classes.
+// Actually services are usually exported as objects or classes.
// Looking at product.ts content: 'class ProductService' and 'export default ProductService' at end but usually instantiated?
// The file has 'class ProductService' but also looks like it might be designed to be used as new ProductService().
// Wait, looking at `product.ts` again:
-// `export default ProductService;` and it's a class.
+// `export default ProductService;` and it's a class.
// So `new ProductService()` is correct.
// Search state
@@ -129,16 +148,17 @@ const selectedProductDisplay = ref("");
const loading = ref(false);
const filterProducts = () => {
- if (!searchQuery.value) {
- searchResults.value = allProducts.value;
- return;
- }
- const query = searchQuery.value.toLowerCase();
- searchResults.value = allProducts.value.filter(p =>
- p.nom.toLowerCase().includes(query) ||
- (p.reference && p.reference.toLowerCase().includes(query)) ||
- (p.description && p.description.toLowerCase().includes(query))
- );
+ if (!searchQuery.value) {
+ searchResults.value = allProducts.value;
+ return;
+ }
+ const query = searchQuery.value.toLowerCase();
+ searchResults.value = allProducts.value.filter(
+ (p) =>
+ p.nom.toLowerCase().includes(query) ||
+ (p.reference && p.reference.toLowerCase().includes(query)) ||
+ (p.description && p.description.toLowerCase().includes(query))
+ );
};
onMounted(async () => {
@@ -147,17 +167,17 @@ onMounted(async () => {
const service = new ProductService();
// Fetch all intervention products (using existing logic or new method if needed)
// Assuming getProductsByCategory or getAllProducts with filter works.
- // The previous code used productStore.fetchProducts({ is_intervention: true }).
- // ProductService has getAllProducts but doesn't seem to explicitly have is_intervention param in interface,
- // but the store might have passed it.
+ // The previous code used productStore.fetchProducts({ is_intervention: true }).
+ // ProductService has getAllProducts but doesn't seem to explicitly have is_intervention param in interface,
+ // but the store might have passed it.
// Let's check ProductService.getAllProducts code in product.ts.
// It takes params object. We can pass 'is_intervention': true/1 if backend supports it.
// Based on previous code, it seems supported.
const response = await service.getAllProducts({
- per_page: 100, // Fetch enough
- is_intervention: true // Assuming backend handles this param as before
+ per_page: 100, // Fetch enough
+ is_intervention: true, // Assuming backend handles this param as before
});
-
+
// Check response structure. product.ts says ProductListResponse { data: Product[] ... }
allProducts.value = response.data;
searchResults.value = allProducts.value;
@@ -169,22 +189,22 @@ onMounted(async () => {
});
const handleSearchInput = () => {
- showResults.value = true;
- filterProducts();
+ showResults.value = true;
+ filterProducts();
};
const selectProduct = (product) => {
- props.formData.product_id = product.id;
- selectedProductDisplay.value = product.nom;
- searchQuery.value = product.nom;
- showResults.value = false;
+ props.formData.product_id = product.id;
+ selectedProductDisplay.value = product.nom;
+ searchQuery.value = product.nom;
+ showResults.value = false;
};
const clearSelection = () => {
- props.formData.product_id = null;
- selectedProductDisplay.value = "";
- searchQuery.value = "";
- searchResults.value = [];
+ props.formData.product_id = null;
+ selectedProductDisplay.value = "";
+ searchQuery.value = "";
+ searchResults.value = [];
};
const getFieldError = (field) => {
diff --git a/thanasoft-front/src/components/Organism/Planning/PlanningNewRequestModal.vue b/thanasoft-front/src/components/Organism/Planning/PlanningNewRequestModal.vue
new file mode 100644
index 0000000..ea3bc4c
--- /dev/null
+++ b/thanasoft-front/src/components/Organism/Planning/PlanningNewRequestModal.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
Choisissez le type à créer :
+
{{ creationTypeTitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/thanasoft-front/src/components/Organism/Planning/PlanningPresentation.vue b/thanasoft-front/src/components/Organism/Planning/PlanningPresentation.vue
index eaaa9a1..80ec0a9 100644
--- a/thanasoft-front/src/components/Organism/Planning/PlanningPresentation.vue
+++ b/thanasoft-front/src/components/Organism/Planning/PlanningPresentation.vue
@@ -1,31 +1,41 @@
-
-
-
Planning
-
{{ interventionCount }} intervention(s) (mes tâches)
-
-
+
+
+ {{ interventionCount }} intervention(s) (mes tâches)
+
-
+
-
-
-
-
-
+
+
+
+
Actualiser
-
-
-
+
+
+
Nouvelle demande
Nouveau
-
+
@@ -48,22 +58,22 @@
-
-
+
-
-
+
-
import { ref, watch, defineProps, defineEmits } from "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 PlanningLegend from "@/components/molecules/Planning/PlanningLegend.vue";
import PlanningCollaboratorsSidebar from "@/components/molecules/Planning/PlanningCollaboratorsSidebar.vue";
@@ -84,50 +93,57 @@ import PlanningWeekGrid from "@/components/molecules/Planning/PlanningWeekGrid.v
import PlanningList from "@/components/molecules/Planning/PlanningList.vue";
import PlanningKanban from "@/components/molecules/Planning/PlanningKanban.vue";
import PlanningDateNavigator from "@/components/molecules/Planning/PlanningDateNavigator.vue";
+import SoftButton from "@/components/SoftButton.vue";
const props = defineProps({
interventionCount: {
type: Number,
- default: 0
+ default: 0,
},
collaborators: {
type: Array,
- default: () => []
+ default: () => [],
},
interventions: {
type: Array,
- default: () => []
+ default: () => [],
},
currentDate: {
type: Date,
- default: () => new Date()
+ default: () => new Date(),
},
activeView: {
type: String,
- default: "grille"
- }
+ default: "grille",
+ },
});
const emit = defineEmits([
- "refresh",
- "new-request",
- "cell-click",
- "update:activeView",
- "prev-week",
+ "refresh",
+ "new-request",
+ "cell-click",
+ "update:activeView",
+ "prev-week",
"next-week",
"edit-intervention",
- "update-status"
+ "update-status",
]);
const localActiveView = ref(props.activeView);
-watch(() => props.activeView, (newVal) => {
- localActiveView.value = newVal;
-});
+watch(
+ () => props.activeView,
+ (newVal) => {
+ localActiveView.value = newVal;
+ }
+);
-watch(() => localActiveView.value, (newVal) => {
- emit("update:activeView", newVal);
-});
+watch(
+ () => localActiveView.value,
+ (newVal) => {
+ emit("update:activeView", newVal);
+ }
+);
const handleCellClick = (info) => {
emit("cell-click", info);
@@ -143,28 +159,6 @@ const handleUpdateStatus = (payload) => {
diff --git a/thanasoft-front/src/components/molecules/Planning/PlanningEventForm.vue b/thanasoft-front/src/components/molecules/Planning/PlanningEventForm.vue
new file mode 100644
index 0000000..41bfb90
--- /dev/null
+++ b/thanasoft-front/src/components/molecules/Planning/PlanningEventForm.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
diff --git a/thanasoft-front/src/components/molecules/Planning/PlanningKanban.vue b/thanasoft-front/src/components/molecules/Planning/PlanningKanban.vue
index 6c9eec0..3330e7b 100644
--- a/thanasoft-front/src/components/molecules/Planning/PlanningKanban.vue
+++ b/thanasoft-front/src/components/molecules/Planning/PlanningKanban.vue
@@ -1,6 +1,6 @@
-
@@ -92,7 +92,7 @@ const initKanban = () => {
kanbanInstance = new jKanban({
element: "#planningKanban",
gutter: "10px",
- widthBoard: "300px",
+ widthBoard: "360px",
responsivePercentage: false,
dragItems: true,
boards: boards,
@@ -153,12 +153,18 @@ watch(() => props.interventions, () => {
/* Global styles for jKanban overrides */
.kanban-container {
display: flex;
- width: 100%;
+ width: max-content;
+ min-width: 100%;
+ height: 100%;
+ overflow-x: visible !important;
+ overflow-y: hidden !important;
}
.kanban-board {
background: transparent !important;
padding: 0 !important;
+ width: 360px !important;
+ height: 100%;
}
.kanban-board-header {
@@ -183,9 +189,27 @@ watch(() => props.interventions, () => {
}
.kanban-drag {
- min-height: 500px;
+ min-height: 100%;
background-color: #f1f5f9; /* slate-100 */
border-radius: 0.75rem;
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%;
+}
diff --git a/thanasoft-front/src/components/molecules/Planning/PlanningLeaveRequestForm.vue b/thanasoft-front/src/components/molecules/Planning/PlanningLeaveRequestForm.vue
new file mode 100644
index 0000000..380d171
--- /dev/null
+++ b/thanasoft-front/src/components/molecules/Planning/PlanningLeaveRequestForm.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
diff --git a/thanasoft-front/src/components/molecules/Planning/PlanningLegend.vue b/thanasoft-front/src/components/molecules/Planning/PlanningLegend.vue
index 7026704..685b862 100644
--- a/thanasoft-front/src/components/molecules/Planning/PlanningLegend.vue
+++ b/thanasoft-front/src/components/molecules/Planning/PlanningLegend.vue
@@ -7,9 +7,16 @@
-
+
{{ item.emoji }}
-
+
{{ item.label }}
@@ -23,7 +30,7 @@ const legend = [
{ emoji: "🏥", label: "Maladie", color: "#ef4444" },
{ emoji: "📚", label: "Formation", color: "#8b5cf6" },
{ emoji: "💼", label: "Sans solde", color: "#6b7280" },
- { emoji: "🚑", label: "Accident travail", color: "#dc2626" }
+ { emoji: "🚑", label: "Accident travail", color: "#dc2626" },
];
diff --git a/thanasoft-front/src/components/molecules/Planning/PlanningList.vue b/thanasoft-front/src/components/molecules/Planning/PlanningList.vue
index c3fe3a4..d2d646d 100644
--- a/thanasoft-front/src/components/molecules/Planning/PlanningList.vue
+++ b/thanasoft-front/src/components/molecules/Planning/PlanningList.vue
@@ -5,56 +5,111 @@
- | Date & Heure |
- Type |
- Défunt / Client |
- Collaborateur |
- Statut |
- Actions |
+
+ Date & Heure
+ |
+
+ Type
+ |
+
+ Défunt / Client
+ |
+
+ Collaborateur
+ |
+
+ Statut
+ |
+
+ Actions
+ |
|
- Aucune intervention pour cette période
+ Aucune intervention pour cette période
|
-
+
|
- {{ formatDate(intervention.date) }}
- {{ formatTime(intervention.date) }}
+
+ {{ formatDate(intervention.date) }}
+
+ {{
+ formatTime(intervention.date)
+ }}
|
-
-
+
+
{{ intervention.type }}
|
- {{ intervention.deceased || 'Non spécifié' }}
- Client: {{ intervention.client || '-' }}
+
+ {{ intervention.deceased || "Non spécifié" }}
+
+ Client: {{ intervention.client || "-" }}
|
-
+
{{ getInitials(intervention.collaborator) }}
- {{ intervention.collaborator }}
+ {{
+ intervention.collaborator
+ }}
|
-
+
{{ intervention.status }}
|
-
+
|
@@ -72,45 +127,57 @@ import { defineProps, defineEmits } from "vue";
defineProps({
interventions: {
type: Array,
- default: () => []
- }
+ default: () => [],
+ },
});
defineEmits(["edit"]);
const formatDate = (dateString) => {
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) => {
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) => {
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 colors = {
- 'Soin': '#3b82f6',
- 'Transport': '#10b981',
- 'Mise en bière': '#f59e0b',
- 'Cérémonie': '#8b5cf6'
+ Soin: "#3b82f6",
+ Transport: "#10b981",
+ "Mise en bière": "#f59e0b",
+ Cérémonie: "#8b5cf6",
};
- return colors[type] || '#6b7280';
+ return colors[type] || "#6b7280";
};
const getStatusBadgeClass = (status) => {
const map = {
- 'Confirmé': 'bg-gradient-info',
- 'Terminé': 'bg-gradient-success',
- 'En attente': 'bg-gradient-warning',
- 'Annulé': 'bg-gradient-danger'
+ Confirmé: "bg-gradient-info",
+ Terminé: "bg-gradient-success",
+ "En attente": "bg-gradient-warning",
+ Annulé: "bg-gradient-danger",
};
- return map[status] || 'bg-gradient-secondary';
+ return map[status] || "bg-gradient-secondary";
};
diff --git a/thanasoft-front/src/components/molecules/Planning/PlanningViewToggles.vue b/thanasoft-front/src/components/molecules/Planning/PlanningViewToggles.vue
index 2607fb8..dd90312 100644
--- a/thanasoft-front/src/components/molecules/Planning/PlanningViewToggles.vue
+++ b/thanasoft-front/src/components/molecules/Planning/PlanningViewToggles.vue
@@ -1,28 +1,29 @@
-
{{ view.label }}
-
+