283 lines
11 KiB
Vue
283 lines
11 KiB
Vue
<script setup>
|
|
import { onMounted, ref } from 'vue';
|
|
import { useSearcher, apiURL } from '@Services/Api';
|
|
import { can } from './Module.js';
|
|
|
|
import SearcherHead from '@Holos/Searcher.vue';
|
|
import ExcelModal from '@Components/POS/ExcelClient.vue';
|
|
import Table from '@Holos/Table.vue';
|
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
|
import CreateModal from './Create.vue';
|
|
import EditModal from './Edit.vue';
|
|
import DeleteModal from './Delete.vue';
|
|
import StatsModal from './Stats.vue';
|
|
|
|
|
|
/** Estado */
|
|
const clients = ref([]);
|
|
const showCreateModal = ref(false);
|
|
const showEditModal = ref(false);
|
|
const showDeleteModal = ref(false);
|
|
const showStatsModal = ref(false);
|
|
const showExcelModal = ref(false);
|
|
const editingClient = ref(null);
|
|
const deletingClient = ref(null);
|
|
const statsClient = ref(null);
|
|
|
|
/** Métodos */
|
|
const searcher = useSearcher({
|
|
url: apiURL('clients'),
|
|
onSuccess: (r) => {
|
|
clients.value = r.clients;
|
|
},
|
|
onError: () => clients.value = []
|
|
});
|
|
|
|
/** Métodos auxiliares */
|
|
const copyClientInfo = (client) => {
|
|
const info = `
|
|
Nombre: ${client.name}
|
|
Email: ${client.email || 'N/A'}
|
|
Teléfono: ${client.phone || 'N/A'}
|
|
Dirección: ${client.address || 'N/A'}
|
|
RFC: ${client.rfc || 'N/A'}
|
|
`.trim();
|
|
|
|
navigator.clipboard.writeText(info).then(() => {
|
|
window.Notify.success(`Información de ${client.name} copiada al portapapeles`);
|
|
}).catch(() => {
|
|
window.Notify.error('No se pudo copiar la información');
|
|
});
|
|
};
|
|
|
|
const confirmDelete = async (id) => {
|
|
try {
|
|
const response = await fetch(apiURL(`clients/${id}`), {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Authorization': `Bearer ${sessionStorage.token}`,
|
|
'Accept': 'application/json'
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
window.Notify.success('Cliente eliminado exitosamente');
|
|
closeDeleteModal();
|
|
searcher.search();
|
|
} else {
|
|
window.Notify.error('Error al eliminar el cliente');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
window.Notify.error('Error al eliminar el cliente');
|
|
}
|
|
};
|
|
|
|
const openCreateModal = () => {
|
|
showCreateModal.value = true;
|
|
};
|
|
|
|
const closeCreateModal = () => {
|
|
showCreateModal.value = false;
|
|
};
|
|
|
|
const openEditModal = (client) => {
|
|
editingClient.value = client;
|
|
showEditModal.value = true;
|
|
};
|
|
|
|
const closeEditModal = () => {
|
|
showEditModal.value = false;
|
|
editingClient.value = null;
|
|
};
|
|
|
|
const openDeleteModal = (client) => {
|
|
deletingClient.value = client;
|
|
showDeleteModal.value = true;
|
|
};
|
|
|
|
const closeDeleteModal = () => {
|
|
showDeleteModal.value = false;
|
|
deletingClient.value = null;
|
|
};
|
|
|
|
const openStatsModal = (client) => {
|
|
statsClient.value = client;
|
|
showStatsModal.value = true;
|
|
};
|
|
|
|
const closeStatsModal = () => {
|
|
showStatsModal.value = false;
|
|
statsClient.value = null;
|
|
};
|
|
const openExcelModal = () => {
|
|
showExcelModal.value = true;
|
|
};
|
|
|
|
const closeExcelModal = () => {
|
|
showExcelModal.value = false;
|
|
};
|
|
|
|
const onClientSaved = () => {
|
|
searcher.search();
|
|
};
|
|
|
|
/** Ciclo de vida */
|
|
onMounted(() => {
|
|
searcher.search();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<SearcherHead
|
|
:title="$t('clients.title')"
|
|
placeholder="Buscar por nombre..."
|
|
@search="(x) => searcher.search(x)"
|
|
>
|
|
<button
|
|
v-if="can('create')"
|
|
class="flex items-center gap-2 px-3 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-semibold rounded-lg transition-colors shadow-sm"
|
|
@click="openCreateModal"
|
|
>
|
|
<GoogleIcon name="add" class="text-xl" />
|
|
Nuevo Cliente
|
|
</button>
|
|
<button
|
|
class="flex items-center gap-2 px-3 py-2 bg-green-600 hover:bg-green-700 text-white text-sm font-semibold rounded-lg transition-colors shadow-sm"
|
|
@click="openExcelModal"
|
|
>
|
|
<GoogleIcon name="add" class="text-xl" />
|
|
Generar excel
|
|
</button>
|
|
</SearcherHead>
|
|
<div class="pt-2 w-full">
|
|
|
|
<Table
|
|
:items="clients"
|
|
@send-pagination="(page) => searcher.pagination(page)"
|
|
>
|
|
<template #head>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">NOMBRE</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">CÓDIGO DE CLIENTE</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">CORREO</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">TELEFONO</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">DIRECCIÓN</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">RFC</th>
|
|
<th class="px-6 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">ACCIONES</th>
|
|
</template>
|
|
<template #body="{items}">
|
|
<tr
|
|
v-for="client in items"
|
|
:key="client.id"
|
|
class="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
|
|
>
|
|
<td class="px-6 py-4 text-center">
|
|
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ client.name }}</p>
|
|
</td>
|
|
<td class="px-6 py-4 text-center">
|
|
<p class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ client.client_number }}</p>
|
|
</td>
|
|
<td class="px-6 py-4 text-center">
|
|
<p class="text-sm text-gray-700 dark:text-gray-300">{{ client.email }}</p>
|
|
</td>
|
|
<td class="px-6 py-4 text-center">
|
|
<p class="text-sm text-gray-700 dark:text-gray-300">{{ client.phone }}</p>
|
|
</td>
|
|
<td class="px-6 py-4 text-center">
|
|
<p class="text-sm text-gray-700 dark:text-gray-300">{{ client.address }}</p>
|
|
</td>
|
|
<td class="px-6 py-4 text-center">
|
|
<p class="text-sm text-gray-700 dark:text-gray-300">{{ client.rfc }}</p>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
|
<div class="flex items-center justify-center gap-2">
|
|
<button
|
|
@click.stop="copyClientInfo(client)"
|
|
class="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 transition-colors"
|
|
title="Copiar información"
|
|
>
|
|
<GoogleIcon name="content_copy" class="text-xl" />
|
|
</button>
|
|
<button
|
|
@click.stop="openStatsModal(client)"
|
|
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300 transition-colors"
|
|
title="Estadísticas del cliente"
|
|
>
|
|
<GoogleIcon name="bar_chart" class="text-xl" />
|
|
</button>
|
|
<button
|
|
v-if="can('edit')"
|
|
@click.stop="openEditModal(client)"
|
|
class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-300 transition-colors"
|
|
title="Editar cliente"
|
|
>
|
|
<GoogleIcon name="edit" class="text-xl" />
|
|
</button>
|
|
<button
|
|
v-if="can('destroy')"
|
|
@click.stop="openDeleteModal(client)"
|
|
class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 transition-colors"
|
|
title="Eliminar cliente"
|
|
>
|
|
<GoogleIcon name="delete" class="text-xl" />
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
<template #empty>
|
|
<td colspan="7" class="table-cell text-center">
|
|
<div class="flex flex-col items-center justify-center py-8 text-gray-500">
|
|
<GoogleIcon
|
|
name="person"
|
|
class="text-6xl mb-2 opacity-50"
|
|
/>
|
|
<p class="font-semibold">
|
|
{{ $t('registers.empty') }}
|
|
</p>
|
|
</div>
|
|
</td>
|
|
</template>
|
|
</Table>
|
|
</div>
|
|
<!-- Modal de Crear Cliente -->
|
|
<CreateModal
|
|
v-if="can('create')"
|
|
:show="showCreateModal"
|
|
@close="closeCreateModal"
|
|
@created="onClientSaved"
|
|
/>
|
|
|
|
<!-- Modal de Editar Cliente -->
|
|
<EditModal
|
|
v-if="can('edit')"
|
|
:show="showEditModal"
|
|
:client="editingClient"
|
|
@close="closeEditModal"
|
|
@updated="onClientSaved"
|
|
/>
|
|
|
|
<!-- Modal de Eliminar Cliente -->
|
|
<DeleteModal
|
|
v-if="can('destroy')"
|
|
:show="showDeleteModal"
|
|
:client="deletingClient"
|
|
@close="closeDeleteModal"
|
|
@confirm="confirmDelete"
|
|
/>
|
|
|
|
<!-- Modal de Estadísticas del Cliente -->
|
|
<StatsModal
|
|
:show="showStatsModal"
|
|
:client="statsClient"
|
|
@close="closeStatsModal"
|
|
/>
|
|
|
|
<!-- Modal de Excel de Clientes -->
|
|
<ExcelModal
|
|
:show="showExcelModal"
|
|
@close="closeExcelModal"
|
|
/>
|
|
</div>
|
|
</template> |