fix modal
This commit is contained in:
parent
99d88ca30b
commit
2a1de6f384
@ -168,11 +168,11 @@ const getInitials = (name) => {
|
|||||||
|
|
||||||
const formatAddress = (client) => {
|
const formatAddress = (client) => {
|
||||||
const parts = [
|
const parts = [
|
||||||
client.billing_address_line1,
|
client.billing_address.line1,
|
||||||
client.billing_address_line2,
|
client.billing_address.line2,
|
||||||
client.billing_postal_code,
|
client.billing_address.postal_code,
|
||||||
client.billing_city,
|
client.billing_address.city,
|
||||||
client.billing_country_code,
|
client.billing_address.country_code,
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
return parts.length > 0 ? parts.join(", ") : "Aucune adresse renseignée";
|
return parts.length > 0 ? parts.join(", ") : "Aucune adresse renseignée";
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<table-action />
|
<table-action />
|
||||||
</template>
|
</template>
|
||||||
<template #contact-table>
|
<template #contact-table>
|
||||||
<contact-table :data="contacts" />
|
<contact-table :data="contacts" :loading="loading" @delete="handleDelete" />
|
||||||
</template>
|
</template>
|
||||||
</contact-template>s
|
</contact-template>s
|
||||||
</template>
|
</template>
|
||||||
@ -20,16 +20,21 @@ import ContactTable from "@/components/molecules/Tables/ContactTable.vue";
|
|||||||
import addButton from "@/components/molecules/new-button/addButton.vue";
|
import addButton from "@/components/molecules/new-button/addButton.vue";
|
||||||
import FilterTable from "@/components/molecules/Tables/FilterTable.vue";
|
import FilterTable from "@/components/molecules/Tables/FilterTable.vue";
|
||||||
import TableAction from "@/components/molecules/Tables/TableAction.vue";
|
import TableAction from "@/components/molecules/Tables/TableAction.vue";
|
||||||
import { defineProps } from "vue";
|
import { defineProps, defineEmits } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const emit = defineEmits(["delete"]);
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
contacts: {
|
contacts: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const goToCreateContact = () => {
|
const goToCreateContact = () => {
|
||||||
@ -37,4 +42,8 @@ const goToCreateContact = () => {
|
|||||||
name: "Add Contact",
|
name: "Add Contact",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDelete = (contactId) => {
|
||||||
|
emit("delete", contactId);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
>
|
>
|
||||||
<i :class="icon" class="me-2"></i>
|
<i :class="icon" class="me-2"></i>
|
||||||
<span class="text-sm">{{ label }}</span>
|
<span class="text-sm" style="margin-right: 10px">{{ label }}</span>
|
||||||
<span v-if="badge" class="badge badge-sm bg-gradient-success ms-auto">
|
<span v-if="badge" class="badge badge-sm bg-gradient-success ms-auto">
|
||||||
{{ badge }}
|
{{ badge }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -65,12 +65,16 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p class="text-xs font-weight-bold mb-0">
|
<p class="text-xs font-weight-bold mb-0">
|
||||||
{{ location.gps_lat ? Number(location.gps_lat).toFixed(6) : "-" }}
|
{{
|
||||||
|
location.gps_lat ? Number(location.gps_lat).toFixed(6) : "-"
|
||||||
|
}}
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p class="text-xs font-weight-bold mb-0">
|
<p class="text-xs font-weight-bold mb-0">
|
||||||
{{ location.gps_lng ? Number(location.gps_lng).toFixed(6) : "-" }}
|
{{
|
||||||
|
location.gps_lng ? Number(location.gps_lng).toFixed(6) : "-"
|
||||||
|
}}
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
@ -114,10 +118,10 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="text-center py-5">
|
<div v-else class="text-center py-5">
|
||||||
<i class="fas fa-map-marked-alt fa-3x text-secondary opacity-5 mb-3"></i>
|
<i
|
||||||
<p class="text-sm text-secondary">
|
class="fas fa-map-marked-alt fa-3x text-secondary opacity-5 mb-3"
|
||||||
Aucune localisation pour ce client
|
></i>
|
||||||
</p>
|
<p class="text-sm text-secondary">Aucune localisation pour ce client</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -167,10 +171,10 @@ const handleRemoveLocation = (locationId) => {
|
|||||||
|
|
||||||
const formatAddress = (location) => {
|
const formatAddress = (location) => {
|
||||||
const parts = [
|
const parts = [
|
||||||
location.address_line1,
|
location.address.line1,
|
||||||
location.address_line2,
|
location.address.line2,
|
||||||
location.postal_code,
|
location.address.postal_code,
|
||||||
location.city,
|
location.address.city,
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
return parts.length > 0 ? parts.join(", ") : "-";
|
return parts.length > 0 ? parts.join(", ") : "-";
|
||||||
|
|||||||
@ -5,8 +5,13 @@
|
|||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
style="background-color: rgba(0, 0, 0, 0.5)"
|
style="background-color: rgba(0, 0, 0, 0.5)"
|
||||||
|
@click="handleBackdropClick"
|
||||||
>
|
>
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
<div
|
||||||
|
class="modal-dialog modal-dialog-centered modal-lg"
|
||||||
|
role="document"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Ajouter une localisation</h5>
|
<h5 class="modal-title">Ajouter une localisation</h5>
|
||||||
@ -15,20 +20,34 @@
|
|||||||
class="btn-close"
|
class="btn-close"
|
||||||
@click="closeModal"
|
@click="closeModal"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
|
:disabled="locationIsLoading"
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<!-- Error Alert -->
|
||||||
|
<div v-if="generalError" class="alert alert-danger" role="alert">
|
||||||
|
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||||
|
{{ generalError }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<form @submit.prevent="handleSubmit">
|
<form @submit.prevent="handleSubmit">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label">Nom de la localisation*</label>
|
<label class="form-label">Nom de la localisation</label>
|
||||||
<input
|
<input
|
||||||
v-model="formData.name"
|
v-model="formData.name"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.name }"
|
||||||
placeholder="Ex: Siège social"
|
placeholder="Ex: Siège social"
|
||||||
required
|
maxlength="191"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.name" class="invalid-feedback">
|
||||||
|
{{ errors.name }}
|
||||||
|
</div>
|
||||||
|
<div class="form-text text-muted">
|
||||||
|
Optionnel - maximum 191 caractères
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label">Type de localisation</label>
|
<label class="form-label">Type de localisation</label>
|
||||||
@ -42,14 +61,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Adresse ligne 1*</label>
|
<label class="form-label">Adresse ligne 1</label>
|
||||||
<input
|
<input
|
||||||
v-model="formData.address_line1"
|
v-model="formData.address_line1"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.address_line1 }"
|
||||||
placeholder="Numéro et nom de rue"
|
placeholder="Numéro et nom de rue"
|
||||||
required
|
maxlength="255"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.address_line1" class="invalid-feedback">
|
||||||
|
{{ errors.address_line1 }}
|
||||||
|
</div>
|
||||||
|
<div class="form-text text-muted">
|
||||||
|
Optionnel - maximum 255 caractères
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -58,43 +84,77 @@
|
|||||||
v-model="formData.address_line2"
|
v-model="formData.address_line2"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.address_line2 }"
|
||||||
placeholder="Complément d'adresse"
|
placeholder="Complément d'adresse"
|
||||||
|
maxlength="255"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.address_line2" class="invalid-feedback">
|
||||||
|
{{ errors.address_line2 }}
|
||||||
|
</div>
|
||||||
|
<div class="form-text text-muted">
|
||||||
|
Optionnel - maximum 255 caractères
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<label class="form-label">Code postal*</label>
|
<label class="form-label">Code postal</label>
|
||||||
<input
|
<input
|
||||||
v-model="formData.postal_code"
|
v-model="formData.postal_code"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.postal_code }"
|
||||||
placeholder="Ex: 75001"
|
placeholder="Ex: 75001"
|
||||||
required
|
maxlength="20"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.postal_code" class="invalid-feedback">
|
||||||
|
{{ errors.postal_code }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<label class="form-label">Ville*</label>
|
<label class="form-label">Ville</label>
|
||||||
<input
|
<input
|
||||||
v-model="formData.city"
|
v-model="formData.city"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.city }"
|
||||||
placeholder="Ex: Paris"
|
placeholder="Ex: Paris"
|
||||||
required
|
maxlength="191"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.city" class="invalid-feedback">
|
||||||
|
{{ errors.city }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 mb-3">
|
<div class="col-md-4 mb-3">
|
||||||
<label class="form-label">Pays*</label>
|
<label class="form-label">Pays</label>
|
||||||
<input
|
<input
|
||||||
v-model="formData.country_code"
|
v-model="formData.country_code"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.country_code }"
|
||||||
placeholder="Ex: FR"
|
placeholder="Ex: FR"
|
||||||
required
|
maxlength="2"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.country_code" class="invalid-feedback">
|
||||||
|
{{ errors.country_code }}
|
||||||
|
</div>
|
||||||
|
<div class="form-text text-muted">
|
||||||
|
2 caractères (ex: FR, BE, DE)
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Address Validation Alert -->
|
||||||
|
<div
|
||||||
|
v-if="showAddressWarning"
|
||||||
|
class="alert alert-warning"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
|
Au moins un champ d'adresse (adresse, code postal ou ville) doit
|
||||||
|
être renseigné.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label">Téléphone</label>
|
<label class="form-label">Téléphone</label>
|
||||||
@ -126,8 +186,13 @@
|
|||||||
min="-90"
|
min="-90"
|
||||||
max="90"
|
max="90"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.gps_lat }"
|
||||||
placeholder="Ex: 48.856614"
|
placeholder="Ex: 48.856614"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.gps_lat" class="invalid-feedback">
|
||||||
|
{{ errors.gps_lat }}
|
||||||
|
</div>
|
||||||
|
<div class="form-text text-muted">Entre -90 et 90</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label">GPS Longitude</label>
|
<label class="form-label">GPS Longitude</label>
|
||||||
@ -138,8 +203,13 @@
|
|||||||
min="-180"
|
min="-180"
|
||||||
max="180"
|
max="180"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:class="{ 'is-invalid': errors.gps_lng }"
|
||||||
placeholder="Ex: 2.352222"
|
placeholder="Ex: 2.352222"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="errors.gps_lng" class="invalid-feedback">
|
||||||
|
{{ errors.gps_lng }}
|
||||||
|
</div>
|
||||||
|
<div class="form-text text-muted">Entre -180 et 180</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -149,10 +219,14 @@
|
|||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="isDefaultCheckbox"
|
id="isDefaultCheckbox"
|
||||||
|
:class="{ 'is-invalid': errors.is_default }"
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" for="isDefaultCheckbox">
|
<label class="form-check-label" for="isDefaultCheckbox">
|
||||||
Définir comme localisation par défaut
|
Définir comme localisation par défaut
|
||||||
</label>
|
</label>
|
||||||
|
<div v-if="errors.is_default" class="invalid-feedback d-block">
|
||||||
|
{{ errors.is_default }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -169,13 +243,13 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@click="handleSubmit"
|
@click="handleSubmit"
|
||||||
:disabled="locationIsLoading"
|
:disabled="locationIsLoading || !isFormValid"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="locationIsLoading"
|
v-if="locationIsLoading"
|
||||||
class="spinner-border spinner-border-sm me-2"
|
class="spinner-border spinner-border-sm me-2"
|
||||||
></span>
|
></span>
|
||||||
Ajouter
|
{{ locationIsLoading ? "Création..." : "Ajouter" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -184,8 +258,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch, computed, nextTick } from "vue";
|
||||||
import { defineProps, defineEmits } from "vue";
|
import { defineProps, defineEmits } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isVisible: {
|
isVisible: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -199,9 +274,13 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
errors: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["close", "location-created"]);
|
const emit = defineEmits(["close", "location-created", "clear-errors"]);
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
name: "",
|
name: "",
|
||||||
@ -219,6 +298,25 @@ const formData = ref({
|
|||||||
client_id: props.clientId,
|
client_id: props.clientId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Computed properties
|
||||||
|
const showAddressWarning = computed(() => {
|
||||||
|
return (
|
||||||
|
!formData.value.address_line1 &&
|
||||||
|
!formData.value.postal_code &&
|
||||||
|
!formData.value.city
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const generalError = computed(() => {
|
||||||
|
return props.errors.general || props.errors.client_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isFormValid = computed(() => {
|
||||||
|
// At least one address field should be filled
|
||||||
|
return !showAddressWarning.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watchers
|
||||||
watch(
|
watch(
|
||||||
() => props.clientId,
|
() => props.clientId,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
@ -226,6 +324,17 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.isVisible,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
// Clear errors when modal opens
|
||||||
|
emit("clear-errors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Methods
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
name: "",
|
name: "",
|
||||||
@ -242,6 +351,7 @@ const resetForm = () => {
|
|||||||
is_default: false,
|
is_default: false,
|
||||||
client_id: props.clientId,
|
client_id: props.clientId,
|
||||||
};
|
};
|
||||||
|
emit("clear-errors");
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
@ -249,9 +359,67 @@ const closeModal = () => {
|
|||||||
emit("close");
|
emit("close");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleBackdropClick = (event) => {
|
||||||
emit("location-created", formData.value);
|
if (event.target === event.currentTarget) {
|
||||||
resetForm();
|
closeModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!isFormValid.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate GPS coordinates if provided
|
||||||
|
if (formData.value.gps_lat !== null) {
|
||||||
|
const lat = parseFloat(formData.value.gps_lat);
|
||||||
|
if (isNaN(lat) || lat < -90 || lat > 90) {
|
||||||
|
emit("location-created", {
|
||||||
|
...formData.value,
|
||||||
|
errors: { gps_lat: "La latitude doit être comprise entre -90 et 90." },
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.value.gps_lng !== null) {
|
||||||
|
const lng = parseFloat(formData.value.gps_lng);
|
||||||
|
if (isNaN(lng) || lng < -180 || lng > 180) {
|
||||||
|
emit("location-created", {
|
||||||
|
...formData.value,
|
||||||
|
errors: {
|
||||||
|
gps_lng: "La longitude doit être comprise entre -180 et 180.",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate country code if provided
|
||||||
|
if (formData.value.country_code && formData.value.country_code.length !== 2) {
|
||||||
|
emit("location-created", {
|
||||||
|
...formData.value,
|
||||||
|
errors: {
|
||||||
|
country_code: "Le code pays doit contenir exactement 2 caractères.",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up data before sending
|
||||||
|
const submitData = {
|
||||||
|
...formData.value,
|
||||||
|
gps_lat: formData.value.gps_lat || null,
|
||||||
|
gps_lng: formData.value.gps_lng || null,
|
||||||
|
name: formData.value.name || null,
|
||||||
|
address_line1: formData.value.address_line1 || null,
|
||||||
|
address_line2: formData.value.address_line2 || null,
|
||||||
|
postal_code: formData.value.postal_code || null,
|
||||||
|
city: formData.value.city || null,
|
||||||
|
country_code: formData.value.country_code || "FR",
|
||||||
|
};
|
||||||
|
|
||||||
|
emit("location-created", submitData);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -270,4 +438,37 @@ const handleSubmit = () => {
|
|||||||
.form-select {
|
.form-select {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invalid-feedback {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-text {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth transitions for modal */
|
||||||
|
.modal-content {
|
||||||
|
animation: modalSlideIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modalSlideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-50px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
class="navbar-brand font-weight-bolder ms-lg-0 ms-3"
|
class="navbar-brand font-weight-bolder ms-lg-0 ms-3"
|
||||||
:class="darkMode ? 'text-black' : 'text-white'"
|
:class="darkMode ? 'text-black' : 'text-white'"
|
||||||
to="/"
|
to="/"
|
||||||
>Soft UI Dashboard PRO</router-link
|
>Thanasoft</router-link
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="shadow-none navbar-toggler ms-2"
|
class="shadow-none navbar-toggler ms-2"
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
></i>
|
></i>
|
||||||
<router-link class="m-0 navbar-brand" to="/">
|
<router-link class="m-0 navbar-brand" to="/">
|
||||||
<img :src="logo" class="navbar-brand-img h-100" alt="main_logo" />
|
<img :src="logo" class="navbar-brand-img h-100" alt="main_logo" />
|
||||||
<span class="ms-1 font-weight-bold">Soft UI Dashboard PRO</span>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mt-0 horizontal dark" />
|
<hr class="mt-0 horizontal dark" />
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<contact-presentation :contacts="contactStore.contacts" />
|
<contact-presentation
|
||||||
|
:contacts="contactStore.contacts"
|
||||||
|
@delete="handleDelete"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import ContactPresentation from "@/components/Organism/CRM/ContactPresentation.vue";
|
import ContactPresentation from "@/components/Organism/CRM/ContactPresentation.vue";
|
||||||
@ -11,4 +14,13 @@ const contactStore = useContactStore();
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
contactStore.fetchContacts();
|
contactStore.fetchContacts();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleDelete = async (contactId) => {
|
||||||
|
try {
|
||||||
|
await contactStore.deleteContact(Number(contactId));
|
||||||
|
await contactStore.fetchContacts();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to delete contact:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user