feat: Implement store management with CRUD operations, including dialog for creating and editing stores
This commit is contained in:
parent
c6eaa2ef75
commit
1465f065b1
157
src/modules/stores/components/StoreForm.vue
Normal file
157
src/modules/stores/components/StoreForm.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Textarea from 'primevue/textarea';
|
||||
import Button from 'primevue/button';
|
||||
import InputSwitch from 'primevue/inputswitch';
|
||||
import type { Store, CreateStoreData } from '../types/store';
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
store?: Store | null;
|
||||
isEditing?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
store: null,
|
||||
isEditing: false
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
save: [data: CreateStoreData];
|
||||
cancel: [];
|
||||
}>();
|
||||
|
||||
// Form data
|
||||
const formData = ref<CreateStoreData>({
|
||||
name: '',
|
||||
location: '',
|
||||
is_active: true
|
||||
});
|
||||
|
||||
// Track if form has been touched
|
||||
const touched = ref(false);
|
||||
|
||||
// Watch for store changes (when editing)
|
||||
watch(() => props.store, (newStore) => {
|
||||
if (newStore) {
|
||||
formData.value = {
|
||||
name: newStore.name,
|
||||
location: newStore.location,
|
||||
is_active: newStore.is_active
|
||||
};
|
||||
touched.value = false;
|
||||
} else {
|
||||
// Reset form
|
||||
formData.value = {
|
||||
name: '',
|
||||
location: '',
|
||||
is_active: true
|
||||
};
|
||||
touched.value = false;
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// Form validation
|
||||
const isFormValid = () => {
|
||||
return formData.value.name.trim() !== '' &&
|
||||
formData.value.location.trim() !== '';
|
||||
};
|
||||
|
||||
// Check if field should show error
|
||||
const showError = (field: keyof CreateStoreData) => {
|
||||
if (!touched.value) return false;
|
||||
if (field === 'name') return formData.value.name.trim() === '';
|
||||
if (field === 'location') return formData.value.location.trim() === '';
|
||||
return false;
|
||||
};
|
||||
|
||||
// Form submission
|
||||
const handleSubmit = () => {
|
||||
touched.value = true;
|
||||
if (!isFormValid()) return;
|
||||
|
||||
emit('save', formData.value);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="store-form">
|
||||
<div class="space-y-6">
|
||||
<!-- Store Name -->
|
||||
<div>
|
||||
<label for="store-name" class="block text-sm font-medium mb-2 text-surface-900 dark:text-white">
|
||||
Nombre del Punto de Venta *
|
||||
</label>
|
||||
<InputText
|
||||
id="store-name"
|
||||
v-model="formData.name"
|
||||
class="w-full"
|
||||
placeholder="ej., Sucursal Centro"
|
||||
:invalid="showError('name')"
|
||||
/>
|
||||
<small class="text-surface-500 dark:text-surface-400 block mt-1">
|
||||
Ingresa un nombre descriptivo para el punto de venta
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Location -->
|
||||
<div>
|
||||
<label for="location" class="block text-sm font-medium mb-2 text-surface-900 dark:text-white">
|
||||
Ubicación *
|
||||
</label>
|
||||
<Textarea
|
||||
id="location"
|
||||
v-model="formData.location"
|
||||
class="w-full"
|
||||
rows="3"
|
||||
placeholder="ej., Av. Principal 123, Ciudad, Estado, CP 12345"
|
||||
:invalid="showError('location')"
|
||||
/>
|
||||
<small class="text-surface-500 dark:text-surface-400 block mt-1">
|
||||
Dirección física completa del punto de venta
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Active Status -->
|
||||
<div class="flex items-center gap-3">
|
||||
<label for="is-active" class="text-sm font-medium text-surface-900 dark:text-white">
|
||||
Estado Activo
|
||||
</label>
|
||||
<InputSwitch
|
||||
id="is-active"
|
||||
v-model="formData.is_active"
|
||||
/>
|
||||
<span class="text-sm text-surface-500 dark:text-surface-400">
|
||||
{{ formData.is_active ? 'Activo' : 'Inactivo' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Actions -->
|
||||
<div class="mt-6 flex justify-end items-center gap-3">
|
||||
<Button
|
||||
label="Cancelar"
|
||||
severity="secondary"
|
||||
outlined
|
||||
@click="handleCancel"
|
||||
/>
|
||||
<Button
|
||||
:label="isEditing ? 'Actualizar' : 'Guardar'"
|
||||
:disabled="!isFormValid()"
|
||||
@click="handleSubmit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.store-form {
|
||||
min-width: 500px;
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import Card from 'primevue/card';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Button from 'primevue/button';
|
||||
@ -9,6 +11,15 @@ import Tag from 'primevue/tag';
|
||||
import IconField from 'primevue/iconfield';
|
||||
import InputIcon from 'primevue/inputicon';
|
||||
import Dropdown from 'primevue/dropdown';
|
||||
import Dialog from 'primevue/dialog';
|
||||
import Toast from 'primevue/toast';
|
||||
import ConfirmDialog from 'primevue/confirmdialog';
|
||||
import { storesService } from '../services/storesServices';
|
||||
import StoreForm from './StoreForm.vue';
|
||||
import type { Store, CreateStoreData } from '../types/store';
|
||||
|
||||
const toast = useToast();
|
||||
const confirm = useConfirm();
|
||||
|
||||
// Search and filters
|
||||
const searchQuery = ref('');
|
||||
@ -16,12 +27,28 @@ const selectedStatus = ref('all');
|
||||
const selectedType = ref('all');
|
||||
const selectedLocation = ref('all');
|
||||
|
||||
// Data and loading state
|
||||
const stores = ref<Store[]>([]);
|
||||
const loading = ref(false);
|
||||
const totalRecords = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const rowsPerPage = ref(5);
|
||||
|
||||
// Dialog state
|
||||
const showDialog = ref(false);
|
||||
const isEditing = ref(false);
|
||||
const selectedStore = ref<Store | null>(null);
|
||||
|
||||
// Computed
|
||||
const dialogTitle = computed(() =>
|
||||
isEditing.value ? 'Editar Punto de Venta' : 'Nuevo Punto de Venta'
|
||||
);
|
||||
|
||||
// Filter options
|
||||
const statusOptions = [
|
||||
{ label: 'Todos', value: 'all' },
|
||||
{ label: 'Activo', value: 'active' },
|
||||
{ label: 'Inactivo', value: 'inactive' },
|
||||
{ label: 'Archivado', value: 'archived' },
|
||||
];
|
||||
|
||||
const typeOptions = [
|
||||
@ -38,65 +65,121 @@ const locationOptions = [
|
||||
{ label: 'Sur', value: 'sur' },
|
||||
];
|
||||
|
||||
// Sample data
|
||||
const stores = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Tienda Principal',
|
||||
location: 'Av. Siempre Viva 742',
|
||||
status: 'active',
|
||||
terminals: 5,
|
||||
lastActivity: 'Hoy, 10:45 AM'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Sucursal Centro',
|
||||
location: 'Calle Falsa 123',
|
||||
status: 'active',
|
||||
terminals: 3,
|
||||
lastActivity: 'Ayer, 08:15 PM'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Kiosco del Parque',
|
||||
location: 'Parque Central, Stand 4',
|
||||
status: 'inactive',
|
||||
terminals: 1,
|
||||
lastActivity: '25/08/2023'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Almacén Norte',
|
||||
location: 'Blvd. Industrial 500',
|
||||
status: 'active',
|
||||
terminals: 8,
|
||||
lastActivity: 'Hoy, 11:00 AM'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Pop-Up de Verano',
|
||||
location: 'Playa Grande, Paseo Marítimo',
|
||||
status: 'archived',
|
||||
terminals: 2,
|
||||
lastActivity: '15/07/2023'
|
||||
// Fetch stores from API
|
||||
const fetchStores = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await storesService.getStores(currentPage.value, rowsPerPage.value);
|
||||
|
||||
if (response.data.status === 'success') {
|
||||
stores.value = response.data.data.points_of_sale.data;
|
||||
totalRecords.value = response.data.data.points_of_sale.total;
|
||||
currentPage.value = response.data.data.points_of_sale.current_page;
|
||||
|
||||
console.log('✅ Stores loaded:', stores.value.length);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error loading stores:', error);
|
||||
stores.value = [];
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'No se pudieron cargar los puntos de venta.',
|
||||
life: 3000
|
||||
});
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
]);
|
||||
|
||||
// Methods
|
||||
const handleCreateStore = () => {
|
||||
console.log('Create new store');
|
||||
};
|
||||
|
||||
const handleEdit = (store: any) => {
|
||||
console.log('Edit store:', store);
|
||||
// Pagination handler
|
||||
const onPage = (event: any) => {
|
||||
currentPage.value = event.page + 1;
|
||||
rowsPerPage.value = event.rows;
|
||||
fetchStores();
|
||||
};
|
||||
|
||||
const handleView = (store: any) => {
|
||||
console.log('View store:', store);
|
||||
// Dialog methods
|
||||
const openCreateDialog = () => {
|
||||
selectedStore.value = null;
|
||||
isEditing.value = false;
|
||||
showDialog.value = true;
|
||||
};
|
||||
|
||||
const handleToggleStatus = (store: any) => {
|
||||
console.log('Toggle status:', store);
|
||||
const handleEdit = (store: Store) => {
|
||||
selectedStore.value = store;
|
||||
isEditing.value = true;
|
||||
showDialog.value = true;
|
||||
};
|
||||
|
||||
const handleSaveStore = async (storeData: CreateStoreData) => {
|
||||
try {
|
||||
if (isEditing.value && selectedStore.value) {
|
||||
await storesService.updateStore(selectedStore.value.id, storeData);
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Punto de Venta Actualizado',
|
||||
detail: 'El punto de venta ha sido actualizado exitosamente.',
|
||||
life: 3000
|
||||
});
|
||||
} else {
|
||||
await storesService.createStore(storeData);
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Punto de Venta Creado',
|
||||
detail: 'El punto de venta ha sido creado exitosamente.',
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
showDialog.value = false;
|
||||
selectedStore.value = null;
|
||||
await fetchStores();
|
||||
} catch (error) {
|
||||
console.error('❌ Error saving store:', error);
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'No se pudo guardar el punto de venta.',
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelForm = () => {
|
||||
showDialog.value = false;
|
||||
selectedStore.value = null;
|
||||
};
|
||||
|
||||
const handleDeleteStore = (store: Store) => {
|
||||
confirm.require({
|
||||
message: `¿Estás seguro de eliminar el punto de venta "${store.name}"?`,
|
||||
header: 'Confirmar Eliminación',
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
rejectLabel: 'Cancelar',
|
||||
acceptLabel: 'Eliminar',
|
||||
rejectClass: 'p-button-secondary p-button-outlined',
|
||||
acceptClass: 'p-button-danger',
|
||||
accept: async () => {
|
||||
try {
|
||||
await storesService.deleteStore(store.id);
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Punto de Venta Eliminado',
|
||||
detail: `El punto de venta "${store.name}" ha sido eliminado exitosamente.`,
|
||||
life: 3000
|
||||
});
|
||||
await fetchStores();
|
||||
} catch (error) {
|
||||
console.error('❌ Error deleting store:', error);
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'No se pudo eliminar el punto de venta.',
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
@ -106,29 +189,27 @@ const clearFilters = () => {
|
||||
selectedLocation.value = 'all';
|
||||
};
|
||||
|
||||
// Get status label
|
||||
const getStatusLabel = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
active: 'Activo',
|
||||
inactive: 'Inactivo',
|
||||
archived: 'Archivado'
|
||||
};
|
||||
return statusMap[status] || status;
|
||||
// Get status configuration
|
||||
const getStatusConfig = (isActive: boolean) => {
|
||||
return isActive
|
||||
? { label: 'Activo', severity: 'success' as const }
|
||||
: { label: 'Inactivo', severity: 'warning' as const };
|
||||
};
|
||||
|
||||
// Get status severity
|
||||
const getStatusSeverity = (status: string) => {
|
||||
const severityMap: Record<string, 'success' | 'warning' | 'secondary'> = {
|
||||
active: 'success',
|
||||
inactive: 'warning',
|
||||
archived: 'secondary'
|
||||
};
|
||||
return severityMap[status] || 'secondary';
|
||||
};
|
||||
// Load stores on mount
|
||||
onMounted(() => {
|
||||
fetchStores();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-6">
|
||||
<!-- Toast Notifications -->
|
||||
<Toast position="bottom-right" />
|
||||
|
||||
<!-- Confirm Dialog -->
|
||||
<ConfirmDialog />
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex flex-wrap justify-between gap-4 items-center">
|
||||
<div class="flex min-w-72 flex-col gap-1">
|
||||
@ -142,7 +223,7 @@ const getStatusSeverity = (status: string) => {
|
||||
<Button
|
||||
label="Crear Nuevo Punto de Venta"
|
||||
icon="pi pi-plus"
|
||||
@click="handleCreateStore"
|
||||
@click="openCreateDialog"
|
||||
class="min-w-[200px]"
|
||||
/>
|
||||
</div>
|
||||
@ -199,11 +280,15 @@ const getStatusSeverity = (status: string) => {
|
||||
<!-- Table -->
|
||||
<DataTable
|
||||
:value="stores"
|
||||
:loading="loading"
|
||||
:paginator="true"
|
||||
:rows="5"
|
||||
:rows="rowsPerPage"
|
||||
:totalRecords="totalRecords"
|
||||
:rowsPerPageOptions="[5, 10, 20, 50]"
|
||||
stripedRows
|
||||
responsiveLayout="scroll"
|
||||
lazy
|
||||
@page="onPage"
|
||||
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown CurrentPageReport"
|
||||
currentPageReportTemplate="Mostrando {first} a {last} de {totalRecords} resultados"
|
||||
>
|
||||
@ -226,29 +311,24 @@ const getStatusSeverity = (status: string) => {
|
||||
</Column>
|
||||
|
||||
<!-- Status Column -->
|
||||
<Column field="status" header="Estado" :sortable="true">
|
||||
<Column field="is_active" header="Estado" :sortable="true">
|
||||
<template #body="{ data }">
|
||||
<Tag
|
||||
:value="getStatusLabel(data.status)"
|
||||
:severity="getStatusSeverity(data.status)"
|
||||
:value="getStatusConfig(data.is_active).label"
|
||||
:severity="getStatusConfig(data.is_active).severity"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
|
||||
<!-- Terminals Column -->
|
||||
<Column field="terminals" header="Terminales" :sortable="true">
|
||||
<template #body="{ data }">
|
||||
<span class="text-center block text-surface-500 dark:text-surface-400 text-sm">
|
||||
{{ data.terminals }}
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
|
||||
<!-- Last Activity Column -->
|
||||
<Column field="lastActivity" header="Última Actividad" :sortable="true">
|
||||
<!-- Created Date Column -->
|
||||
<Column field="created_at" header="Fecha de Creación" :sortable="true">
|
||||
<template #body="{ data }">
|
||||
<span class="text-surface-500 dark:text-surface-400 text-sm">
|
||||
{{ data.lastActivity }}
|
||||
{{ new Date(data.created_at).toLocaleDateString('es-ES', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
}) }}
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
@ -266,39 +346,12 @@ const getStatusSeverity = (status: string) => {
|
||||
v-tooltip.top="'Editar'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-eye"
|
||||
severity="secondary"
|
||||
text
|
||||
rounded
|
||||
@click="handleView(data)"
|
||||
v-tooltip.top="'Ver detalles'"
|
||||
/>
|
||||
<Button
|
||||
v-if="data.status === 'active'"
|
||||
icon="pi pi-power-off"
|
||||
icon="pi pi-trash"
|
||||
severity="danger"
|
||||
text
|
||||
rounded
|
||||
@click="handleToggleStatus(data)"
|
||||
v-tooltip.top="'Desactivar'"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="data.status === 'inactive'"
|
||||
icon="pi pi-check"
|
||||
severity="success"
|
||||
text
|
||||
rounded
|
||||
@click="handleToggleStatus(data)"
|
||||
v-tooltip.top="'Activar'"
|
||||
/>
|
||||
<Button
|
||||
v-else
|
||||
icon="pi pi-inbox"
|
||||
severity="secondary"
|
||||
text
|
||||
rounded
|
||||
@click="handleToggleStatus(data)"
|
||||
v-tooltip.top="'Desarchivar'"
|
||||
@click="handleDeleteStore(data)"
|
||||
v-tooltip.top="'Eliminar'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -306,6 +359,22 @@ const getStatusSeverity = (status: string) => {
|
||||
</DataTable>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Create/Edit Dialog -->
|
||||
<Dialog
|
||||
v-model:visible="showDialog"
|
||||
modal
|
||||
:header="dialogTitle"
|
||||
:style="{ width: '90vw', maxWidth: '800px' }"
|
||||
:contentStyle="{ padding: '1.5rem' }"
|
||||
>
|
||||
<StoreForm
|
||||
:store="selectedStore"
|
||||
:isEditing="isEditing"
|
||||
@save="handleSaveStore"
|
||||
@cancel="handleCancelForm"
|
||||
/>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
106
src/modules/stores/services/storesServices.ts
Normal file
106
src/modules/stores/services/storesServices.ts
Normal file
@ -0,0 +1,106 @@
|
||||
|
||||
|
||||
import api from '../../../services/api';
|
||||
import type { StoreResponse, CreateStoreData, UpdateStoreData, SingleStoreResponse } from '../types/store';
|
||||
|
||||
export const storesService = {
|
||||
/**
|
||||
* Get all stores/points of sale with pagination
|
||||
* @param page - Page number (default: 1)
|
||||
* @param perPage - Items per page (default: 20)
|
||||
*/
|
||||
async getStores(page: number = 1, perPage: number = 20) {
|
||||
try {
|
||||
const response = await api.get<StoreResponse>('/api/stores', {
|
||||
params: {
|
||||
page,
|
||||
per_page: perPage
|
||||
}
|
||||
});
|
||||
console.log('📦 Stores response:', response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('❌ Error fetching stores:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a single store by ID
|
||||
* @param id - Store ID
|
||||
*/
|
||||
async getStoreById(id: number) {
|
||||
try {
|
||||
const response = await api.get<SingleStoreResponse>(`/api/stores/${id}`);
|
||||
console.log('📦 Store detail:', response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('❌ Error fetching store:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new store/point of sale
|
||||
* @param data - Store data
|
||||
*/
|
||||
async createStore(data: CreateStoreData) {
|
||||
try {
|
||||
const response = await api.post<SingleStoreResponse>('/api/stores', data);
|
||||
console.log('✅ Store created:', response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('❌ Error creating store:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update an existing store
|
||||
* @param id - Store ID
|
||||
* @param data - Updated store data
|
||||
*/
|
||||
async updateStore(id: number, data: UpdateStoreData) {
|
||||
try {
|
||||
const response = await api.put<SingleStoreResponse>(`/api/stores/${id}`, data);
|
||||
console.log('✅ Store updated:', response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('❌ Error updating store:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete a store (soft delete)
|
||||
* @param id - Store ID
|
||||
*/
|
||||
async deleteStore(id: number) {
|
||||
try {
|
||||
const response = await api.delete(`/api/stores/${id}`);
|
||||
console.log('✅ Store deleted:', response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('❌ Error deleting store:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle store active status
|
||||
* @param id - Store ID
|
||||
* @param isActive - New active status
|
||||
*/
|
||||
async toggleStoreStatus(id: number, isActive: boolean) {
|
||||
try {
|
||||
const response = await api.put<SingleStoreResponse>(`/api/stores/${id}`, {
|
||||
is_active: isActive
|
||||
});
|
||||
console.log('✅ Store status toggled:', response);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('❌ Error toggling store status:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
61
src/modules/stores/types/store.d.ts
vendored
Normal file
61
src/modules/stores/types/store.d.ts
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Store/Point of Sale Type Definitions
|
||||
*/
|
||||
|
||||
export interface Store {
|
||||
id: number;
|
||||
name: string;
|
||||
location: string;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
}
|
||||
|
||||
export interface CreateStoreData {
|
||||
name: string;
|
||||
location: string;
|
||||
is_active?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateStoreData {
|
||||
name?: string;
|
||||
location?: string;
|
||||
is_active?: boolean;
|
||||
}
|
||||
|
||||
export interface StorePagination {
|
||||
current_page: number;
|
||||
data: Store[];
|
||||
first_page_url: string;
|
||||
from: number;
|
||||
last_page: number;
|
||||
last_page_url: string;
|
||||
links: Array<{
|
||||
url: string | null;
|
||||
label: string;
|
||||
active: boolean;
|
||||
}>;
|
||||
next_page_url: string | null;
|
||||
path: string;
|
||||
per_page: number;
|
||||
prev_page_url: string | null;
|
||||
to: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface StoreData {
|
||||
points_of_sale: StorePagination;
|
||||
}
|
||||
|
||||
export interface StoreResponse {
|
||||
status: string;
|
||||
data: StoreData;
|
||||
}
|
||||
|
||||
export interface SingleStoreResponse {
|
||||
status: string;
|
||||
data: {
|
||||
point_of_sale: Store;
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user