294 lines
9.8 KiB
Vue
294 lines
9.8 KiB
Vue
<template>
|
|
<div class="container-fluid py-4">
|
|
<div class="row justify-content-center">
|
|
<div class="col-12 col-xl-8">
|
|
<div class="card">
|
|
<div class="card-header pb-0">
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<div>
|
|
<h5 class="mb-1">Ajouter une localisation</h5>
|
|
<p class="text-sm text-secondary mb-0">
|
|
Le client est obligatoire et au moins un champ d'adresse doit
|
|
etre renseigne.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div v-if="generalError" class="alert alert-danger" role="alert">
|
|
{{ generalError }}
|
|
</div>
|
|
|
|
<form @submit.prevent="handleSubmit">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Client *</label>
|
|
<select
|
|
v-model="form.client_id"
|
|
class="form-select"
|
|
:class="{ 'is-invalid': fieldErrors.client_id }"
|
|
required
|
|
>
|
|
<option value="">Selectionner un client</option>
|
|
<option
|
|
v-for="client in clients"
|
|
:key="client.id"
|
|
:value="String(client.id)"
|
|
>
|
|
{{
|
|
client.company_name ||
|
|
client.name ||
|
|
`Client #${client.id}`
|
|
}}
|
|
</option>
|
|
</select>
|
|
<div v-if="fieldErrors.client_id" class="invalid-feedback">
|
|
{{ fieldErrors.client_id }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Nom</label>
|
|
<input
|
|
v-model="form.name"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': fieldErrors.name }"
|
|
maxlength="191"
|
|
placeholder="Ex: Depot principal"
|
|
/>
|
|
<div v-if="fieldErrors.name" class="invalid-feedback">
|
|
{{ fieldErrors.name }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Adresse ligne 1</label>
|
|
<input
|
|
v-model="form.address_line1"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': fieldErrors.address_line1 }"
|
|
maxlength="255"
|
|
/>
|
|
<div v-if="fieldErrors.address_line1" class="invalid-feedback">
|
|
{{ fieldErrors.address_line1 }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Adresse ligne 2</label>
|
|
<input
|
|
v-model="form.address_line2"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': fieldErrors.address_line2 }"
|
|
maxlength="255"
|
|
/>
|
|
<div v-if="fieldErrors.address_line2" class="invalid-feedback">
|
|
{{ fieldErrors.address_line2 }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Code postal</label>
|
|
<input
|
|
v-model="form.postal_code"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': fieldErrors.postal_code }"
|
|
maxlength="20"
|
|
/>
|
|
<div v-if="fieldErrors.postal_code" class="invalid-feedback">
|
|
{{ fieldErrors.postal_code }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Ville</label>
|
|
<input
|
|
v-model="form.city"
|
|
type="text"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': fieldErrors.city }"
|
|
maxlength="191"
|
|
/>
|
|
<div v-if="fieldErrors.city" class="invalid-feedback">
|
|
{{ fieldErrors.city }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Code pays</label>
|
|
<input
|
|
v-model="form.country_code"
|
|
type="text"
|
|
class="form-control text-uppercase"
|
|
:class="{ 'is-invalid': fieldErrors.country_code }"
|
|
maxlength="2"
|
|
/>
|
|
<div v-if="fieldErrors.country_code" class="invalid-feedback">
|
|
{{ fieldErrors.country_code }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Latitude GPS</label>
|
|
<input
|
|
v-model="form.gps_lat"
|
|
type="number"
|
|
step="0.000001"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': fieldErrors.gps_lat }"
|
|
/>
|
|
<div v-if="fieldErrors.gps_lat" class="invalid-feedback">
|
|
{{ fieldErrors.gps_lat }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Longitude GPS</label>
|
|
<input
|
|
v-model="form.gps_lng"
|
|
type="number"
|
|
step="0.000001"
|
|
class="form-control"
|
|
:class="{ 'is-invalid': fieldErrors.gps_lng }"
|
|
/>
|
|
<div v-if="fieldErrors.gps_lng" class="invalid-feedback">
|
|
{{ fieldErrors.gps_lng }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-check form-switch mb-4">
|
|
<input
|
|
id="isDefault"
|
|
v-model="form.is_default"
|
|
class="form-check-input"
|
|
type="checkbox"
|
|
/>
|
|
<label class="form-check-label" for="isDefault">
|
|
Definir comme localisation par defaut
|
|
</label>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-end gap-2">
|
|
<button
|
|
type="button"
|
|
class="btn btn-outline-secondary mb-0"
|
|
:disabled="clientLocationStore.isLoading"
|
|
@click="router.push({ name: 'Localisation clients' })"
|
|
>
|
|
Annuler
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
class="btn bg-gradient-primary mb-0"
|
|
:disabled="clientLocationStore.isLoading"
|
|
>
|
|
{{
|
|
clientLocationStore.isLoading ? "Creation..." : "Ajouter"
|
|
}}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, onMounted, reactive, ref } from "vue";
|
|
import { useRouter } from "vue-router";
|
|
import { useClientLocationStore } from "@/stores/clientLocation";
|
|
import { useClientStore } from "@/stores/clientStore";
|
|
import { useNotificationStore } from "@/stores/notification";
|
|
|
|
const router = useRouter();
|
|
const clientLocationStore = useClientLocationStore();
|
|
const clientStore = useClientStore();
|
|
const notificationStore = useNotificationStore();
|
|
|
|
const fieldErrors = ref({});
|
|
|
|
const form = reactive({
|
|
client_id: "",
|
|
name: "",
|
|
address_line1: "",
|
|
address_line2: "",
|
|
postal_code: "",
|
|
city: "",
|
|
country_code: "FR",
|
|
gps_lat: "",
|
|
gps_lng: "",
|
|
is_default: false,
|
|
});
|
|
|
|
const clients = computed(() => clientStore.clients || []);
|
|
|
|
const generalError = computed(() => {
|
|
const generalFieldError = fieldErrors.value.general;
|
|
|
|
if (Array.isArray(generalFieldError)) {
|
|
return generalFieldError[0];
|
|
}
|
|
|
|
return generalFieldError || clientLocationStore.error;
|
|
});
|
|
|
|
onMounted(async () => {
|
|
if (!clients.value.length) {
|
|
await clientStore.fetchClients({ page: 1, per_page: 100 });
|
|
}
|
|
});
|
|
|
|
const normalizePayload = () => ({
|
|
client_id: Number(form.client_id),
|
|
name: form.name || null,
|
|
address_line1: form.address_line1 || null,
|
|
address_line2: form.address_line2 || null,
|
|
postal_code: form.postal_code || null,
|
|
city: form.city || null,
|
|
country_code: (form.country_code || "FR").toUpperCase(),
|
|
gps_lat: form.gps_lat === "" ? null : Number(form.gps_lat),
|
|
gps_lng: form.gps_lng === "" ? null : Number(form.gps_lng),
|
|
is_default: form.is_default,
|
|
});
|
|
|
|
const handleSubmit = async () => {
|
|
fieldErrors.value = {};
|
|
clientLocationStore.clearError();
|
|
|
|
try {
|
|
await clientLocationStore.createClientLocation(normalizePayload());
|
|
notificationStore.created("Localisation");
|
|
|
|
setTimeout(() => {
|
|
router.push({ name: "Localisation clients" });
|
|
}, 1500);
|
|
} catch (error) {
|
|
if (error.response?.status === 422) {
|
|
fieldErrors.value = error.response.data.errors || {};
|
|
notificationStore.error(
|
|
"Erreur de validation",
|
|
"Veuillez corriger les erreurs dans le formulaire"
|
|
);
|
|
return;
|
|
}
|
|
|
|
const errorMessage =
|
|
error.response?.data?.message || "Impossible de creer la localisation";
|
|
|
|
notificationStore.error("Erreur", errorMessage);
|
|
}
|
|
};
|
|
</script>
|