feat(auth): implement permission checks for product management and sidebar menu items
This commit is contained in:
parent
1cf8cc3aa5
commit
97b91013dd
@ -68,7 +68,14 @@ const menuItems = ref<MenuItem[]>([
|
|||||||
{
|
{
|
||||||
label: 'Productos',
|
label: 'Productos',
|
||||||
icon: 'pi pi-shopping-cart',
|
icon: 'pi pi-shopping-cart',
|
||||||
to: '/products'
|
to: '/products',
|
||||||
|
permission: [
|
||||||
|
'products.index',
|
||||||
|
'products.show',
|
||||||
|
'products.store',
|
||||||
|
'products.update',
|
||||||
|
'products.destroy',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Requisiciones',
|
label: 'Requisiciones',
|
||||||
|
|||||||
@ -1,449 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<html class="light" lang="es">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
|
||||||
<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&display=swap"
|
|
||||||
rel="stylesheet" />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
|
||||||
rel="stylesheet" />
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
|
||||||
rel="stylesheet" />
|
|
||||||
<script id="tailwind-config">
|
|
||||||
tailwind.config = {
|
|
||||||
darkMode: "class",
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
"outline-variant": "#c3c6d7",
|
|
||||||
"tertiary-fixed": "#ffdbcd",
|
|
||||||
"on-secondary-fixed": "#00174b",
|
|
||||||
"on-tertiary": "#ffffff",
|
|
||||||
"on-primary": "#ffffff",
|
|
||||||
"error-container": "#ffdad6",
|
|
||||||
"on-error": "#ffffff",
|
|
||||||
"inverse-on-surface": "#f0f0fb",
|
|
||||||
"on-secondary": "#ffffff",
|
|
||||||
"on-secondary-container": "#394c84",
|
|
||||||
"surface-container": "#ededf9",
|
|
||||||
"tertiary-container": "#bc4800",
|
|
||||||
"on-tertiary-fixed-variant": "#7d2d00",
|
|
||||||
"on-surface": "#191b23",
|
|
||||||
"on-tertiary-fixed": "#360f00",
|
|
||||||
"outline": "#737686",
|
|
||||||
"secondary-fixed-dim": "#b4c5ff",
|
|
||||||
"surface-tint": "#0053db",
|
|
||||||
"surface-container-low": "#f3f3fe",
|
|
||||||
"background": "#faf8ff",
|
|
||||||
"inverse-primary": "#b4c5ff",
|
|
||||||
"on-primary-fixed": "#00174b",
|
|
||||||
"on-tertiary-container": "#ffede6",
|
|
||||||
"tertiary": "#943700",
|
|
||||||
"primary-fixed": "#dbe1ff",
|
|
||||||
"surface-container-high": "#e7e7f3",
|
|
||||||
"on-surface-variant": "#434655",
|
|
||||||
"on-secondary-fixed-variant": "#31447b",
|
|
||||||
"surface-container-lowest": "#ffffff",
|
|
||||||
"on-error-container": "#93000a",
|
|
||||||
"surface-dim": "#d9d9e5",
|
|
||||||
"inverse-surface": "#2e3039",
|
|
||||||
"surface-container-highest": "#e1e2ed",
|
|
||||||
"primary": "#004ac6",
|
|
||||||
"secondary-container": "#acbfff",
|
|
||||||
"surface-bright": "#faf8ff",
|
|
||||||
"on-background": "#191b23",
|
|
||||||
"primary-container": "#2563eb",
|
|
||||||
"error": "#ba1a1a",
|
|
||||||
"secondary": "#495c95",
|
|
||||||
"secondary-fixed": "#dbe1ff",
|
|
||||||
"surface": "#faf8ff",
|
|
||||||
"on-primary-container": "#eeefff",
|
|
||||||
"primary-fixed-dim": "#b4c5ff",
|
|
||||||
"on-primary-fixed-variant": "#003ea8",
|
|
||||||
"tertiary-fixed-dim": "#ffb596",
|
|
||||||
"surface-variant": "#e1e2ed"
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
"headline": ["Inter"],
|
|
||||||
"body": ["Inter"],
|
|
||||||
"label": ["Inter"]
|
|
||||||
},
|
|
||||||
borderRadius: { "DEFAULT": "0.125rem", "lg": "0.25rem", "xl": "0.5rem", "full": "0.75rem" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
.material-symbols-outlined {
|
|
||||||
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glass-panel {
|
|
||||||
background: rgba(250, 248, 255, 0.8);
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
-webkit-backdrop-filter: blur(12px);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="bg-background text-on-surface min-h-screen flex">
|
|
||||||
<!-- SideNavBar Shell -->
|
|
||||||
<aside class="h-screen w-64 fixed left-0 top-0 z-50 bg-[#ffffff] dark:bg-slate-900 flex flex-col p-4 gap-2">
|
|
||||||
<div class="mb-8 px-2">
|
|
||||||
<h1 class="text-lg font-bold text-[#191b23] dark:text-white">The Precise Curator</h1>
|
|
||||||
<p class="text-xs text-slate-500 font-medium uppercase tracking-wider">Management Suite</p>
|
|
||||||
</div>
|
|
||||||
<nav class="flex-grow space-y-1">
|
|
||||||
<a class="flex items-center gap-3 px-3 py-2 text-slate-500 hover:text-[#004ac6] hover:bg-[#ededf9] rounded-lg transition-all font-medium text-sm"
|
|
||||||
href="#">
|
|
||||||
<span class="material-symbols-outlined" data-icon="dashboard">dashboard</span>
|
|
||||||
<span>Dashboard</span>
|
|
||||||
</a>
|
|
||||||
<a class="flex items-center gap-3 px-3 py-2 bg-[#f3f3fe] text-[#004ac6] font-semibold rounded-lg text-sm"
|
|
||||||
href="#">
|
|
||||||
<span class="material-symbols-outlined" data-icon="inventory_2">inventory_2</span>
|
|
||||||
<span>Warehouse</span>
|
|
||||||
</a>
|
|
||||||
<a class="flex items-center gap-3 px-3 py-2 text-slate-500 hover:text-[#004ac6] hover:bg-[#ededf9] rounded-lg transition-all font-medium text-sm"
|
|
||||||
href="#">
|
|
||||||
<span class="material-symbols-outlined" data-icon="inventory">inventory</span>
|
|
||||||
<span>Products</span>
|
|
||||||
</a>
|
|
||||||
<a class="flex items-center gap-3 px-3 py-2 text-slate-500 hover:text-[#004ac6] hover:bg-[#ededf9] rounded-lg transition-all font-medium text-sm"
|
|
||||||
href="#">
|
|
||||||
<span class="material-symbols-outlined" data-icon="group">group</span>
|
|
||||||
<span>HR</span>
|
|
||||||
</a>
|
|
||||||
<a class="flex items-center gap-3 px-3 py-2 text-slate-500 hover:text-[#004ac6] hover:bg-[#ededf9] rounded-lg transition-all font-medium text-sm"
|
|
||||||
href="#">
|
|
||||||
<span class="material-symbols-outlined" data-icon="settings">settings</span>
|
|
||||||
<span>Settings</span>
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
<div class="mt-auto pt-4 border-t border-surface-container space-y-1">
|
|
||||||
<a class="flex items-center gap-3 px-3 py-2 text-slate-500 hover:text-[#004ac6] hover:bg-[#ededf9] rounded-lg transition-all font-medium text-sm"
|
|
||||||
href="#">
|
|
||||||
<span class="material-symbols-outlined" data-icon="contact_support">contact_support</span>
|
|
||||||
<span>Support</span>
|
|
||||||
</a>
|
|
||||||
<a class="flex items-center gap-3 px-3 py-2 text-slate-500 hover:text-[#004ac6] hover:bg-[#ededf9] rounded-lg transition-all font-medium text-sm"
|
|
||||||
href="#">
|
|
||||||
<span class="material-symbols-outlined" data-icon="logout">logout</span>
|
|
||||||
<span>Logout</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
<!-- Main Content Area -->
|
|
||||||
<main class="ml-64 flex-grow flex flex-col min-h-screen">
|
|
||||||
<!-- TopAppBar Shell -->
|
|
||||||
<header class="w-full h-16 sticky top-0 z-40 bg-[#faf8ff] flex justify-between items-center px-8">
|
|
||||||
<div class="flex items-center gap-8">
|
|
||||||
<span class="text-xl font-bold text-[#004ac6]">Architectural ERP</span>
|
|
||||||
<div class="hidden md:flex gap-6 items-center">
|
|
||||||
<a class="text-slate-600 font-medium hover:text-[#004ac6] transition-colors text-sm"
|
|
||||||
href="#">Inventario</a>
|
|
||||||
<a class="text-[#004ac6] font-bold border-b-2 border-[#004ac6] text-sm py-5"
|
|
||||||
href="#">Configuración</a>
|
|
||||||
<a class="text-slate-600 font-medium hover:text-[#004ac6] transition-colors text-sm"
|
|
||||||
href="#">Reportes</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-4">
|
|
||||||
<button class="p-2 text-slate-600 hover:bg-slate-100 rounded-full transition-colors">
|
|
||||||
<span class="material-symbols-outlined" data-icon="notifications">notifications</span>
|
|
||||||
</button>
|
|
||||||
<button class="p-2 text-slate-600 hover:bg-slate-100 rounded-full transition-colors">
|
|
||||||
<span class="material-symbols-outlined" data-icon="help_outline">help_outline</span>
|
|
||||||
</button>
|
|
||||||
<div class="h-8 w-8 rounded-full overflow-hidden bg-primary-container flex items-center justify-center">
|
|
||||||
<img alt="User Profile Avatar"
|
|
||||||
data-alt="professional headshot of a middle-aged architect in a minimalist workspace with soft natural lighting"
|
|
||||||
src="https://lh3.googleusercontent.com/aida-public/AB6AXuBWBuLM7PNHJEV0FZ_f5K3-vmg--FVfXa5_kQAbfarkJeDejtpvXC6TJQTC2geDqYJDl8GYz3QSAA1HpQr3fYwelTpFgvnSQTUPRXSgK-H38In03m51WsW1PP00_4cdEErO6dgAGN_BVReJr3-L6eKpYOiA8BZmDKerxWs4YYgffqz9bmIVybVi1bin083ZBZy0LgQGaDVCru_hs46OonyQHM4qWbyYgmVUTRcCaevGZ68ag6vo2eCXt5fwFknLVpplpZd-ccf0L26n" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<!-- Canvas -->
|
|
||||||
<div class="p-8 space-y-8 max-w-7xl mx-auto w-full">
|
|
||||||
<!-- Header Section -->
|
|
||||||
<div class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
|
||||||
<div class="space-y-1">
|
|
||||||
<h2 class="text-3xl font-bold text-on-surface tracking-tight">Gestión de Equivalencias</h2>
|
|
||||||
<p class="text-on-surface-variant text-sm">Configura las relaciones entre diferentes unidades de
|
|
||||||
medida para el control de stock.</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
class="bg-gradient-to-br from-primary to-primary-container text-white px-5 py-2.5 rounded-md flex items-center gap-2 font-semibold shadow-sm hover:opacity-90 transition-all active:scale-95">
|
|
||||||
<span class="material-symbols-outlined text-lg" data-icon="add">add</span>
|
|
||||||
Nueva Equivalencia
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- Dashboard Layout (Bento Style) -->
|
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
|
||||||
<!-- Filters & Search Bento Box -->
|
|
||||||
<div class="lg:col-span-12 bg-surface-container-lowest p-6 rounded-xl space-y-6">
|
|
||||||
<div class="flex flex-col md:flex-row gap-4">
|
|
||||||
<div class="flex-grow relative">
|
|
||||||
<span
|
|
||||||
class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-outline"
|
|
||||||
data-icon="search">search</span>
|
|
||||||
<input
|
|
||||||
class="w-full pl-10 pr-4 py-2 bg-surface-container-low border-b border-outline-variant focus:border-primary focus:ring-0 transition-all text-sm outline-none"
|
|
||||||
placeholder="Buscar unidades..." type="text" />
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 items-center overflow-x-auto pb-2 md:pb-0">
|
|
||||||
<button
|
|
||||||
class="bg-secondary-container text-on-secondary-container px-4 py-1.5 rounded-full text-xs font-bold whitespace-nowrap">Todas</button>
|
|
||||||
<button
|
|
||||||
class="bg-surface-container-low text-on-surface-variant hover:bg-surface-container-high px-4 py-1.5 rounded-full text-xs font-semibold transition-colors whitespace-nowrap">Peso</button>
|
|
||||||
<button
|
|
||||||
class="bg-surface-container-low text-on-surface-variant hover:bg-surface-container-high px-4 py-1.5 rounded-full text-xs font-semibold transition-colors whitespace-nowrap">Volumen</button>
|
|
||||||
<button
|
|
||||||
class="bg-surface-container-low text-on-surface-variant hover:bg-surface-container-high px-4 py-1.5 rounded-full text-xs font-semibold transition-colors whitespace-nowrap">Longitud</button>
|
|
||||||
<button
|
|
||||||
class="bg-surface-container-low text-on-surface-variant hover:bg-surface-container-high px-4 py-1.5 rounded-full text-xs font-semibold transition-colors whitespace-nowrap">Unidades</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Main Table Section -->
|
|
||||||
<div class="lg:col-span-8 bg-surface-container-lowest rounded-xl overflow-hidden">
|
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table class="w-full text-left border-collapse">
|
|
||||||
<thead>
|
|
||||||
<tr class="bg-surface-container-high">
|
|
||||||
<th
|
|
||||||
class="px-6 py-4 text-[10px] uppercase font-bold text-on-surface-variant tracking-widest">
|
|
||||||
Unidad Origen</th>
|
|
||||||
<th
|
|
||||||
class="px-6 py-4 text-[10px] uppercase font-bold text-on-surface-variant tracking-widest text-center">
|
|
||||||
Operador</th>
|
|
||||||
<th
|
|
||||||
class="px-6 py-4 text-[10px] uppercase font-bold text-on-surface-variant tracking-widest text-center">
|
|
||||||
Factor</th>
|
|
||||||
<th
|
|
||||||
class="px-6 py-4 text-[10px] uppercase font-bold text-on-surface-variant tracking-widest">
|
|
||||||
Unidad Destino</th>
|
|
||||||
<th
|
|
||||||
class="px-6 py-4 text-[10px] uppercase font-bold text-on-surface-variant tracking-widest text-right">
|
|
||||||
Acciones</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="divide-y-2 divide-surface">
|
|
||||||
<tr
|
|
||||||
class="bg-surface-container-lowest hover:bg-surface-container-low transition-colors group">
|
|
||||||
<td class="px-6 py-4">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
class="h-8 w-8 bg-surface-container-low flex items-center justify-center rounded text-primary">
|
|
||||||
<span class="material-symbols-outlined text-sm"
|
|
||||||
data-icon="inventory_2">inventory_2</span>
|
|
||||||
</div>
|
|
||||||
<span class="font-semibold text-sm">Caja (Master)</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-center font-bold text-primary">x</td>
|
|
||||||
<td class="px-6 py-4 text-center font-mono text-sm">12.00</td>
|
|
||||||
<td class="px-6 py-4">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span
|
|
||||||
class="bg-secondary-container/30 text-secondary px-2 py-1 rounded text-xs font-bold">Pieza</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-right">
|
|
||||||
<div
|
|
||||||
class="flex justify-end gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
||||||
<button
|
|
||||||
class="p-2 text-primary hover:bg-primary-fixed rounded-lg transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-lg"
|
|
||||||
data-icon="edit">edit</span></button>
|
|
||||||
<button
|
|
||||||
class="p-2 text-error hover:bg-error-container rounded-lg transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-lg"
|
|
||||||
data-icon="delete">delete</span></button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr
|
|
||||||
class="bg-surface-container-low hover:bg-surface-container-high transition-colors group">
|
|
||||||
<td class="px-6 py-4">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
class="h-8 w-8 bg-surface-container-lowest flex items-center justify-center rounded text-primary">
|
|
||||||
<span class="material-symbols-outlined text-sm"
|
|
||||||
data-icon="scale">scale</span>
|
|
||||||
</div>
|
|
||||||
<span class="font-semibold text-sm">Kilogramo</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-center font-bold text-primary">/</td>
|
|
||||||
<td class="px-6 py-4 text-center font-mono text-sm">1000.00</td>
|
|
||||||
<td class="px-6 py-4">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span
|
|
||||||
class="bg-secondary-container/30 text-secondary px-2 py-1 rounded text-xs font-bold">Gramo</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-right">
|
|
||||||
<div
|
|
||||||
class="flex justify-end gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
||||||
<button
|
|
||||||
class="p-2 text-primary hover:bg-primary-fixed rounded-lg transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-lg"
|
|
||||||
data-icon="edit">edit</span></button>
|
|
||||||
<button
|
|
||||||
class="p-2 text-error hover:bg-error-container rounded-lg transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-lg"
|
|
||||||
data-icon="delete">delete</span></button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr
|
|
||||||
class="bg-surface-container-lowest hover:bg-surface-container-low transition-colors group">
|
|
||||||
<td class="px-6 py-4">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
class="h-8 w-8 bg-surface-container-low flex items-center justify-center rounded text-primary">
|
|
||||||
<span class="material-symbols-outlined text-sm"
|
|
||||||
data-icon="widgets">widgets</span>
|
|
||||||
</div>
|
|
||||||
<span class="font-semibold text-sm">Pallet</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-center font-bold text-primary">x</td>
|
|
||||||
<td class="px-6 py-4 text-center font-mono text-sm">48.00</td>
|
|
||||||
<td class="px-6 py-4">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span
|
|
||||||
class="bg-secondary-container/30 text-secondary px-2 py-1 rounded text-xs font-bold">Caja</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="px-6 py-4 text-right">
|
|
||||||
<div
|
|
||||||
class="flex justify-end gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
||||||
<button
|
|
||||||
class="p-2 text-primary hover:bg-primary-fixed rounded-lg transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-lg"
|
|
||||||
data-icon="edit">edit</span></button>
|
|
||||||
<button
|
|
||||||
class="p-2 text-error hover:bg-error-container rounded-lg transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-lg"
|
|
||||||
data-icon="delete">delete</span></button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- Pagination -->
|
|
||||||
<div class="px-6 py-4 bg-surface-container-low flex items-center justify-between">
|
|
||||||
<span class="text-xs text-on-surface-variant">Mostrando 1 a 3 de 24 entradas</span>
|
|
||||||
<div class="flex gap-1">
|
|
||||||
<button
|
|
||||||
class="p-1 text-on-surface-variant hover:bg-surface-container-highest rounded transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-sm"
|
|
||||||
data-icon="chevron_left">chevron_left</span></button>
|
|
||||||
<button class="px-2.5 py-1 text-xs font-bold bg-primary text-white rounded">1</button>
|
|
||||||
<button
|
|
||||||
class="px-2.5 py-1 text-xs font-semibold text-on-surface-variant hover:bg-surface-container-highest rounded transition-colors">2</button>
|
|
||||||
<button
|
|
||||||
class="px-2.5 py-1 text-xs font-semibold text-on-surface-variant hover:bg-surface-container-highest rounded transition-colors">3</button>
|
|
||||||
<button
|
|
||||||
class="p-1 text-on-surface-variant hover:bg-surface-container-highest rounded transition-colors"><span
|
|
||||||
class="material-symbols-outlined text-sm"
|
|
||||||
data-icon="chevron_right">chevron_right</span></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Form Bento Box -->
|
|
||||||
<div class="lg:col-span-4 space-y-6">
|
|
||||||
<div
|
|
||||||
class="bg-surface-container-lowest p-6 rounded-xl shadow-sm border border-outline-variant border-opacity-10">
|
|
||||||
<div class="flex items-center gap-3 mb-6">
|
|
||||||
<span class="material-symbols-outlined text-primary" data-icon="add_box">add_box</span>
|
|
||||||
<h3 class="text-sm font-bold text-on-surface uppercase tracking-tight">Agregar Nueva
|
|
||||||
Equivalencia</h3>
|
|
||||||
</div>
|
|
||||||
<form class="space-y-6">
|
|
||||||
<div class="space-y-1.5">
|
|
||||||
<label class="text-[10px] font-bold text-on-surface-variant uppercase">Unidad
|
|
||||||
Base</label>
|
|
||||||
<select
|
|
||||||
class="w-full bg-surface-container-low border-b border-outline-variant py-2 text-sm focus:border-primary outline-none transition-all appearance-none">
|
|
||||||
<option>Seleccionar unidad...</option>
|
|
||||||
<option>Caja</option>
|
|
||||||
<option>Kilogramo</option>
|
|
||||||
<option>Litro</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-2 gap-4">
|
|
||||||
<div class="space-y-1.5">
|
|
||||||
<label
|
|
||||||
class="text-[10px] font-bold text-on-surface-variant uppercase">Operador</label>
|
|
||||||
<select
|
|
||||||
class="w-full bg-surface-container-low border-b border-outline-variant py-2 text-sm focus:border-primary outline-none transition-all appearance-none">
|
|
||||||
<option>Multiplicar (x)</option>
|
|
||||||
<option>Dividir (/)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="space-y-1.5">
|
|
||||||
<label
|
|
||||||
class="text-[10px] font-bold text-on-surface-variant uppercase">Factor</label>
|
|
||||||
<input
|
|
||||||
class="w-full bg-surface-container-low border-b border-outline-variant py-2 text-sm focus:border-primary outline-none transition-all"
|
|
||||||
placeholder="1.00" type="number" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="space-y-1.5">
|
|
||||||
<label class="text-[10px] font-bold text-on-surface-variant uppercase">Unidad
|
|
||||||
Destino</label>
|
|
||||||
<select
|
|
||||||
class="w-full bg-surface-container-low border-b border-outline-variant py-2 text-sm focus:border-primary outline-none transition-all appearance-none">
|
|
||||||
<option>Seleccionar unidad...</option>
|
|
||||||
<option>Pieza</option>
|
|
||||||
<option>Gramo</option>
|
|
||||||
<option>Mililitro</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="pt-4 flex gap-3">
|
|
||||||
<button
|
|
||||||
class="flex-grow bg-surface-container-high text-on-surface-variant font-semibold py-2 rounded-md hover:bg-surface-dim transition-colors text-sm"
|
|
||||||
type="button">Cancelar</button>
|
|
||||||
<button
|
|
||||||
class="flex-grow bg-primary text-white font-semibold py-2 rounded-md hover:opacity-90 transition-all text-sm shadow-sm"
|
|
||||||
type="submit">Guardar</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- Quick Insights Bento -->
|
|
||||||
<div
|
|
||||||
class="bg-gradient-to-br from-secondary-container to-primary-container p-6 rounded-xl text-on-secondary-container">
|
|
||||||
<div class="flex items-center justify-between mb-4">
|
|
||||||
<span class="text-[10px] font-bold uppercase tracking-widest opacity-80">Info Rápida</span>
|
|
||||||
<span class="material-symbols-outlined text-lg" data-icon="info">info</span>
|
|
||||||
</div>
|
|
||||||
<p class="text-sm font-medium leading-relaxed">
|
|
||||||
Asegúrese de que las unidades base correspondan a las medidas de inventario físico para
|
|
||||||
mantener la precisión en los conteos cíclicos.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- FAB for Mobile (Contextual suppression on larger screens as per logic) -->
|
|
||||||
<button
|
|
||||||
class="md:hidden fixed bottom-6 right-6 h-14 w-14 bg-primary text-white rounded-full shadow-2xl flex items-center justify-center z-50">
|
|
||||||
<span class="material-symbols-outlined" data-icon="add">add</span>
|
|
||||||
</button>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@ -10,6 +10,10 @@ import { useComercialClassificationStore } from '../../catalog/stores/comercialC
|
|||||||
import { useUnitOfMeasureStore } from '../../catalog/stores/unitOfMeasureStore';
|
import { useUnitOfMeasureStore } from '../../catalog/stores/unitOfMeasureStore';
|
||||||
import { satCodeProductsService, type SatCodeProduct } from '../../catalog/services/sat-code-products.services';
|
import { satCodeProductsService, type SatCodeProduct } from '../../catalog/services/sat-code-products.services';
|
||||||
import type { Product, CreateProductData } from '../types/product';
|
import type { Product, CreateProductData } from '../types/product';
|
||||||
|
import { useAuth } from '@/modules/auth/composables/useAuth';
|
||||||
|
|
||||||
|
// Auth/Permissions
|
||||||
|
const { hasPermission } = useAuth();
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -32,6 +36,11 @@ const emit = defineEmits<{
|
|||||||
const clasificationsStore = useComercialClassificationStore();
|
const clasificationsStore = useComercialClassificationStore();
|
||||||
const unitOfMeasureStore = useUnitOfMeasureStore();
|
const unitOfMeasureStore = useUnitOfMeasureStore();
|
||||||
|
|
||||||
|
// Computed permission to store or update
|
||||||
|
const canSaveProduct = computed(() => {
|
||||||
|
return props.isEditing ? hasPermission('products.update') : hasPermission('products.store');
|
||||||
|
});
|
||||||
|
|
||||||
// Form data
|
// Form data
|
||||||
const formData = ref<CreateProductData>({
|
const formData = ref<CreateProductData>({
|
||||||
code: '',
|
code: '',
|
||||||
@ -700,11 +709,12 @@ const onUpload = (event: any) => {
|
|||||||
outlined
|
outlined
|
||||||
@click="handleCancel"
|
@click="handleCancel"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
:label="isEditing ? 'Actualizar Producto' : 'Guardar Producto'"
|
v-if="canSaveProduct"
|
||||||
:disabled="!isFormValid"
|
:label="isEditing ? 'Actualizar Producto' : 'Guardar Producto'"
|
||||||
@click="handleSubmit"
|
:disabled="!isFormValid"
|
||||||
/>
|
@click="handleSubmit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
|
import { useAuth } from '@/modules/auth/composables/useAuth';
|
||||||
import { useToast } from 'primevue/usetoast';
|
import { useToast } from 'primevue/usetoast';
|
||||||
import { useConfirm } from 'primevue/useconfirm';
|
import { useConfirm } from 'primevue/useconfirm';
|
||||||
import Breadcrumb from 'primevue/breadcrumb';
|
import Breadcrumb from 'primevue/breadcrumb';
|
||||||
@ -38,6 +39,13 @@ const isEditing = ref(false);
|
|||||||
const searchTerm = ref('');
|
const searchTerm = ref('');
|
||||||
const selectedProduct = ref<Product | null>(null);
|
const selectedProduct = ref<Product | null>(null);
|
||||||
|
|
||||||
|
// Auth/Permissions
|
||||||
|
const { hasPermission } = useAuth();
|
||||||
|
const canCreateProduct = computed(() => hasPermission('products.store'));
|
||||||
|
const canUpdateProduct = computed(() => hasPermission('products.update'));
|
||||||
|
const canDeleteProduct = computed(() => hasPermission('products.destroy'));
|
||||||
|
const canViewProduct = computed(() => hasPermission('products.index'));
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
const products = computed(() => productStore.products);
|
const products = computed(() => productStore.products);
|
||||||
const loading = computed(() => productStore.loading);
|
const loading = computed(() => productStore.loading);
|
||||||
@ -167,7 +175,12 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<div>
|
||||||
|
<div v-if="!canViewProduct" class="flex flex-col items-center py-16">
|
||||||
|
<i class="pi pi-ban text-red-400 text-5xl mb-4"></i>
|
||||||
|
<p class="text-lg text-red-600 font-semibold">No tienes permisos para visualizar este módulo.</p>
|
||||||
|
</div>
|
||||||
|
<div v-else class="space-y-6">
|
||||||
<!-- Toast Notifications -->
|
<!-- Toast Notifications -->
|
||||||
<Toast position="bottom-right" />
|
<Toast position="bottom-right" />
|
||||||
|
|
||||||
@ -182,11 +195,12 @@ onMounted(() => {
|
|||||||
<h1 class="text-2xl font-bold leading-tight tracking-tight text-surface-900 dark:text-white">
|
<h1 class="text-2xl font-bold leading-tight tracking-tight text-surface-900 dark:text-white">
|
||||||
Catálogo de Productos
|
Catálogo de Productos
|
||||||
</h1>
|
</h1>
|
||||||
<Button
|
<Button
|
||||||
label="Nuevo Producto"
|
v-if="canCreateProduct"
|
||||||
icon="pi pi-plus"
|
label="Nuevo Producto"
|
||||||
@click="openCreateDialog"
|
icon="pi pi-plus"
|
||||||
/>
|
@click="openCreateDialog"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filters & Search -->
|
<!-- Filters & Search -->
|
||||||
@ -296,23 +310,25 @@ onMounted(() => {
|
|||||||
<Column header="Acciones" headerStyle="text-align: right" bodyStyle="text-align: right" style="min-width: 120px">
|
<Column header="Acciones" headerStyle="text-align: right" bodyStyle="text-align: right" style="min-width: 120px">
|
||||||
<template #body="slotProps">
|
<template #body="slotProps">
|
||||||
<div class="flex items-center justify-end gap-2">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<Button
|
<Button
|
||||||
icon="pi pi-pencil"
|
v-if="canUpdateProduct"
|
||||||
text
|
icon="pi pi-pencil"
|
||||||
rounded
|
text
|
||||||
size="small"
|
rounded
|
||||||
@click="editProduct(slotProps.data)"
|
size="small"
|
||||||
v-tooltip.top="'Editar'"
|
@click="editProduct(slotProps.data)"
|
||||||
/>
|
v-tooltip.top="'Editar'"
|
||||||
<Button
|
/>
|
||||||
icon="pi pi-trash"
|
<Button
|
||||||
text
|
v-if="canDeleteProduct"
|
||||||
rounded
|
icon="pi pi-trash"
|
||||||
size="small"
|
text
|
||||||
severity="danger"
|
rounded
|
||||||
@click="deleteProductConfirm(slotProps.data)"
|
size="small"
|
||||||
v-tooltip.top="'Eliminar'"
|
severity="danger"
|
||||||
/>
|
@click="deleteProductConfirm(slotProps.data)"
|
||||||
|
v-tooltip.top="'Eliminar'"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Column>
|
</Column>
|
||||||
@ -363,6 +379,7 @@ onMounted(() => {
|
|||||||
@save="handleSaveProduct"
|
@save="handleSaveProduct"
|
||||||
@cancel="handleCancelForm"
|
@cancel="handleCancelForm"
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
Loading…
x
Reference in New Issue
Block a user