diff --git a/src/components/layout/SidebarReorganized.tsx b/src/components/layout/SidebarReorganized.tsx index 4af878fb6..2ed6ad2e0 100644 --- a/src/components/layout/SidebarReorganized.tsx +++ b/src/components/layout/SidebarReorganized.tsx @@ -2,11 +2,38 @@ import React, { useState, useMemo, useEffect, useCallback } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useLocation, useNavigate } from 'react-router-dom'; import { - Package, Users, Heart, GitCompare, FolderOpen, ChevronLeft, ChevronRight, - ShieldCheck, Calculator, Sparkles, FileText, ShoppingCart, Wrench, Zap, - RefreshCw, DollarSign, Plus, Activity, Gauge, Truck, Palette, Brain, - Workflow, Layers, SlidersHorizontal, Boxes, ImagePlus, BarChart3, - Crosshair, Settings, Plug, ChevronsDownUp, + Package, + Users, + Heart, + GitCompare, + FolderOpen, + ChevronLeft, + ChevronRight, + ShieldCheck, + Calculator, + Sparkles, + FileText, + ShoppingCart, + Wrench, + Zap, + RefreshCw, + DollarSign, + Plus, + Activity, + Gauge, + Truck, + Palette, + Brain, + Workflow, + Layers, + SlidersHorizontal, + Boxes, + ImagePlus, + BarChart3, + Crosshair, + Settings, + Plug, + ChevronsDownUp, } from 'lucide-react'; import { cn } from '@/lib/utils'; import { supabase } from '@/integrations/supabase/client'; @@ -33,7 +60,14 @@ const navGroups: NavGroup[] = [ defaultOpen: true, items: [ { icon: Plus, label: 'Novo Orçamento', href: '/orcamentos/novo', shortcut: 'Alt+N' }, - { icon: FileText, label: 'Orçamentos', href: '/orcamentos', tourId: 'quotes', exact: true, shortcut: 'Alt+O' }, + { + icon: FileText, + label: 'Orçamentos', + href: '/orcamentos', + tourId: 'quotes', + exact: true, + shortcut: 'Alt+O', + }, { icon: ShoppingCart, label: 'Carrinhos', href: '/carrinhos', shortcut: 'Alt+R' }, ], }, @@ -94,7 +128,10 @@ const navGroups: NavGroup[] = [ { icon: ShieldCheck, label: 'Acesso & Bots', href: '/admin/seguranca-acesso', devOnly: true }, { icon: Plug, label: 'Conexões', href: '/admin/conexoes', devOnly: true }, { - icon: FolderOpen, label: 'Cadastros', href: '/admin/cadastros', adminOnly: true, + icon: FolderOpen, + label: 'Cadastros', + href: '/admin/cadastros', + adminOnly: true, children: [ { icon: Package, label: 'Produtos', href: '/admin/cadastros?tab=products' }, { icon: Truck, label: 'Fornecedores', href: '/admin/cadastros?tab=suppliers' }, @@ -105,7 +142,12 @@ const navGroups: NavGroup[] = [ { icon: Workflow, label: 'Workflows IA', href: '/admin/workflows', devOnly: true }, { icon: Activity, label: 'Telemetria', href: '/admin/telemetria', devOnly: true }, { icon: Gauge, label: 'Performance UX', href: '/admin/client-performance', devOnly: true }, - { icon: DollarSign, label: 'Validade de Preços', href: '/admin/validade-precos', devOnly: true }, + { + icon: DollarSign, + label: 'Validade de Preços', + href: '/admin/validade-precos', + devOnly: true, + }, { icon: ShieldCheck, label: 'Auditoria RBAC', href: '/admin/rbac-rotas', devOnly: true }, { icon: Activity, label: 'Status do Sistema', href: '/admin/status', devOnly: true }, ], @@ -120,9 +162,7 @@ const navGroups: NavGroup[] = [ function computeOpenGroups(pathname: string): Record { const next: Record = {}; navGroups.forEach((group) => { - const hasActive = group.items.some((item) => - isNavItemActive(pathname, item.href, item.exact), - ); + const hasActive = group.items.some((item) => isNavItemActive(pathname, item.href, item.exact)); next[group.id] = hasActive || (group.defaultOpen ?? false); }); return next; @@ -138,12 +178,11 @@ export const SidebarReorganized = React.memo( const [isCollapsed, setIsCollapsed] = useState(false); useEffect(() => { - document.documentElement.style.setProperty('--sidebar-w', - isCollapsed ? '4rem' : '16rem'); + document.documentElement.style.setProperty('--sidebar-w', isCollapsed ? '4rem' : '16rem'); }, [isCollapsed]); - const [openGroups, setOpenGroups] = useState>( - () => computeOpenGroups(location.pathname), + const [openGroups, setOpenGroups] = useState>(() => + computeOpenGroups(location.pathname), ); const prevPathRef = React.useRef(location.pathname); @@ -163,8 +202,8 @@ export const SidebarReorganized = React.memo( const { data: pendingApprovalCount } = useQuery({ queryKey: ['pending-discount-approvals-count'], queryFn: async () => { - // rls-allow: admin-only badge query, guarded by `enabled: isAdmin` const { count } = await supabase + // rls-allow: admin-only badge query, guarded by `enabled: isAdmin` .from('discount_approval_requests') .select('*', { count: 'exact', head: true }) .eq('status', 'pending'); @@ -197,7 +236,9 @@ export const SidebarReorganized = React.memo( const collapseAllGroups = () => { setOpenGroups((prev) => { const collapsed: Record = {}; - Object.keys(prev).forEach((key) => { collapsed[key] = false; }); + Object.keys(prev).forEach((key) => { + collapsed[key] = false; + }); return collapsed; }); }; @@ -214,9 +255,17 @@ export const SidebarReorganized = React.memo( const handler = (e: KeyboardEvent) => { if (e.altKey && !e.ctrlKey && !e.metaKey) { const target = e.target as HTMLElement; - if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) return; + if ( + target.tagName === 'INPUT' || + target.tagName === 'TEXTAREA' || + target.isContentEditable + ) + return; const href = shortcutMap[e.key.toLowerCase()]; - if (href) { e.preventDefault(); navigate(href); } + if (href) { + e.preventDefault(); + navigate(href); + } } }; window.addEventListener('keydown', handler); @@ -278,12 +327,21 @@ export const SidebarReorganized = React.memo( 'theme-transitioning fixed left-0 top-0 z-50 h-full border-r border-sidebar-border/20 bg-sidebar/80 backdrop-blur-3xl transition-all duration-500', isCollapsed ? 'overflow-visible' : 'overflow-hidden', 'lg:sticky lg:top-0 lg:z-40 lg:h-screen', - isOpen ? 'translate-x-0 shadow-[40px_0_100px_rgba(0,0,0,0.4)]' : '-translate-x-full lg:translate-x-0', - isCollapsed ? 'w-16 lg:shadow-[20px_0_50px_rgba(0,0,0,0.15)]' : 'w-64 lg:shadow-[30px_0_80px_rgba(0,0,0,0.2)]', + isOpen + ? 'translate-x-0 shadow-[40px_0_100px_rgba(0,0,0,0.4)]' + : '-translate-x-full lg:translate-x-0', + isCollapsed + ? 'w-16 lg:shadow-[20px_0_50px_rgba(0,0,0,0.15)]' + : 'w-64 lg:shadow-[30px_0_80px_rgba(0,0,0,0.2)]', )} > -
+
@@ -291,7 +349,8 @@ export const SidebarReorganized = React.memo(
{!isCollapsed && (
@@ -329,8 +393,12 @@ export const SidebarReorganized = React.memo( > {filteredGroups.map((group, index) => (
- {index > 0 && !isCollapsed &&
} - {index > 0 && isCollapsed &&
} + {index > 0 && !isCollapsed && ( +
+ )} + {index > 0 && isCollapsed && ( +
+ )} { const getTrend = (values: number[]) => { if (values.length < 2) return 0; - return Number((values[values.length - 1]! - values[0]).toFixed(1)); + return Number((values[values.length - 1] - values[0]).toFixed(1)); }; export default function CoverageInsightsDashboardPage() { diff --git a/src/services/quoteService.ts b/src/services/quoteService.ts index 8ea53e0ae..852149bc5 100644 --- a/src/services/quoteService.ts +++ b/src/services/quoteService.ts @@ -1,4 +1,5 @@ import { supabase } from '@/integrations/supabase/client'; +import type { Json } from '@/integrations/supabase/types'; import { type Quote, type QuoteItem, @@ -209,10 +210,10 @@ export const quoteService = { user_id: userId, action, description, - field_changed: options?.fieldChanged || null, - old_value: options?.oldValue || null, - new_value: options?.newValue || null, - metadata: options?.metadata || {}, + field_changed: typeof options?.fieldChanged === 'string' ? options.fieldChanged : null, + old_value: typeof options?.oldValue === 'string' ? options.oldValue : null, + new_value: typeof options?.newValue === 'string' ? options.newValue : null, + metadata: (options?.metadata ?? {}) as Json, }); }, };