feat: add modal for batch inventory item addition with responsive design and Tailwind CSS

This commit is contained in:
Edgar Méndez Mendoza 2026-01-30 15:38:06 -06:00
parent 730cae825c
commit 19753a0f48
6 changed files with 1945 additions and 7 deletions

View File

@ -0,0 +1,648 @@
<template>
<div class="space-y-6">
<!-- Toast Notifications -->
<Toast position="bottom-right" />
<!-- Breadcrumb -->
<Breadcrumb :home="breadcrumbHome" :model="breadcrumbItems" />
<!-- Page Header -->
<div class="flex flex-wrap justify-between items-center gap-4">
<div class="flex flex-col gap-2">
<h1 class="text-gray-900 dark:text-white text-3xl md:text-4xl font-black leading-tight tracking-tight">
Agregar Items al Inventario
</h1>
<p class="text-gray-500 dark:text-gray-400 text-base font-normal leading-normal">
Busca y agrega múltiples productos a tu stock en un lote único.
</p>
</div>
</div>
<!-- Search & Select Products Section -->
<Card class="shadow-sm">
<template #header>
<div class="px-6 py-4">
<h2 class="text-lg font-bold text-gray-900 dark:text-white">Seleccionar Productos del Catálogo</h2>
</div>
</template>
<template #content>
<div class="flex flex-col lg:flex-row gap-4 items-end">
<!-- Search Input -->
<div class="flex-1 w-full">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Buscar Catálogo
</label>
<div class="relative">
<i class="pi pi-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
<InputText
v-model="searchQuery"
type="text"
placeholder="Escribe Nombre del Producto o SKU..."
class="w-full pl-10 h-11"
@focus="showProductSearch = true"
/>
</div>
</div>
<!-- Add Button -->
<Button
label="Agregar Producto"
icon="pi pi-plus"
@click="openProductModal"
class="min-w-[200px]"
/>
</div>
<!-- Selected Product Preview (shown when typing) -->
<div v-if="showProductSearch && selectedProductPreview" class="mt-4 bg-gray-50 dark:bg-gray-800/30 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<!-- Product Info -->
<div class="md:col-span-4">
<p class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 mb-2">
Producto Seleccionado
</p>
<div class="flex items-center gap-3">
<Avatar :image="selectedProductPreview.image" shape="square" size="large" class="w-12 h-12" />
<div>
<p class="font-bold text-gray-900 dark:text-white">{{ selectedProductPreview.name }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400">SKU: {{ selectedProductPreview.sku }}</p>
</div>
</div>
</div>
<!-- Variants -->
<div class="md:col-span-2">
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 mb-2">
Tamaño
</label>
<Select
v-model="tempVariant.size"
:options="selectedProductPreview.sizes"
placeholder="Selecciona..."
class="w-full"
/>
</div>
<div class="md:col-span-2">
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 mb-2">
Resolución
</label>
<Select
v-model="tempVariant.resolution"
:options="selectedProductPreview.resolutions"
placeholder="Selecciona..."
class="w-full"
/>
</div>
<!-- Quantity -->
<div class="md:col-span-2">
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 mb-2">
Cantidad
</label>
<InputNumber
v-model="tempVariant.quantity"
:min="1"
placeholder="0"
class="w-full"
/>
</div>
<!-- Add Button -->
<div class="md:col-span-2 flex items-end">
<Button
label="Agregar"
icon="pi pi-plus"
@click="addItemToQueue"
class="w-full"
/>
</div>
</div>
</div>
</template>
</Card>
<!-- Queued Items Table -->
<Card class="shadow-sm">
<template #header>
<div class="flex items-center justify-between px-6 py-4">
<h2 class="text-lg font-bold text-gray-900 dark:text-white">
Items en Cola ({{ queuedItems.length }})
</h2>
<Button
label="Limpiar Todo"
severity="danger"
text
size="small"
@click="clearAllItems"
v-if="queuedItems.length > 0"
/>
</div>
</template>
<template #content>
<DataTable
v-if="queuedItems.length > 0"
:value="queuedItems"
stripedRows
responsiveLayout="scroll"
class="text-sm"
>
<Column field="productName" header="Nombre del Producto">
<template #body="slotProps">
<div class="flex items-center gap-3">
<Avatar :image="slotProps.data.image" shape="square" size="large" class="w-8 h-8" />
<span class="font-medium">{{ slotProps.data.productName }}</span>
</div>
</template>
</Column>
<Column field="sku" header="SKU">
<template #body="slotProps">
<span class="text-sm text-gray-500">{{ slotProps.data.sku }}</span>
</template>
</Column>
<Column field="variant" header="Variante">
<template #body="slotProps">
<div class="flex flex-wrap gap-2">
<Tag
v-if="slotProps.data.size"
:value="`Tamaño: ${slotProps.data.size}`"
severity="info"
class="text-xs"
/>
<Tag
v-if="slotProps.data.resolution"
:value="`Resolución: ${slotProps.data.resolution}`"
severity="warning"
class="text-xs"
/>
</div>
</template>
</Column>
<Column field="quantity" header="Cantidad">
<template #body="slotProps">
<InputNumber
v-model="slotProps.data.quantity"
:min="1"
class="w-20"
/>
</template>
</Column>
<Column field="unit" header="Unidad">
<template #body="slotProps">
<span class="text-sm">{{ slotProps.data.unit }}</span>
</template>
</Column>
<Column header="Acción" :exportable="false">
<template #body="slotProps">
<Button
icon="pi pi-trash"
severity="danger"
text
rounded
size="small"
@click="removeItem(slotProps.data.id)"
/>
</template>
</Column>
</DataTable>
<!-- Empty State -->
<div v-else class="text-center py-12">
<i class="pi pi-inbox text-4xl text-gray-300 dark:text-gray-600 mb-4 block"></i>
<p class="text-gray-500 dark:text-gray-400">No hay items en la cola. Comienza a agregar productos.</p>
</div>
<!-- Summary -->
<div v-if="queuedItems.length > 0" class="mt-6 p-4 bg-gray-50 dark:bg-gray-800/30 border border-gray-200 dark:border-gray-700 rounded-lg">
<p class="text-sm text-gray-600 dark:text-gray-300">
Total de items a agregar: <span class="font-bold text-gray-900 dark:text-white">{{ totalQueuedQuantity }} unidades</span>
en {{ queuedItems.length }} productos únicos.
</p>
</div>
</template>
</Card>
<!-- Product Selection Modal -->
<Dialog
v-model:visible="showModal"
:header="'Agregar Producto al Lote'"
:modal="true"
:style="{ width: '90vw' }"
:maximizable="true"
class="md:w-full max-w-2xl"
>
<div class="space-y-6">
<!-- Search Input -->
<div class="space-y-3">
<label class="block text-sm font-semibold text-gray-700 dark:text-gray-300">
Buscar Catálogo
</label>
<div class="relative">
<i class="pi pi-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
<InputText
v-model="modalSearchQuery"
type="text"
placeholder="Escribe Nombre del Producto o SKU..."
class="w-full pl-10"
/>
</div>
</div>
<!-- Selected Product Details -->
<div class="bg-gray-50 dark:bg-gray-800/30 rounded-lg p-5 border border-gray-100 dark:border-gray-700 space-y-6">
<!-- Product Info -->
<div class="flex items-center gap-4">
<Avatar :image="modalProduct.image" shape="square" size="xlarge" class="w-16 h-16" />
<div>
<p class="text-lg font-bold text-gray-900 dark:text-white">{{ modalProduct.name }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 font-medium">SKU: {{ modalProduct.sku }}</p>
</div>
</div>
<!-- Variants Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="block text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400">
Tamaño
</label>
<Select
v-model="modalProduct.selectedSize"
:options="modalProduct.sizes"
placeholder="Selecciona..."
class="w-full"
/>
</div>
<div class="space-y-2">
<label class="block text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400">
Resolución
</label>
<Select
v-model="modalProduct.selectedResolution"
:options="modalProduct.resolutions"
placeholder="Selecciona..."
class="w-full"
/>
</div>
<div class="space-y-2">
<label class="block text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400">
Cantidad
</label>
<div class="flex items-center gap-2">
<InputNumber
v-model="modalProduct.selectedQuantity"
:min="1"
placeholder="0"
class="flex-1"
/>
<span class="text-sm font-medium text-gray-500">pcs</span>
</div>
</div>
<!-- Stock Info -->
<div class="flex items-end">
<InlineMessage severity="info" class="w-full text-xs">
<i class="pi pi-info-circle mr-2"></i>
Stock actual: {{ modalProduct.currentStock }} unidades
</InlineMessage>
</div>
</div>
</div>
</div>
<template #footer>
<Button
label="Cancelar"
severity="secondary"
@click="showModal = false"
/>
<Button
label="Confirmar y Agregar a la Lista"
icon="pi pi-check"
@click="confirmAddFromModal"
/>
</template>
</Dialog>
<!-- Bottom Action Bar -->
<div class="fixed bottom-0 right-0 left-0 md:left-64 bg-white dark:bg-background-dark border-t border-gray-200 dark:border-gray-700 p-4 px-6 md:px-10 flex justify-between items-center shadow-lg">
<div class="flex items-center gap-6">
<div class="flex items-center gap-2">
<span class="text-xs font-medium text-gray-500 uppercase tracking-widest">Ubicación de Almacenamiento</span>
<Select
v-model="selectedLocation"
:options="warehouseLocations"
optionLabel="label"
optionValue="value"
placeholder="Selecciona..."
class="h-9 w-64"
/>
</div>
</div>
<div class="flex items-center gap-4">
<Button
label="Cancelar"
severity="secondary"
@click="cancelBatch"
/>
<Button
label="Confirmar y Guardar Inventario"
icon="pi pi-check"
@click="saveBatch"
:disabled="queuedItems.length === 0"
/>
</div>
</div>
<!-- Bottom Spacing -->
<div class="pb-24"></div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast';
// PrimeVue Components
import Toast from 'primevue/toast';
import Breadcrumb from 'primevue/breadcrumb';
import Card from 'primevue/card';
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import InputNumber from 'primevue/inputnumber';
import Select from 'primevue/select';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import Dialog from 'primevue/dialog';
import Tag from 'primevue/tag';
import Avatar from 'primevue/avatar';
import InlineMessage from 'primevue/inlinemessage';
const router = useRouter();
const toast = useToast();
// State
const searchQuery = ref('');
const modalSearchQuery = ref('Smart Display Panel');
const selectedLocation = ref('main-warehouse');
const showProductSearch = ref(false);
const showModal = ref(false);
// Breadcrumb
const breadcrumbHome = ref({ icon: 'pi pi-home', to: '/' });
const breadcrumbItems = ref([
{ label: 'Almacén', to: '/warehouse' },
{ label: 'Agregar Items al Inventario' }
]);
// Warehouse Locations
const warehouseLocations = [
{ label: 'Almacén Principal (A1)', value: 'main-warehouse' },
{ label: 'Almacenamiento Sur (B4)', value: 'south-storage' },
{ label: 'Almacenamiento Frío (C2)', value: 'cold-storage' },
];
// Mock Product Data
const mockProducts = [
{
id: 1,
name: 'Smart Display Panel',
sku: 'DISP-PNL-SD-001',
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBMbZhSSPYoHEi3akXSqZXm-T6EghACuHJJ5NYeJ8g4XJIKtqXRhCSQ1kFcvo7Txk01ryR-r6RJ61NzPLENWMywvUVnCkQglsizZG0NKPBwRFQjXvtDULkGbGFm6EPocyHGfuzKJ0EoDr601zMBI34mfPCgn9AAaRkTMj2Ize2nCUcanlGn7QJEp6BdNcmf3JFlk-51jPTXj1-mM4Pw6AGIvarhxnZqtEAji6qRF7evVTR_56c48h5if21S3jD-wVhNVp8AltVn8KEH',
sizes: ['24', '36', '64', '85'],
resolutions: ['1080', '2K', '4K', '8K'],
currentStock: 420,
},
{
id: 2,
name: 'Mechanical Keyboard RGB',
sku: 'KYBD-RGB-US-02',
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBBMBDEGh1exdBX1lU--TOCsUwfsFkYhrl8DxxvxgH-hKLkcuVtuGP_ZhRS5l_YXFWls08Ecr-Ic748cVHqHexFMMzYTPH8YL2s9OISDgsMIwcDOPxitrPUn3RBCD_krFC8SrBXBeNC8ilcFdg_JCwD5BW5gaYjAAEUMFXyM1vd-YT27KyubBk1IKATsMdDkH-NtwAHcrPgNoXsRaAcubBQiebCG1hmxKVW3fAXoDoSqywrxhwZ7cmIuo7BTThJJ1KcczxQh1jYh_w4',
switches: ['Red Switch', 'Blue Switch', 'Brown Switch'],
currentStock: 230,
},
{
id: 3,
name: 'USB-C Charging Cable 2m',
sku: 'CBL-USBC-2M',
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuAsb9nqDON7PeKGYYAU8hmHn7B2l3waP_ewMQTpsgnpJe2vFiY-k-zncE5VEf186tDzlTwVPA6-1SgAFagugq9Anw2WXloUFTNwOrOHgAi-MNhgs0FuxRKF8XMLk6W_u6YLF5xWh3K2q317JXFSUWtURtxCi8pYM4YtHYEyuT2aIrhbAnI2LQ7ja8DvSdvpO6t1IJ6HE9RqlnAFXuLG22igxiekKT2NjRpuK5Zzu_90rBPEjb0KitG9DwiBjzKa702pccHPRqupgXke',
variants: ['Braided', 'Standard'],
currentStock: 1200,
},
];
// Queued Items
const queuedItems = ref<any[]>([]);
// Temporary variant selection
const tempVariant = ref({
size: null,
resolution: null,
quantity: 1,
});
// Selected Product Preview
const selectedProductPreview = ref<any>(null);
// Get default product
const defaultProduct = mockProducts[0]!;
// Modal Product
const modalProduct = ref({
name: 'Smart Display Panel',
sku: 'DISP-PNL-SD-001',
image: defaultProduct.image,
sizes: defaultProduct.sizes,
resolutions: defaultProduct.resolutions,
currentStock: defaultProduct.currentStock,
selectedSize: '85',
selectedResolution: '4K',
selectedQuantity: 10,
});
// Computed
const totalQueuedQuantity = computed(() => {
return queuedItems.value.reduce((sum, item) => sum + item.quantity, 0);
});
// Methods
const openProductModal = () => {
showModal.value = true;
resetModalProduct();
};
const resetModalProduct = () => {
modalProduct.value = {
name: 'Smart Display Panel',
sku: 'DISP-PNL-SD-001',
image: defaultProduct.image,
sizes: defaultProduct.sizes,
resolutions: defaultProduct.resolutions,
currentStock: defaultProduct.currentStock,
selectedSize: '85',
selectedResolution: '4K',
selectedQuantity: 1,
};
};
const addItemToQueue = () => {
if (!selectedProductPreview.value || !tempVariant.value.quantity) {
toast.add({
severity: 'warn',
summary: 'Validación',
detail: 'Por favor selecciona un producto y cantidad',
life: 3000
});
return;
}
const newItem = {
id: Math.random(),
productName: selectedProductPreview.value.name,
sku: selectedProductPreview.value.sku,
image: selectedProductPreview.value.image,
size: tempVariant.value.size,
resolution: tempVariant.value.resolution,
quantity: tempVariant.value.quantity,
unit: 'pcs',
};
queuedItems.value.push(newItem);
toast.add({
severity: 'success',
summary: 'Producto Agregado',
detail: `${selectedProductPreview.value.name} ha sido agregado a la cola`,
life: 3000
});
// Reset
selectedProductPreview.value = null;
showProductSearch.value = false;
searchQuery.value = '';
tempVariant.value = { size: null, resolution: null, quantity: 1 };
};
const confirmAddFromModal = () => {
if (!modalProduct.value.selectedQuantity || !modalProduct.value.selectedSize) {
toast.add({
severity: 'warn',
summary: 'Validación',
detail: 'Por favor completa todos los campos',
life: 3000
});
return;
}
const newItem = {
id: Math.random(),
productName: modalProduct.value.name,
sku: modalProduct.value.sku,
image: modalProduct.value.image,
size: modalProduct.value.selectedSize,
resolution: modalProduct.value.selectedResolution,
quantity: modalProduct.value.selectedQuantity,
unit: 'pcs',
};
queuedItems.value.push(newItem);
toast.add({
severity: 'success',
summary: 'Producto Agregado',
detail: `${modalProduct.value.name} ha sido agregado a la cola`,
life: 3000
});
showModal.value = false;
resetModalProduct();
};
const removeItem = (id: number) => {
const index = queuedItems.value.findIndex(item => item.id === id);
if (index > -1) {
const removedItem = queuedItems.value.splice(index, 1)[0];
toast.add({
severity: 'info',
summary: 'Item Removido',
detail: `${removedItem.productName} ha sido removido`,
life: 3000
});
}
};
const clearAllItems = () => {
queuedItems.value = [];
toast.add({
severity: 'info',
summary: 'Cola Limpiada',
detail: 'Todos los items han sido removidos',
life: 3000
});
};
const cancelBatch = () => {
if (queuedItems.value.length > 0) {
const confirmed = confirm('¿Estás seguro? Se perderán todos los items en la cola.');
if (!confirmed) return;
}
router.back();
};
const saveBatch = () => {
if (queuedItems.value.length === 0) {
toast.add({
severity: 'warn',
summary: 'Validación',
detail: 'Agrega al menos un item a la cola',
life: 3000
});
return;
}
if (!selectedLocation.value) {
toast.add({
severity: 'warn',
summary: 'Validación',
detail: 'Selecciona una ubicación de almacenamiento',
life: 3000
});
return;
}
// TODO: Enviar datos al API
console.log('Batch data:', {
items: queuedItems.value,
location: selectedLocation.value,
});
toast.add({
severity: 'success',
summary: 'Éxito',
detail: `${queuedItems.value.length} productos agregados al inventario exitosamente`,
life: 3000
});
// Reset y volver
queuedItems.value = [];
setTimeout(() => {
router.push({ name: 'WarehouseHome' });
}, 1500);
};
</script>
<style scoped>
:deep(.p-dialog-header) {
background-color: var(--surface-50);
}
.dark :deep(.p-dialog-header) {
background-color: var(--surface-800);
}
:deep(.p-dialog-content) {
max-height: calc(90vh - 200px);
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,551 @@
<template>
<div class="space-y-6">
<!-- Toast Notifications -->
<Toast position="bottom-right" />
<!-- Breadcrumb -->
<Breadcrumb :home="breadcrumbHome" :model="breadcrumbItems" />
<!-- Page Header -->
<div class="flex flex-wrap items-center justify-between gap-4">
<div class="flex flex-col gap-2">
<h1 class="text-surface-900 dark:text-white text-3xl md:text-4xl font-black leading-tight tracking-tight">
{{ warehouseData?.name || 'North Logistics Center' }}
</h1>
<div class="flex flex-wrap items-center gap-3">
<div class="flex items-center gap-2 text-surface-500 dark:text-surface-400">
<i class="pi pi-map-marker text-sm"></i>
<span class="text-sm">{{ warehouseData?.location || 'Zone A, Building 4' }}</span>
</div>
<div class="size-1 bg-surface-300 dark:bg-surface-600 rounded-full"></div>
<Tag :value="warehouseData?.isActive ? 'Operacional' : 'Inactivo'"
:severity="warehouseData?.isActive ? 'success' : 'secondary'" />
</div>
</div>
<div class="flex gap-3">
<Button label="Exportar Datos" icon="pi pi-download" outlined severity="secondary" />
<Button icon="pi pi-print" outlined severity="secondary" />
<Button label="Entradas" icon="pi pi-plus" @click="openBatchAdd" />
</div>
</div>
<!-- Stats Overview -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Total SKUs -->
<Card class="shadow-sm">
<template #content>
<div class="space-y-3">
<p class="text-sm font-medium text-surface-500 dark:text-surface-400">Total SKUs</p>
<p class="text-2xl font-bold text-surface-900 dark:text-white">1,240</p>
<div class="flex items-center gap-1 text-xs font-semibold text-green-600 dark:text-green-400">
<i class="pi pi-arrow-up text-xs"></i>
+2.4% vs mes anterior
</div>
</div>
</template>
</Card>
<!-- Low Stock Items -->
<Card class="shadow-sm border-l-4 border-l-red-500">
<template #content>
<div class="space-y-3">
<p class="text-sm font-medium text-surface-500 dark:text-surface-400">Items con Stock Bajo</p>
<p class="text-2xl font-bold text-surface-900 dark:text-white">18</p>
<div class="flex items-center gap-1 text-xs font-semibold text-red-600 dark:text-red-400">
<i class="pi pi-exclamation-triangle text-xs"></i>
Acción requerida
</div>
</div>
</template>
</Card>
<!-- Movements 24h -->
<Card class="shadow-sm">
<template #content>
<div class="space-y-3">
<p class="text-sm font-medium text-surface-500 dark:text-surface-400">Movimientos (24h)</p>
<p class="text-2xl font-bold text-surface-900 dark:text-white">142</p>
<div class="flex items-center gap-1 text-xs font-semibold text-blue-600 dark:text-blue-400">
<i class="pi pi-refresh text-xs"></i>
Alta actividad
</div>
</div>
</template>
</Card>
<!-- Warehouse Utilization -->
<Card class="shadow-sm">
<template #content>
<div class="space-y-3">
<p class="text-sm font-medium text-surface-500 dark:text-surface-400">Utilización del Almacén</p>
<p class="text-2xl font-bold text-surface-900 dark:text-white">84%</p>
<ProgressBar :value="84" :showValue="false" class="h-2" />
</div>
</template>
</Card>
</div>
<!-- Tabs & Main Content Table -->
<Card class="shadow-sm">
<template #content>
<TabView v-model:activeIndex="activeTab" class="w-full">
<!-- Stock Actual Tab -->
<TabPanel value="0">
<template #header>
<div class="flex items-center gap-2">
<i class="pi pi-box"></i>
<span>Stock Actual</span>
</div>
</template>
<!-- Filters -->
<div class="flex flex-wrap items-center justify-end gap-3 mb-4">
<Select
v-model="selectedCategory"
:options="categoryOptions"
optionLabel="label"
optionValue="value"
placeholder="Todas las Categorías"
class="w-full md:w-48"
/>
<Select
v-model="selectedStockLevel"
:options="stockLevelOptions"
optionLabel="label"
optionValue="value"
placeholder="Todos los Niveles"
class="w-full md:w-48"
/>
<Button icon="pi pi-filter" outlined severity="secondary" />
</div>
<!-- Current Stock Table -->
<DataTable
:value="inventoryData"
:paginator="true"
:rows="10"
:rowsPerPageOptions="[10, 25, 50]"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown CurrentPageReport"
currentPageReportTemplate="Mostrando {first} a {last} de {totalRecords} resultados"
:loading="loading"
stripedRows
responsiveLayout="scroll"
class="text-sm"
>
<Column field="sku" header="SKU" sortable class="font-mono">
<template #body="slotProps">
<span class="font-semibold">{{ slotProps.data.sku }}</span>
</template>
</Column>
<Column field="product" header="Producto" sortable>
<template #body="slotProps">
<div class="flex flex-col gap-1">
<span class="font-semibold">{{ slotProps.data.product }}</span>
<span class="text-xs text-surface-500">{{ slotProps.data.category }}</span>
</div>
</template>
</Column>
<Column field="quantity" header="Cantidad" sortable>
<template #body="slotProps">
<span class="font-bold tabular-nums">{{ slotProps.data.quantity.toLocaleString() }}</span>
</template>
</Column>
<Column field="unit" header="Unidad" sortable />
<Column field="location" header="Ubicación" sortable>
<template #body="slotProps">
<div class="flex items-center gap-2">
<i class="pi pi-map-marker text-surface-400 text-xs"></i>
<span>{{ slotProps.data.location }}</span>
</div>
</template>
</Column>
<Column field="status" header="Estado" sortable>
<template #body="slotProps">
<Tag
:value="slotProps.data.status"
:severity="getStatusSeverity(slotProps.data.status)"
/>
</template>
</Column>
<Column field="lastUpdate" header="Última Actualización" sortable>
<template #body="slotProps">
<span class="text-xs text-surface-500">{{ slotProps.data.lastUpdate }}</span>
</template>
</Column>
<Column header="Acciones" :exportable="false">
<template #body="slotProps">
<div class="flex gap-2">
<Button icon="pi pi-eye" outlined rounded size="small" severity="secondary" @click="viewItem(slotProps.data)" />
<Button icon="pi pi-pencil" outlined rounded size="small" @click="editItem(slotProps.data)" />
</div>
</template>
</Column>
</DataTable>
</TabPanel>
<!-- Historial de Movimientos Tab -->
<TabPanel value="1">
<template #header>
<div class="flex items-center gap-2">
<i class="pi pi-history"></i>
<span>Historial de Movimientos</span>
</div>
</template>
<!-- Movement History Table -->
<DataTable
:value="movementHistory"
:paginator="true"
:rows="10"
:rowsPerPageOptions="[10, 25, 50]"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown CurrentPageReport"
currentPageReportTemplate="Mostrando {first} a {last} de {totalRecords} resultados"
:loading="loading"
stripedRows
responsiveLayout="scroll"
class="text-sm"
>
<Column field="date" header="Fecha" sortable />
<Column field="type" header="Tipo" sortable>
<template #body="slotProps">
<Tag
:value="slotProps.data.type"
:severity="getMovementTypeSeverity(slotProps.data.type)"
/>
</template>
</Column>
<Column field="product" header="Producto" sortable />
<Column field="quantity" header="Cantidad" sortable>
<template #body="slotProps">
<span class="font-bold tabular-nums">{{ slotProps.data.quantity }}</span>
</template>
</Column>
<Column field="user" header="Usuario" sortable />
<Column field="reference" header="Referencia" sortable />
</DataTable>
</TabPanel>
</TabView>
</template>
</Card>
<!-- Secondary Section: Recent Movements & Insights -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Quick Activity Log -->
<Card class="lg:col-span-2 shadow-sm">
<template #header>
<div class="flex items-center justify-between p-4">
<h3 class="font-bold text-surface-900 dark:text-white">Registro Rápido de Actividad</h3>
<Button label="Ver Todos los Movimientos" link class="text-sm" @click="viewAllMovements" />
</div>
</template>
<template #content>
<div class="space-y-4">
<div v-for="activity in recentActivities" :key="activity.id"
class="flex items-center gap-4 py-3 border-b border-surface-200 dark:border-surface-700 last:border-b-0">
<div :class="[
'flex items-center justify-center size-10 rounded-full',
activity.type === 'in' ? 'bg-green-100 dark:bg-green-900/30' : 'bg-red-100 dark:bg-red-900/30'
]">
<i :class="[
'pi text-sm',
activity.type === 'in' ? 'pi-arrow-down text-green-600' : 'pi-arrow-up text-red-600'
]"></i>
</div>
<div class="flex-1 min-w-0">
<p class="font-semibold text-surface-900 dark:text-white">{{ activity.action }}</p>
<p class="text-sm text-surface-500 dark:text-surface-400">{{ activity.product }}</p>
</div>
<div class="text-right">
<p class="font-bold text-surface-900 dark:text-white">{{ activity.quantity }}</p>
<p class="text-xs text-surface-500 dark:text-surface-400">{{ activity.time }}</p>
</div>
</div>
</div>
</template>
</Card>
<!-- Warehouse Health Insights -->
<Card class="shadow-sm bg-primary-50 dark:bg-primary-900/10 border border-primary-200 dark:border-primary-800">
<template #content>
<div class="space-y-4">
<div>
<h3 class="font-bold text-surface-900 dark:text-white mb-2">Salud del Almacén</h3>
<p class="text-sm text-surface-600 dark:text-surface-300 leading-relaxed">
North Logistics Center está actualmente al 84% de capacidad. Recomendamos auditar la Zona B
para optimización de espacio potencial.
</p>
</div>
<div class="space-y-3">
<div class="flex items-center justify-between">
<span class="text-sm text-surface-700 dark:text-surface-200">Capacidad Total</span>
<span class="font-bold text-surface-900 dark:text-white">50,000 </span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-surface-700 dark:text-surface-200">Espacio Utilizado</span>
<span class="font-bold text-surface-900 dark:text-white">42,000 </span>
</div>
<div class="flex items-center justify-between">
<span class="text-sm text-surface-700 dark:text-surface-200">Espacio Disponible</span>
<span class="font-bold text-primary-700 dark:text-primary-400">8,000 </span>
</div>
</div>
<Button
label="Generar Reporte Completo"
class="w-full"
outlined
@click="generateReport"
/>
</div>
</template>
</Card>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useToast } from 'primevue/usetoast';
// PrimeVue Components
import Toast from 'primevue/toast';
import Breadcrumb from 'primevue/breadcrumb';
import Card from 'primevue/card';
import Button from 'primevue/button';
import Tag from 'primevue/tag';
import TabView from 'primevue/tabview';
import TabPanel from 'primevue/tabpanel';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import Select from 'primevue/select';
import ProgressBar from 'primevue/progressbar';
const router = useRouter();
const route = useRoute();
const toast = useToast();
// Reactive State
const warehouseData = ref<any>(null);
const loading = ref(false);
const activeTab = ref(0);
const selectedCategory = ref('all');
const selectedStockLevel = ref('all');
// Breadcrumb
const breadcrumbHome = ref({ icon: 'pi pi-home', to: '/' });
const breadcrumbItems = ref([
{ label: 'Almacenes', to: '/warehouse' },
{ label: 'North Logistics Center' }
]);
// Filter Options
const categoryOptions = [
{ label: 'Todas las Categorías', value: 'all' },
{ label: 'Electrónica', value: 'electronics' },
{ label: 'Maquinaria', value: 'machinery' },
{ label: 'Textiles', value: 'textiles' },
];
const stockLevelOptions = [
{ label: 'Todos los Niveles', value: 'all' },
{ label: 'Stock Bajo', value: 'low' },
{ label: 'En Stock', value: 'in_stock' },
{ label: 'Sobrestock', value: 'overstock' },
];
// Mock Data - Current Stock
const inventoryData = ref([
{
sku: 'WH-2024-001',
product: 'Laptop Dell XPS 15',
category: 'Electrónica',
quantity: 450,
unit: 'Unidades',
location: 'Rack A-12',
status: 'En Stock',
lastUpdate: 'Hace 2 horas'
},
{
sku: 'WH-2024-002',
product: 'Silla Ergonómica Pro',
category: 'Mobiliario',
quantity: 85,
unit: 'Unidades',
location: 'Zona B-04',
status: 'Stock Bajo',
lastUpdate: 'Hace 4 horas'
},
{
sku: 'WH-2024-003',
product: 'Monitor LG 27" 4K',
category: 'Electrónica',
quantity: 320,
unit: 'Unidades',
location: 'Rack A-15',
status: 'En Stock',
lastUpdate: 'Hace 1 hora'
},
{
sku: 'WH-2024-004',
product: 'Teclado Mecánico RGB',
category: 'Periféricos',
quantity: 12,
unit: 'Unidades',
location: 'Rack C-08',
status: 'Stock Crítico',
lastUpdate: 'Hace 30 min'
},
]);
// Mock Data - Movement History
const movementHistory = ref([
{
date: '2024-01-30 14:30',
type: 'Entrada',
product: 'Laptop Dell XPS 15',
quantity: '+50',
user: 'Juan Pérez',
reference: 'PO-2024-156'
},
{
date: '2024-01-30 12:15',
type: 'Salida',
product: 'Monitor LG 27" 4K',
quantity: '-25',
user: 'María García',
reference: 'SO-2024-892'
},
{
date: '2024-01-30 10:00',
type: 'Entrada',
product: 'Silla Ergonómica Pro',
quantity: '+100',
user: 'Carlos López',
reference: 'PO-2024-155'
},
]);
// Mock Data - Recent Activities
const recentActivities = ref([
{
id: 1,
type: 'in',
action: 'Entrada de Stock',
product: 'Laptop Dell XPS 15 - 50 unidades',
quantity: '+50',
time: 'Hace 2 horas'
},
{
id: 2,
type: 'out',
action: 'Salida de Stock',
product: 'Monitor LG 27" - 25 unidades',
quantity: '-25',
time: 'Hace 4 horas'
},
{
id: 3,
type: 'in',
action: 'Entrada de Stock',
product: 'Silla Ergonómica - 100 unidades',
quantity: '+100',
time: 'Hace 6 horas'
},
]);
// Methods
const getStatusSeverity = (status: string) => {
const severityMap: Record<string, string> = {
'En Stock': 'success',
'Stock Bajo': 'warn',
'Stock Crítico': 'danger',
'Sobrestock': 'info',
};
return severityMap[status] || 'secondary';
};
const getMovementTypeSeverity = (type: string) => {
const severityMap: Record<string, string> = {
'Entrada': 'success',
'Salida': 'danger',
'Transferencia': 'info',
'Ajuste': 'warn',
};
return severityMap[type] || 'secondary';
};
const openBatchAdd = () => {
router.push({ name: 'BatchAddInventory' });
};
const viewItem = (item: any) => {
toast.add({
severity: 'info',
summary: 'Ver Item',
detail: `Visualizando: ${item.product}`,
life: 3000
});
};
const editItem = (item: any) => {
toast.add({
severity: 'info',
summary: 'Editar Item',
detail: `Editando: ${item.product}`,
life: 3000
});
};
const viewAllMovements = () => {
activeTab.value = 1;
};
const generateReport = () => {
toast.add({
severity: 'success',
summary: 'Generando Reporte',
detail: 'El reporte se está generando...',
life: 3000
});
};
// Lifecycle
onMounted(async () => {
const warehouseId = route.params.id;
loading.value = true;
try {
// TODO: Cargar datos del almacén desde el API
// const response = await warehouseService.getWarehouseById(warehouseId);
// warehouseData.value = response.data;
// Mock data por ahora
warehouseData.value = {
id: warehouseId,
name: 'North Logistics Center',
location: 'Zone A, Building 4',
isActive: true,
};
} catch (error) {
toast.add({
severity: 'error',
summary: 'Error',
detail: 'Error al cargar los datos del almacén',
life: 3000
});
} finally {
loading.value = false;
}
});
</script>
<style scoped>
.tabular-nums {
font-variant-numeric: tabular-nums;
}
</style>

