Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6710c3015e | |||
| b6dd3ad194 | |||
| b4062618f7 | |||
| 3d991f78e5 |
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
parameters:
|
parameters:
|
||||||
app.version: '0.4.33'
|
app.version: '0.4.34'
|
||||||
|
|||||||
@@ -24,7 +24,9 @@
|
|||||||
"updated": "Client mis à jour avec succès.",
|
"updated": "Client mis à jour avec succès.",
|
||||||
"deleted": "Client supprimé avec succès.",
|
"deleted": "Client supprimé avec succès.",
|
||||||
"addClient": "Ajouter un client",
|
"addClient": "Ajouter un client",
|
||||||
"editClient": "Modifier un client"
|
"editClient": "Modifier un client",
|
||||||
|
"deleteConfirmTitle": "Supprimer le client",
|
||||||
|
"deleteConfirmMessage": "Êtes-vous sûr de vouloir supprimer le client « {name} » ? Cette action est irréversible."
|
||||||
},
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
"title": "Projets",
|
"title": "Projets",
|
||||||
@@ -908,6 +910,8 @@
|
|||||||
"editProspect": "Modifier un prospect",
|
"editProspect": "Modifier un prospect",
|
||||||
"convert": "Convertir en client",
|
"convert": "Convertir en client",
|
||||||
"alreadyConverted": "Déjà converti en client",
|
"alreadyConverted": "Déjà converti en client",
|
||||||
|
"deleteConfirmTitle": "Supprimer le prospect",
|
||||||
|
"deleteConfirmMessage": "Êtes-vous sûr de vouloir supprimer le prospect « {name} » ? Cette action est irréversible.",
|
||||||
"fields": {
|
"fields": {
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"company": "Société",
|
"company": "Société",
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<Teleport v-if="modelValue" to="body">
|
||||||
|
<Transition name="modal" appear>
|
||||||
|
<div class="fixed inset-0 z-50 flex items-center justify-center">
|
||||||
|
<div class="absolute inset-0 bg-black/30" @click="cancel" />
|
||||||
|
<div class="relative z-10 w-full max-w-md rounded-lg bg-white p-6 shadow-xl">
|
||||||
|
<h3 class="text-lg font-bold text-neutral-900">{{ title }}</h3>
|
||||||
|
<p class="mt-3 text-sm text-neutral-600">
|
||||||
|
{{ message }}
|
||||||
|
</p>
|
||||||
|
<div class="mt-6 flex justify-end gap-3">
|
||||||
|
<MalioButton
|
||||||
|
variant="tertiary"
|
||||||
|
:label="$t('common.cancel')"
|
||||||
|
button-class="w-auto px-4"
|
||||||
|
@click="cancel"
|
||||||
|
/>
|
||||||
|
<MalioButton
|
||||||
|
variant="danger"
|
||||||
|
:label="$t('common.delete')"
|
||||||
|
button-class="w-auto px-4"
|
||||||
|
@click="$emit('confirm')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
modelValue: boolean
|
||||||
|
title: string
|
||||||
|
message: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value: boolean): void
|
||||||
|
(e: 'confirm'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
emit('update:modelValue', false)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal-enter-active,
|
||||||
|
.modal-leave-active {
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-enter-from,
|
||||||
|
.modal-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -31,6 +31,17 @@
|
|||||||
<template #cell-phone="{ item }">
|
<template #cell-phone="{ item }">
|
||||||
{{ (item as Client).phone ?? '—' }}
|
{{ (item as Client).phone ?? '—' }}
|
||||||
</template>
|
</template>
|
||||||
|
<template #cell-actions="{ item }">
|
||||||
|
<div class="flex justify-end" @click.stop>
|
||||||
|
<MalioButtonIcon
|
||||||
|
icon="mdi:trash-can-outline"
|
||||||
|
:aria-label="$t('common.delete')"
|
||||||
|
button-class="!bg-red-100 !text-red-700"
|
||||||
|
:icon-size="18"
|
||||||
|
@click="askDeleteClient(item as Client)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</MalioDataTable>
|
</MalioDataTable>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -75,20 +86,23 @@
|
|||||||
{{ (item as ProspectRow).phone ?? '—' }}
|
{{ (item as ProspectRow).phone ?? '—' }}
|
||||||
</template>
|
</template>
|
||||||
<template #cell-actions="{ item }">
|
<template #cell-actions="{ item }">
|
||||||
<div
|
<div class="flex justify-end gap-2" @click.stop>
|
||||||
v-if="!(item as ProspectRow).convertedClient"
|
|
||||||
class="flex justify-end"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<MalioButtonIcon
|
<MalioButtonIcon
|
||||||
|
v-if="!(item as ProspectRow).convertedClient"
|
||||||
icon="mdi:account-convert"
|
icon="mdi:account-convert"
|
||||||
:aria-label="$t('prospects.convert')"
|
:aria-label="$t('prospects.convert')"
|
||||||
button-class="!bg-green-100 !text-green-700"
|
button-class="!bg-green-100 !text-green-700"
|
||||||
:icon-size="18"
|
:icon-size="18"
|
||||||
@click="convertProspect(item as ProspectRow)"
|
@click="convertProspect(item as ProspectRow)"
|
||||||
/>
|
/>
|
||||||
|
<MalioButtonIcon
|
||||||
|
icon="mdi:trash-can-outline"
|
||||||
|
:aria-label="$t('common.delete')"
|
||||||
|
button-class="!bg-red-100 !text-red-700"
|
||||||
|
:icon-size="18"
|
||||||
|
@click="askDeleteProspect(item as ProspectRow)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span v-else class="text-neutral-300">—</span>
|
|
||||||
</template>
|
</template>
|
||||||
</MalioDataTable>
|
</MalioDataTable>
|
||||||
</div>
|
</div>
|
||||||
@@ -105,6 +119,13 @@
|
|||||||
:prospect="selectedProspect"
|
:prospect="selectedProspect"
|
||||||
@saved="onProspectSaved"
|
@saved="onProspectSaved"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ConfirmDeleteModal
|
||||||
|
v-model="deleteModalOpen"
|
||||||
|
:title="deleteModalTitle"
|
||||||
|
:message="deleteModalMessage"
|
||||||
|
@confirm="confirmDelete"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -139,6 +160,7 @@ const clientColumns = [
|
|||||||
{ key: 'name', label: t('prospects.fields.name') },
|
{ key: 'name', label: t('prospects.fields.name') },
|
||||||
{ key: 'email', label: t('prospects.fields.email') },
|
{ key: 'email', label: t('prospects.fields.email') },
|
||||||
{ key: 'phone', label: t('prospects.fields.phone') },
|
{ key: 'phone', label: t('prospects.fields.phone') },
|
||||||
|
{ key: 'actions', label: '' },
|
||||||
]
|
]
|
||||||
|
|
||||||
async function loadClients() {
|
async function loadClients() {
|
||||||
@@ -225,6 +247,54 @@ async function onProspectSaved() {
|
|||||||
await Promise.all([loadProspects(), loadClients()])
|
await Promise.all([loadProspects(), loadClients()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Suppression (clients & prospects) ---
|
||||||
|
type DeleteTarget =
|
||||||
|
| { type: 'client'; item: Client }
|
||||||
|
| { type: 'prospect'; item: Prospect }
|
||||||
|
|
||||||
|
const deleteModalOpen = ref(false)
|
||||||
|
const deleteTarget = ref<DeleteTarget | null>(null)
|
||||||
|
|
||||||
|
const deleteModalTitle = computed(() =>
|
||||||
|
deleteTarget.value?.type === 'prospect'
|
||||||
|
? t('prospects.deleteConfirmTitle')
|
||||||
|
: t('clients.deleteConfirmTitle'),
|
||||||
|
)
|
||||||
|
|
||||||
|
const deleteModalMessage = computed(() => {
|
||||||
|
if (!deleteTarget.value) return ''
|
||||||
|
const name = deleteTarget.value.item.name
|
||||||
|
return deleteTarget.value.type === 'prospect'
|
||||||
|
? t('prospects.deleteConfirmMessage', { name })
|
||||||
|
: t('clients.deleteConfirmMessage', { name })
|
||||||
|
})
|
||||||
|
|
||||||
|
function askDeleteClient(item: Client) {
|
||||||
|
deleteTarget.value = { type: 'client', item }
|
||||||
|
deleteModalOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function askDeleteProspect(item: Prospect) {
|
||||||
|
deleteTarget.value = { type: 'prospect', item }
|
||||||
|
deleteModalOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmDelete() {
|
||||||
|
const target = deleteTarget.value
|
||||||
|
if (!target) return
|
||||||
|
|
||||||
|
if (target.type === 'client') {
|
||||||
|
await clientService.remove(target.item.id)
|
||||||
|
await loadClients()
|
||||||
|
} else {
|
||||||
|
await prospectService.remove(target.item.id)
|
||||||
|
await loadProspects()
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteModalOpen.value = false
|
||||||
|
deleteTarget.value = null
|
||||||
|
}
|
||||||
|
|
||||||
watch(statusFilter, loadProspects)
|
watch(statusFilter, loadProspects)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user