diff --git a/backlog/features.json b/backlog/features.json
index cfc34af..9cd5efd 100644
--- a/backlog/features.json
+++ b/backlog/features.json
@@ -6709,6 +6709,83 @@
"close": true
},
"completed_at": "2026-08-22T15:50:54Z"
+ },
+ {
+ "id": "F-159",
+ "type": "feature",
+ "title": "Admin responsive collapsible sidebar",
+ "description": "Add accessible persisted SidebarToggle: desktop icon-only collapse and mobile/tablet navigation drawer without duplicated navigation logic.",
+ "priority": "high",
+ "risk": "med",
+ "status": "done",
+ "created_at": "2026-08-22",
+ "gates": {
+ "reviewer": true,
+ "security": true,
+ "qa": true,
+ "close": true
+ },
+ "completed_at": "2026-08-22T16:02:11Z"
+ },
+ {
+ "id": "F-160",
+ "type": "fix",
+ "title": "Fix Reporting sales grouped queries returning 500",
+ "description": "Diagnose and correct SQL/runtime failures for day, channel, store and terminal grouped reporting sales endpoints.",
+ "priority": "high",
+ "risk": "high",
+ "status": "pending",
+ "created_at": "2026-08-22",
+ "gates": {
+ "reviewer": false,
+ "security": false,
+ "qa": false
+ }
+ },
+ {
+ "id": "F-161",
+ "type": "fix",
+ "title": "Complete admin order detail information",
+ "description": "Order detail must show shipping address, payment method, customer details and correct customer email without false missing-email warning.",
+ "priority": "high",
+ "risk": "med",
+ "status": "pending",
+ "created_at": "2026-08-22",
+ "gates": {
+ "reviewer": false,
+ "security": false,
+ "qa": false
+ }
+ },
+ {
+ "id": "F-162",
+ "type": "fix",
+ "title": "Add storefront product link to inventory",
+ "description": "Add final Tienda column to inventory, matching product list, linking to storefront product page.",
+ "priority": "med",
+ "risk": "low",
+ "status": "pending",
+ "created_at": "2026-08-22",
+ "gates": {
+ "reviewer": false,
+ "security": false,
+ "qa": false
+ }
+ },
+ {
+ "id": "F-163",
+ "type": "feature",
+ "title": "Make POS terminal and cash session setup usable",
+ "description": "Clarify and expose terminal binding and cash session workflow so operator can configure and open TPV without manual database/API steps.",
+ "priority": "high",
+ "risk": "med",
+ "status": "pending",
+ "created_at": "2026-08-22",
+ "gates": {
+ "reviewer": false,
+ "security": false,
+ "qa": false
+ }
}
]
}
diff --git a/project/apps/admin/next-env.d.ts b/project/apps/admin/next-env.d.ts
index a419cbe..ce4e94a 100644
--- a/project/apps/admin/next-env.d.ts
+++ b/project/apps/admin/next-env.d.ts
@@ -1,7 +1,7 @@
///
///
-import "./.next/dev/types/routes.d.ts";
-import "./.next/dev/types/root-params.d.ts";
+import "./.next/types/routes.d.ts";
+import "./.next/types/root-params.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/project/apps/admin/src/app/(dashboard)/layout.tsx b/project/apps/admin/src/app/(dashboard)/layout.tsx
index 64192ae..483c8b9 100644
--- a/project/apps/admin/src/app/(dashboard)/layout.tsx
+++ b/project/apps/admin/src/app/(dashboard)/layout.tsx
@@ -1,32 +1,74 @@
'use client';
-import { useEffect } from 'react';
-import { usePathname, useRouter } from 'next/navigation';
+
+import { useEffect, useState } from 'react';
import Link from 'next/link';
+import { usePathname, useRouter } from 'next/navigation';
import { AuthProvider, useAuth } from '@/features/auth/components/AuthProvider';
import { visibleNavItems, type NavItem } from '@/lib/permissions';
import type { Role } from '@/types';
-function NavItemRow({ item }: { item: NavItem }) {
+const SIDEBAR_STORAGE_KEY = 'mdv.admin.sidebar.collapsed';
+const SIDEBAR_ID = 'admin-sidebar';
+
+interface SidebarToggleProps {
+ expanded: boolean;
+ onToggle: () => void;
+ mode: 'desktop' | 'mobile';
+}
+
+function SidebarToggle({ expanded, onToggle, mode }: SidebarToggleProps) {
+ const label = mode === 'mobile'
+ ? expanded ? 'Cerrar menú de navegación' : 'Abrir menú de navegación'
+ : expanded ? 'Colapsar menú lateral' : 'Expandir menú lateral';
+
+ return (
+
+ );
+}
+
+function NavItemRow({ item, collapsed, onNavigate }: { item: NavItem; collapsed: boolean; onNavigate: () => void }) {
const pathname = usePathname();
- const active = item.href === '/'
- ? pathname === '/'
- : pathname.startsWith(item.href);
+ const active = item.href === '/' ? pathname === '/' : pathname.startsWith(item.href);
return (
- {item.icon}
- {item.label}
+ {item.icon}
+ {item.label}
{item.badge != null && item.badge > 0 && (
-
+
{item.badge}
)}
@@ -34,51 +76,107 @@ function NavItemRow({ item }: { item: NavItem }) {
);
}
-function Sidebar({ role, email }: { role: Role; email: string }) {
+interface SidebarProps {
+ role: Role;
+ email: string;
+ collapsed: boolean;
+ mobileOpen: boolean;
+ onCloseMobile: () => void;
+ onLogout: () => void;
+}
+
+function Sidebar({ role, email, collapsed, mobileOpen, onCloseMobile, onLogout }: SidebarProps) {
const items = visibleNavItems(role);
return (
-