View File

@ -51,6 +51,13 @@ const createWarehouse = () => {
router.push({ name: 'WarehouseCreate' });
};
const viewWarehouseDetails = (warehouse: any) => {
router.push({
name: 'WarehouseDetails',
params: { id: warehouse.id }
});
};
onMounted(async () => {
// Reload warehouses to show new data
await warehouseStore.refreshWarehouses();
@ -146,6 +153,8 @@ onMounted(async () => {
responsiveLayout="scroll"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown CurrentPageReport"
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} results"
class="cursor-pointer"
@row-click="(event) => viewWarehouseDetails(event.data)"
>
<Column field="code" header="Código" sortable>
<template #body="slotProps">
@ -186,13 +195,22 @@ onMounted(async () => {
</Column>
<Column header="Acciones" headerStyle="text-align: right" bodyStyle="text-align: right">
<template #body>
<Button
icon="pi pi-ellipsis-v"
text
rounded
@click="(event) => event.stopPropagation()"
/>
<template #body="slotProps">
<div class="flex gap-2 justify-end">
<Button
icon="pi pi-eye"
text
size="small"
@click="(event) => { event.stopPropagation(); viewWarehouseDetails(slotProps.data); }"
/>
<Button
icon="pi pi-ellipsis-v"
text
rounded
size="small"
@click="(event) => event.stopPropagation()"
/>
</div>
</template>
</Column>
</DataTable>

View File

@ -0,0 +1,337 @@
<!DOCTYPE html>
<html class="light" lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>WMS - Batch Add Inventory Items</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
"primary": "#137fec",
"background-light": "#f6f7f8",
"background-dark": "#101922",
},
fontFamily: {
"display": ["Inter", "sans-serif"]
},
borderRadius: {
"DEFAULT": "0.25rem",
"lg": "0.5rem",
"xl": "0.75rem",
"full": "9999px"
},
},
},
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark font-display text-gray-800 dark:text-gray-200 overflow-hidden">
<div class="flex h-screen w-full">
<aside class="flex w-64 flex-col border-r border-gray-200 dark:border-gray-800 bg-white dark:bg-background-dark">
<div class="flex h-full flex-col justify-between p-4">
<div class="flex flex-col gap-4">
<div class="flex items-center gap-3 p-2">
<div class="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-10" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuCztlHhjKvu2qkn2Xi2zFHagNsNToKwcTg3vQr0KtTqBCo13dK1yyz9HzB2uLCiciLyDfnrf7pREvdblPqCcUiN0HqlSbkFwY1dpQLMbJ4hmpVgHVWaLaUCMXju06qyGQSdg2ChGVcbTQIrk-RNI2-hDOFnfrI1PD89RNSsByXGRsdkYWSyEYFOFk7bT4l7aIaasB6cdVxDfNwJdvVx15wb7-qOHZHFTPMbrkkzmjGec-f7iVqTi5U1ykNDclBSezBM97TfXajTwRJE");'></div>
<div class="flex flex-col">
<h1 class="text-gray-900 dark:text-white text-base font-medium leading-normal">Admin User</h1>
<p class="text-gray-500 dark:text-gray-400 text-sm font-normal leading-normal">Warehouse Manager</p>
</div>
</div>
<nav class="flex flex-col gap-2">
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">dashboard</span>
<p class="text-sm font-medium leading-normal">Dashboard</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 rounded-lg bg-primary/10 text-primary dark:bg-primary/20" href="#">
<span class="material-symbols-outlined">warehouse</span>
<p class="text-sm font-medium leading-normal">Inventory</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">receipt_long</span>
<p class="text-sm font-medium leading-normal">Orders</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">local_shipping</span>
<p class="text-sm font-medium leading-normal">Suppliers</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">pie_chart</span>
<p class="text-sm font-medium leading-normal">Reports</p>
</a>
</nav>
</div>
<div class="flex flex-col gap-2">
<nav class="flex flex-col gap-1">
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">settings</span>
<p class="text-sm font-medium leading-normal">Settings</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">help</span>
<p class="text-sm font-medium leading-normal">Help</p>
</a>
</nav>
<button class="flex min-w-[84px] cursor-pointer items-center justify-center overflow-hidden rounded-lg h-10 px-4 bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-sm font-bold leading-normal hover:bg-gray-300 dark:hover:bg-gray-700">
<span class="truncate">Logout</span>
</button>
</div>
</div>
</aside>
<main class="flex-1 flex flex-col h-screen overflow-hidden">
<header class="flex flex-none items-center justify-between whitespace-nowrap border-b border-solid border-gray-200 dark:border-gray-800 px-10 py-3 bg-white dark:bg-background-dark z-10">
<div class="flex items-center gap-4 text-gray-900 dark:text-white">
<div class="size-6 text-primary">
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M21.435 7.182a.75.75 0 0 0-.87-.11L12 10.435 3.435 7.072a.75.75 0 0 0-.87.11.75.75 0 0 0-.11.87l2.122 7.878a.75.75 0 0 0 .869.59l6-1.635a.75.75 0 0 0 .108 0l6 1.635a.75.75 0 0 0 .87-.59l2.12-7.878a.75.75 0 0 0-.11-.87zM12 12.18l-5.693-1.55L12 4.288l5.693 6.342L12 12.18z"></path>
</svg>
</div>
<h2 class="text-lg font-bold leading-tight tracking-[-0.015em]">WMS Dashboard</h2>
</div>
<div class="flex flex-1 justify-end items-center gap-4">
<label class="relative grow max-w-sm">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-500">search</span>
<input class="form-input w-full rounded-lg border-none bg-gray-100 dark:bg-gray-800 h-10 pl-10 pr-4 text-sm text-gray-900 dark:text-white placeholder:text-gray-500 focus:ring-primary" placeholder="Search items, orders..." value=""/>
</label>
<div class="flex gap-2">
<button class="flex items-center justify-center rounded-lg h-10 w-10 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-700">
<span class="material-symbols-outlined text-xl">notifications</span>
</button>
<button class="flex items-center justify-center rounded-lg h-10 w-10 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-700">
<span class="material-symbols-outlined text-xl">help_outline</span>
</button>
</div>
<div class="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-10" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuBNpxncwdjcD3fLZbRbit_NZyuFBZhcpV-Bvdlu6NiZL3kb65hsFRsDkTNHtC0zJxG3HGV1TInl_DCfafj3axNGSwW4-UNj1sZWhiHCYE2aK9hm-FYjrNGiEh0UqKya1EAYMTM5Z4k8qKOWPEPdIaZz9X98tPC5FIn5lbRRusCTuQmgRL-QxK9SdIMA3TflImwA1vyh3zq44j8EkkNTQWf94-e82GDHs5MwHIkK0S-Gg4d950IyRTpoABSa9qXA5yPzoaT9jCGjCodL");'></div>
</div>
</header>
<div class="flex-1 overflow-y-auto p-6 lg:p-10 pb-24 relative">
<div class="mx-auto max-w-7xl">
<div class="flex flex-wrap gap-2 mb-6">
<a class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal" href="#">Dashboard</a>
<span class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal">/</span>
<a class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal" href="#">Inventory</a>
<span class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal">/</span>
<span class="text-gray-800 dark:text-gray-200 text-sm font-medium leading-normal">Batch Add Items</span>
</div>
<div class="flex flex-wrap justify-between items-center gap-4 mb-8">
<div class="flex flex-col gap-2">
<h1 class="text-gray-900 dark:text-white text-3xl font-bold tracking-tight">Add Items to Inventory</h1>
<p class="text-gray-500 dark:text-gray-400 text-base font-normal leading-normal">Search and add multiple products to your stock in a single batch.</p>
</div>
</div>
<div class="bg-white dark:bg-gray-900/50 p-6 rounded-xl border border-gray-200 dark:border-gray-800 mb-8 shadow-sm">
<h2 class="text-lg font-bold text-gray-900 dark:text-white mb-4">Select Products from Catalog</h2>
<div class="flex flex-col lg:flex-row gap-4 items-end">
<div class="flex-1 w-full relative">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Search Catalog</label>
<div class="relative">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-500">search</span>
<input class="form-input w-full rounded-lg border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-white pl-10 h-11 focus:ring-primary focus:border-primary" placeholder="Type Product Name or SKU..." type="text"/>
</div>
<div class="absolute top-full left-0 w-full mt-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-2xl z-20 p-5 grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-span-4">
<p class="text-xs font-semibold uppercase tracking-wider text-gray-400 mb-2">Selected Product</p>
<div class="flex items-center gap-3">
<div class="w-12 h-12 rounded bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
<span class="material-symbols-outlined text-gray-400">image</span>
</div>
<div>
<p class="font-bold text-gray-900 dark:text-white">Smart Display Panel</p>
<p class="text-xs text-gray-500">SKU: DISP-PNL-SD-001</p>
</div>
</div>
</div>
<div class="md:col-span-2">
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 mb-2">Medida</label>
<select class="form-select w-full rounded-lg border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-primary">
<option value="24">24</option>
<option value="36">36</option>
<option value="64">64</option>
<option value="85">85</option>
</select>
</div>
<div class="md:col-span-2">
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 mb-2">Resolución</label>
<select class="form-select w-full rounded-lg border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-primary">
<option value="1080">1080</option>
<option value="2K">2K</option>
<option value="4K">4K</option>
<option value="8K">8K</option>
</select>
</div>
<div class="md:col-span-2">
<label class="block text-xs font-semibold uppercase tracking-wider text-gray-400 mb-2">Qty</label>
<input class="form-input w-full rounded-lg border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-primary" type="number" value="10"/>
</div>
<div class="md:col-span-2 flex items-end">
<button class="w-full bg-primary text-white h-11 px-4 rounded-lg font-bold hover:bg-primary/90 flex items-center justify-center gap-1">
<span class="material-symbols-outlined text-lg">add</span> Add
</button>
</div>
</div>
</div>
</div>
</div>
<div class="bg-white dark:bg-gray-900/50 rounded-xl border border-gray-200 dark:border-gray-800 overflow-hidden shadow-sm">
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-800 flex justify-between items-center">
<h2 class="text-lg font-bold text-gray-900 dark:text-white">Queued Items (4)</h2>
<button class="text-sm font-medium text-red-500 hover:text-red-600">Clear All</button>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-gray-50 dark:bg-gray-800/50">
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Product Name</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Variant</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Quantity</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Unit</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider text-right">Action</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-gray-800">
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBMbZhSSPYoHEi3akXSqZXm-T6EghACuHJJ5NYeJ8g4XJIKtqXRhCSQ1kFcvo7Txk01ryR-r6RJ61NzPLENWMywvUVnCkQglsizZG0NKPBwRFQjXvtDULkGbGFm6EPocyHGfuzKJ0EoDr601zMBI34mfPCgn9AAaRkTMj2Ize2nCUcanlGn7QJEp6BdNcmf3JFlk-51jPTXj1-mM4Pw6AGIvarhxnZqtEAji6qRF7evVTR_56c48h5if21S3jD-wVhNVp8AltVn8KEH"/>
</div>
<span class="font-medium">Smart Display Panel</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">DISP-PNL-SD-001</td>
<td class="px-6 py-4">
<div class="flex flex-wrap gap-1">
<span class="px-2.5 py-1 text-[10px] font-semibold bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded-full border border-blue-100 dark:border-blue-800/50 uppercase tracking-tight">Medida: 85</span>
<span class="px-2.5 py-1 text-[10px] font-semibold bg-purple-50 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400 rounded-full border border-purple-100 dark:border-purple-800/50 uppercase tracking-tight">Resolución: 4K</span>
</div>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="25"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBBMBDEGh1exdBX1lU--TOCsUwfsFkYhrl8DxxvxgH-hKLkcuVtuGP_ZhRS5l_YXFWls08Ecr-Ic748cVHqHexFMMzYTPH8YL2s9OISDgsMIwcDOPxitrPUn3RBCD_krFC8SrBXBeNC8ilcFdg_JCwD5BW5gaYjAAEUMFXyM1vd-YT27KyubBk1IKATsMdDkH-NtwAHcrPgNoXsRaAcubBQiebCG1hmxKVW3fAXoDoSqywrxhwZ7cmIuo7BTThJJ1KcczxQh1jYh_w4"/>
</div>
<span class="font-medium">Mechanical Keyboard RGB</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">KYBD-RGB-US-02</td>
<td class="px-6 py-4">
<span class="px-2.5 py-1 text-xs font-medium bg-gray-100 dark:bg-gray-800 rounded-full border border-gray-200 dark:border-gray-700">Red Switch</span>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="12"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAsb9nqDON7PeKGYYAU8hmHn7B2l3waP_ewMQTpsgnpJe2vFiY-k-zncE5VEf186tDzlTwVPA6-1SgAFagugq9Anw2WXloUFTNwOrOHgAi-MNhgs0FuxRKF8XMLk6W_u6YLF5xWh3K2q317JXFSUWtURtxCi8pYM4YtHYEyuT2aIrhbAnI2LQ7ja8DvSdvpO6t1IJ6HE9RqlnAFXuLG22igxiekKT2NjRpuK5Zzu_90rBPEjb0KitG9DwiBjzKa702pccHPRqupgXke"/>
</div>
<span class="font-medium">USB-C Charging Cable 2m</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">CBL-USBC-2M</td>
<td class="px-6 py-4">
<span class="px-2.5 py-1 text-xs font-medium bg-gray-100 dark:bg-gray-800 rounded-full border border-gray-200 dark:border-gray-700">Braided</span>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="100"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuD1PtrsyrvB9KtL5DHjKotvi92lM1NsfdzyEkoW5DAEfSYoPI8MNsCJZDBMtrz6rXnIWApiymAIlp7Yq5P6JTQqpwaNJVw5I4G3KiefVME-D_jR_0iTDtPBxljmlcohkhIrSfK77wV9Wq1KL0yvjCB2UuDewIxBOs0RUl7_RbISQxYaSzVe9GbqnwI5cpj_N_1faxUA89ATStV6GovMDXFA881MUGKd4_ox_CzEG5zzPZYRe9JF7M33YH-rRHdxlpQWtRH9yw4X8sKp"/>
</div>
<span class="font-medium">Smart Display Panel</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">DISP-PNL-SD-001</td>
<td class="px-6 py-4">
<div class="flex flex-wrap gap-1">
<span class="px-2.5 py-1 text-[10px] font-semibold bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded-full border border-blue-100 dark:border-blue-800/50 uppercase tracking-tight">Medida: 36</span>
<span class="px-2.5 py-1 text-[10px] font-semibold bg-purple-50 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400 rounded-full border border-purple-100 dark:border-purple-800/50 uppercase tracking-tight">Resolución: 1080</span>
</div>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="5"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="p-6 bg-gray-50/50 dark:bg-gray-800/50 border-t border-gray-200 dark:border-gray-800 text-sm text-gray-500 dark:text-gray-400">
Total items to be added: <span class="font-bold text-gray-900 dark:text-white">142 units</span> across 4 unique products.
</div>
</div>
</div>
</div>
<footer class="fixed bottom-0 right-0 left-64 bg-white dark:bg-background-dark border-t border-gray-200 dark:border-gray-800 p-4 px-10 flex justify-between items-center z-20 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)]">
<div class="flex items-center gap-6">
<div class="flex items-center gap-2">
<span class="text-xs font-medium text-gray-500 uppercase tracking-widest">Storage Location</span>
<select class="form-select text-sm rounded-lg border-gray-300 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 focus:ring-primary h-9">
<option>Main Warehouse (A1)</option>
<option>South Storage (B4)</option>
<option>Cold Storage (C2)</option>
</select>
</div>
</div>
<div class="flex items-center gap-4">
<button class="flex min-w-[100px] cursor-pointer items-center justify-center rounded-lg h-11 px-6 bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-sm font-bold leading-normal hover:bg-gray-300 dark:hover:bg-gray-700 transition-all">
Cancel
</button>
<button class="flex min-w-[200px] cursor-pointer items-center justify-center rounded-lg h-11 px-8 bg-primary text-white text-sm font-bold leading-normal tracking-[0.015em] hover:bg-primary/90 shadow-lg shadow-primary/20 transition-all">
Confirm and Save Inventory
</button>
</div>
</footer>
</main>
</div>
</body></html>

View File

@ -0,0 +1,364 @@
<!DOCTYPE html>
<html class="light" lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>WMS - Batch Add Inventory Items</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
darkMode: "class",
theme: {
extend: {
colors: {
"primary": "#137fec",
"background-light": "#f6f7f8",
"background-dark": "#101922",
},
fontFamily: {
"display": ["Inter", "sans-serif"]
},
borderRadius: {
"DEFAULT": "0.25rem",
"lg": "0.5rem",
"xl": "0.75rem",
"full": "9999px"
},
},
},
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
</style>
</head>
<body class="bg-background-light dark:bg-background-dark font-display text-gray-800 dark:text-gray-200 overflow-hidden">
<div class="flex h-screen w-full relative">
<aside class="flex w-64 flex-col border-r border-gray-200 dark:border-gray-800 bg-white dark:bg-background-dark">
<div class="flex h-full flex-col justify-between p-4">
<div class="flex flex-col gap-4">
<div class="flex items-center gap-3 p-2">
<div class="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-10" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuCztlHhjKvu2qkn2Xi2zFHagNsNToKwcTg3vQr0KtTqBCo13dK1yyz9HzB2uLCiciLyDfnrf7pREvdblPqCcUiN0HqlSbkFwY1dpQLMbJ4hmpVgHVWaLaUCMXju06qyGQSdg2ChGVcbTQIrk-RNI2-hDOFnfrI1PD89RNSsByXGRsdkYWSyEYFOFk7bT4l7aIaasB6cdVxDfNwJdvVx15wb7-qOHZHFTPMbrkkzmjGec-f7iVqTi5U1ykNDclBSezBM97TfXajTwRJE");'></div>
<div class="flex flex-col">
<h1 class="text-gray-900 dark:text-white text-base font-medium leading-normal">Admin User</h1>
<p class="text-gray-500 dark:text-gray-400 text-sm font-normal leading-normal">Warehouse Manager</p>
</div>
</div>
<nav class="flex flex-col gap-2">
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">dashboard</span>
<p class="text-sm font-medium leading-normal">Dashboard</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 rounded-lg bg-primary/10 text-primary dark:bg-primary/20" href="#">
<span class="material-symbols-outlined">warehouse</span>
<p class="text-sm font-medium leading-normal">Inventory</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">receipt_long</span>
<p class="text-sm font-medium leading-normal">Orders</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">local_shipping</span>
<p class="text-sm font-medium leading-normal">Suppliers</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">pie_chart</span>
<p class="text-sm font-medium leading-normal">Reports</p>
</a>
</nav>
</div>
<div class="flex flex-col gap-2">
<nav class="flex flex-col gap-1">
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">settings</span>
<p class="text-sm font-medium leading-normal">Settings</p>
</a>
<a class="flex items-center gap-3 px-3 py-2 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800" href="#">
<span class="material-symbols-outlined">help</span>
<p class="text-sm font-medium leading-normal">Help</p>
</a>
</nav>
<button class="flex min-w-[84px] cursor-pointer items-center justify-center overflow-hidden rounded-lg h-10 px-4 bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-sm font-bold leading-normal hover:bg-gray-300 dark:hover:bg-gray-700">
<span class="truncate">Logout</span>
</button>
</div>
</div>
</aside>
<main class="flex-1 flex flex-col h-screen overflow-hidden">
<header class="flex flex-none items-center justify-between whitespace-nowrap border-b border-solid border-gray-200 dark:border-gray-800 px-10 py-3 bg-white dark:bg-background-dark z-10">
<div class="flex items-center gap-4 text-gray-900 dark:text-white">
<div class="size-6 text-primary">
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M21.435 7.182a.75.75 0 0 0-.87-.11L12 10.435 3.435 7.072a.75.75 0 0 0-.87.11.75.75 0 0 0-.11.87l2.122 7.878a.75.75 0 0 0 .869.59l6-1.635a.75.75 0 0 0 .108 0l6 1.635a.75.75 0 0 0 .87-.59l2.12-7.878a.75.75 0 0 0-.11-.87zM12 12.18l-5.693-1.55L12 4.288l5.693 6.342L12 12.18z"></path>
</svg>
</div>
<h2 class="text-lg font-bold leading-tight tracking-[-0.015em]">WMS Dashboard</h2>
</div>
<div class="flex flex-1 justify-end items-center gap-4">
<label class="relative grow max-w-sm">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-500">search</span>
<input class="form-input w-full rounded-lg border-none bg-gray-100 dark:bg-gray-800 h-10 pl-10 pr-4 text-sm text-gray-900 dark:text-white placeholder:text-gray-500 focus:ring-primary" placeholder="Search items, orders..." value=""/>
</label>
<div class="flex gap-2">
<button class="flex items-center justify-center rounded-lg h-10 w-10 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-700">
<span class="material-symbols-outlined text-xl">notifications</span>
</button>
<button class="flex items-center justify-center rounded-lg h-10 w-10 bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-700">
<span class="material-symbols-outlined text-xl">help_outline</span>
</button>
</div>
<div class="bg-center bg-no-repeat aspect-square bg-cover rounded-full size-10" style='background-image: url("https://lh3.googleusercontent.com/aida-public/AB6AXuBNpxncwdjcD3fLZbRbit_NZyuFBZhcpV-Bvdlu6NiZL3kb65hsFRsDkTNHtC0zJxG3HGV1TInl_DCfafj3axNGSwW4-UNj1sZWhiHCYE2aK9hm-FYjrNGiEh0UqKya1EAYMTM5Z4k8qKOWPEPdIaZz9X98tPC5FIn5lbRRusCTuQmgRL-QxK9SdIMA3TflImwA1vyh3zq44j8EkkNTQWf94-e82GDHs5MwHIkK0S-Gg4d950IyRTpoABSa9qXA5yPzoaT9jCGjCodL");'></div>
</div>
</header>
<div class="flex-1 overflow-y-auto p-6 lg:p-10 pb-24">
<div class="mx-auto max-w-7xl">
<div class="flex flex-wrap gap-2 mb-6">
<a class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal" href="#">Dashboard</a>
<span class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal">/</span>
<a class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal" href="#">Inventory</a>
<span class="text-gray-500 dark:text-gray-400 text-sm font-medium leading-normal">/</span>
<span class="text-gray-800 dark:text-gray-200 text-sm font-medium leading-normal">Batch Add Items</span>
</div>
<div class="flex flex-wrap justify-between items-center gap-4 mb-8">
<div class="flex flex-col gap-2">
<h1 class="text-gray-900 dark:text-white text-3xl font-bold tracking-tight">Add Items to Inventory</h1>
<p class="text-gray-500 dark:text-gray-400 text-base font-normal leading-normal">Manage your batch inventory addition list here.</p>
</div>
<button class="flex items-center justify-center gap-2 bg-primary text-white px-6 py-3 rounded-xl font-bold hover:bg-primary/90 transition-all shadow-lg shadow-primary/20">
<span class="material-symbols-outlined">add_circle</span>
Select Product to Add
</button>
</div>
<div class="bg-white dark:bg-gray-900/50 rounded-xl border border-gray-200 dark:border-gray-800 overflow-hidden shadow-sm">
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-800 flex justify-between items-center">
<h2 class="text-lg font-bold text-gray-900 dark:text-white">Queued Items (4)</h2>
<button class="text-sm font-medium text-red-500 hover:text-red-600">Clear All</button>
</div>
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-gray-50 dark:bg-gray-800/50">
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Product Name</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">SKU</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Variant</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Quantity</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider">Unit</th>
<th class="px-6 py-4 text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider text-right">Action</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100 dark:divide-gray-800">
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBMbZhSSPYoHEi3akXSqZXm-T6EghACuHJJ5NYeJ8g4XJIKtqXRhCSQ1kFcvo7Txk01ryR-r6RJ61NzPLENWMywvUVnCkQglsizZG0NKPBwRFQjXvtDULkGbGFm6EPocyHGfuzKJ0EoDr601zMBI34mfPCgn9AAaRkTMj2Ize2nCUcanlGn7QJEp6BdNcmf3JFlk-51jPTXj1-mM4Pw6AGIvarhxnZqtEAji6qRF7evVTR_56c48h5if21S3jD-wVhNVp8AltVn8KEH"/>
</div>
<span class="font-medium">Smart Display Panel</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">DISP-PNL-SD-001</td>
<td class="px-6 py-4">
<div class="flex flex-wrap gap-1">
<span class="px-2.5 py-1 text-[10px] font-semibold bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded-full border border-blue-100 dark:border-blue-800/50 uppercase tracking-tight">Medida: 85</span>
<span class="px-2.5 py-1 text-[10px] font-semibold bg-purple-50 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400 rounded-full border border-purple-100 dark:border-purple-800/50 uppercase tracking-tight">Resolución: 4K</span>
</div>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="25"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBBMBDEGh1exdBX1lU--TOCsUwfsFkYhrl8DxxvxgH-hKLkcuVtuGP_ZhRS5l_YXFWls08Ecr-Ic748cVHqHexFMMzYTPH8YL2s9OISDgsMIwcDOPxitrPUn3RBCD_krFC8SrBXBeNC8ilcFdg_JCwD5BW5gaYjAAEUMFXyM1vd-YT27KyubBk1IKATsMdDkH-NtwAHcrPgNoXsRaAcubBQiebCG1hmxKVW3fAXoDoSqywrxhwZ7cmIuo7BTThJJ1KcczxQh1jYh_w4"/>
</div>
<span class="font-medium">Mechanical Keyboard RGB</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">KYBD-RGB-US-02</td>
<td class="px-6 py-4">
<span class="px-2.5 py-1 text-xs font-medium bg-gray-100 dark:bg-gray-800 rounded-full border border-gray-200 dark:border-gray-700">Red Switch</span>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="12"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAsb9nqDON7PeKGYYAU8hmHn7B2l3waP_ewMQTpsgnpJe2vFiY-k-zncE5VEf186tDzlTwVPA6-1SgAFagugq9Anw2WXloUFTNwOrOHgAi-MNhgs0FuxRKF8XMLk6W_u6YLF5xWh3K2q317JXFSUWtURtxCi8pYM4YtHYEyuT2aIrhbAnI2LQ7ja8DvSdvpO6t1IJ6HE9RqlnAFXuLG22igxiekKT2NjRpuK5Zzu_90rBPEjb0KitG9DwiBjzKa702pccHPRqupgXke"/>
</div>
<span class="font-medium">USB-C Charging Cable 2m</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">CBL-USBC-2M</td>
<td class="px-6 py-4">
<span class="px-2.5 py-1 text-xs font-medium bg-gray-100 dark:bg-gray-800 rounded-full border border-gray-200 dark:border-gray-700">Braided</span>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="100"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
<tr class="hover:bg-gray-50/50 dark:hover:bg-gray-800/30 transition-colors">
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded bg-gray-100 dark:bg-gray-800 overflow-hidden">
<img alt="product" class="w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida-public/AB6AXuD1PtrsyrvB9KtL5DHjKotvi92lM1NsfdzyEkoW5DAEfSYoPI8MNsCJZDBMtrz6rXnIWApiymAIlp7Yq5P6JTQqpwaNJVw5I4G3KiefVME-D_jR_0iTDtPBxljmlcohkhIrSfK77wV9Wq1KL0yvjCB2UuDewIxBOs0RUl7_RbISQxYaSzVe9GbqnwI5cpj_N_1faxUA89ATStV6GovMDXFA881MUGKd4_ox_CzEG5zzPZYRe9JF7M33YH-rRHdxlpQWtRH9yw4X8sKp"/>
</div>
<span class="font-medium">Smart Display Panel</span>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500">DISP-PNL-SD-001</td>
<td class="px-6 py-4">
<div class="flex flex-wrap gap-1">
<span class="px-2.5 py-1 text-[10px] font-semibold bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400 rounded-full border border-blue-100 dark:border-blue-800/50 uppercase tracking-tight">Medida: 36</span>
<span class="px-2.5 py-1 text-[10px] font-semibold bg-purple-50 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400 rounded-full border border-purple-100 dark:border-purple-800/50 uppercase tracking-tight">Resolución: 1080</span>
</div>
</td>
<td class="px-6 py-4">
<input class="w-20 form-input h-8 rounded border-gray-300 dark:border-gray-700 bg-transparent text-sm" type="number" value="5"/>
</td>
<td class="px-6 py-4 text-sm">pcs</td>
<td class="px-6 py-4 text-right">
<button class="text-gray-400 hover:text-red-500 transition-colors">
<span class="material-symbols-outlined text-xl">delete</span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="p-6 bg-gray-50/50 dark:bg-gray-800/50 border-t border-gray-200 dark:border-gray-800 text-sm text-gray-500 dark:text-gray-400">
Total items to be added: <span class="font-bold text-gray-900 dark:text-white">142 units</span> across 4 unique products.
</div>
</div>
</div>
</div>
<footer class="fixed bottom-0 right-0 left-64 bg-white dark:bg-background-dark border-t border-gray-200 dark:border-gray-800 p-4 px-10 flex justify-between items-center z-20 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.1)]">
<div class="flex items-center gap-6">
<div class="flex items-center gap-2">
<span class="text-xs font-medium text-gray-500 uppercase tracking-widest">Storage Location</span>
<select class="form-select text-sm rounded-lg border-gray-300 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 focus:ring-primary h-9">
<option>Main Warehouse (A1)</option>
<option>South Storage (B4)</option>
<option>Cold Storage (C2)</option>
</select>
</div>
</div>
<div class="flex items-center gap-4">
<button class="flex min-w-[100px] cursor-pointer items-center justify-center rounded-lg h-11 px-6 bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200 text-sm font-bold leading-normal hover:bg-gray-300 dark:hover:bg-gray-700 transition-all">
Cancel
</button>
<button class="flex min-w-[200px] cursor-pointer items-center justify-center rounded-lg h-11 px-8 bg-primary text-white text-sm font-bold leading-normal tracking-[0.015em] hover:bg-primary/90 shadow-lg shadow-primary/20 transition-all">
Confirm and Save Inventory
</button>
</div>
</footer>
</main>
<div class="fixed inset-0 z-100 flex items-center justify-center">
<div class="absolute inset-0 bg-gray-900/60 backdrop-blur-sm"></div>
<div class="relative bg-white dark:bg-background-dark w-full max-w-2xl mx-4 rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-800 overflow-hidden flex flex-col max-h-[90vh]">
<div class="p-6 border-b border-gray-100 dark:border-gray-800 flex justify-between items-center">
<div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Add Product to Batch</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Search and configure the product variant</p>
</div>
<button class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors">
<span class="material-symbols-outlined text-2xl">close</span>
</button>
</div>
<div class="p-6 overflow-y-auto space-y-8">
<div class="space-y-3">
<label class="block text-sm font-semibold text-gray-700 dark:text-gray-300">Search Catalog</label>
<div class="relative">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-500">search</span>
<input class="form-input w-full rounded-xl border-gray-300 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 text-gray-900 dark:text-white pl-11 h-12 focus:ring-primary focus:border-primary" placeholder="Type Product Name or SKU..." type="text" value="Smart Display Panel"/>
</div>
</div>
<div class="bg-gray-50 dark:bg-gray-800/30 rounded-xl p-5 border border-gray-100 dark:border-gray-700 space-y-6">
<div class="flex items-center gap-4">
<div class="w-16 h-16 rounded-lg bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 flex items-center justify-center shadow-sm">
<span class="material-symbols-outlined text-3xl text-gray-400">image</span>
</div>
<div>
<p class="text-lg font-bold text-gray-900 dark:text-white">Smart Display Panel</p>
<p class="text-sm text-gray-500 font-medium tracking-tight">SKU: DISP-PNL-SD-001</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label class="block text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400">Medida</label>
<select class="form-select w-full rounded-lg border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-primary focus:border-primary h-11">
<option value="24">24</option>
<option value="36">36</option>
<option value="64">64</option>
<option selected="" value="85">85</option>
</select>
</div>
<div class="space-y-2">
<label class="block text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400">Resolución</label>
<select class="form-select w-full rounded-lg border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-primary focus:border-primary h-11">
<option value="1080">1080</option>
<option value="2K">2K</option>
<option selected="" value="4K">4K</option>
<option value="8K">8K</option>
</select>
</div>
<div class="space-y-2">
<label class="block text-xs font-bold uppercase tracking-wider text-gray-500 dark:text-gray-400">Quantity</label>
<div class="flex items-center gap-2">
<input class="form-input flex-1 rounded-lg border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-sm focus:ring-primary focus:border-primary h-11" type="number" value="10"/>
<span class="text-sm font-medium text-gray-500">pcs</span>
</div>
</div>
<div class="flex items-end pb-1">
<div class="bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 px-3 py-2 rounded-lg border border-blue-100 dark:border-blue-800/50 w-full flex items-center gap-2">
<span class="material-symbols-outlined text-lg">info</span>
<span class="text-xs font-medium">Current Stock: 420 units</span>
</div>
</div>
</div>
</div>
</div>
<div class="p-6 bg-gray-50 dark:bg-gray-800/50 border-t border-gray-100 dark:border-gray-800 flex justify-end gap-3">
<button class="px-5 py-2.5 rounded-lg text-sm font-bold text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors">
Cancel
</button>
<button class="bg-primary text-white px-8 py-2.5 rounded-lg text-sm font-bold hover:bg-primary/90 transition-all shadow-lg shadow-primary/20 flex items-center gap-2">
<span class="material-symbols-outlined text-lg">add_task</span>
Confirm and Add to List
</button>
</div>
</div>
</div>
</div>
</body></html>

View File

@ -6,6 +6,8 @@ import Login from '../modules/auth/components/Login.vue';
import MainLayout from '../MainLayout.vue';
import WarehouseIndex from '../modules/warehouse/components/WarehouseIndex.vue';
import WarehouseForm from '../modules/warehouse/components/WarehouseForm.vue';
import WarehouseDetails from '../modules/warehouse/components/WarehouseDetails.vue';
import BatchAddInventory from '../modules/warehouse/components/BatchAddInventory.vue';
import WarehouseClassification from '../modules/warehouse/components/WarehouseClassification.vue';
import UnitOfMeasure from '../modules/catalog/components/UnitOfMeasure.vue';
@ -71,6 +73,15 @@ const routes: RouteRecordRaw[] = [
requiresAuth: true
}
},
{
path: ':id',
name: 'WarehouseDetails',
component: WarehouseDetails,
meta: {
title: 'Detalles del Almacén',
requiresAuth: true
}
},
{
path: 'classifications',
name: 'WarehouseClassifications',
@ -80,6 +91,15 @@ const routes: RouteRecordRaw[] = [
requiresAuth: true
}
},
{
path: 'batch-add',
name: 'BatchAddInventory',
component: BatchAddInventory,
meta: {
title: 'Agregar Items al Inventario',
requiresAuth: true
}
},
{
path: 'inventory',
name: 'WarehouseInventory',