commit04
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-10-30 08:35:27 +02:00
parent 465426847e
commit 0235c7609e
7 changed files with 331 additions and 81 deletions
+183 -76
View File
@@ -13,6 +13,7 @@ import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
@@ -76,7 +77,7 @@ export function SgcWorkspaceShell({ children }: { children: ReactNode }) {
<Link
href="/dashboard"
onClick={navigate}
className="flex items-center gap-3 px-5 py-6 focus-visible:outline-2 focus-visible:outline-offset-[-4px] focus-visible:outline-white"
className="flex items-center gap-3 px-5 py-6 focus-visible:outline-2 focus-visible:outline-offset-[-4px] focus-visible:outline-white lg:py-3"
>
<span className="grid size-10 shrink-0 place-items-center rounded-xl bg-[#ed1c24] text-sm font-bold text-white">
SGC
@@ -253,18 +254,13 @@ export function SgcWorkspaceShell({ children }: { children: ReactNode }) {
</div>
);
return (
<div className="min-h-screen bg-slate-50 text-foreground lg:grid lg:grid-cols-[17rem_minmax(0,1fr)] dark:bg-background print:block">
<div className="min-h-screen bg-slate-50 text-foreground dark:bg-background print:block">
<a
href="#sgc-main"
className="sr-only z-50 rounded bg-white p-3 text-black focus:not-sr-only focus:fixed focus:top-2 focus:left-2"
>
Saltar para o conteúdo
</a>
<aside className="sticky top-0 hidden h-screen flex-col border-r border-white/5 bg-[#101715] text-white lg:flex print:hidden">
{brand}
{navigation}
{sidebarFooter}
</aside>
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
<SheetContent
side="left"
@@ -282,79 +278,181 @@ export function SgcWorkspaceShell({ children }: { children: ReactNode }) {
{sidebarFooter}
</SheetContent>
</Sheet>
<div className="flex min-w-0 flex-col">
<header className="sticky top-0 z-20 flex min-h-18 items-center justify-between gap-4 border-b border-border bg-background/95 px-5 py-3 backdrop-blur lg:px-8 print:hidden">
<div className="flex min-w-0 items-center gap-3">
<Button
variant="outline"
size="icon"
aria-label="Abrir menu de Compras"
onClick={() => setMobileOpen(true)}
className="shrink-0 lg:hidden"
<div className="flex min-h-screen min-w-0 flex-col">
<header className="sticky top-0 z-30 border-b border-border bg-background/95 backdrop-blur print:hidden">
<div className="hidden items-center border-b border-white/5 bg-[#101715] text-white lg:flex">
<div className="shrink-0">{brand}</div>
<nav
aria-label="Navegação principal horizontal"
className="flex min-w-0 flex-1 flex-wrap items-center gap-1 px-3 py-3"
>
<Menu className="size-4" />
</Button>
<div className="min-w-0">
<p className="text-[10px] font-semibold tracking-[0.15em] text-muted-foreground">
COMPRAS
</p>
<p className="truncate text-sm font-medium">
{page.url.split('?')[0] === '/dashboard'
? 'Painel inicial'
: (activeGroup?.label ??
'Espaço de trabalho')}
</p>
</div>
</div>
{searchField()}
<div className="flex shrink-0 items-center gap-2">
<Button
asChild
size="sm"
className="hidden bg-[#ed1c24] text-white hover:bg-[#d01720] sm:inline-flex"
>
<Link href="/requisicoes/criar">
<Plus className="size-4" />
Nova requisição
</Link>
</Button>
{user && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
aria-label={`Conta de ${user.name}`}
className="h-auto max-w-52 gap-2 px-2 py-1.5"
>
<span
className="grid size-8 shrink-0 place-items-center rounded-full bg-muted text-xs font-semibold"
aria-hidden="true"
{(!query ||
normalize(
'Painel inicial Visão geral',
).includes(query)) && (
<Link
href="/dashboard"
onClick={navigate}
aria-current={
page.url.split('?')[0] === '/dashboard'
? 'page'
: undefined
}
className={`flex items-center gap-2 rounded-lg px-3 py-2.5 text-[13px] font-medium transition-colors focus-visible:outline-2 focus-visible:outline-white ${page.url.split('?')[0] === '/dashboard' ? 'bg-white/10 text-white' : 'text-slate-300 hover:bg-white/5 hover:text-white'}`}
>
<LayoutDashboard
className="size-4 shrink-0"
aria-hidden="true"
/>
Painel inicial
</Link>
)}
{visibleGroups.map((group) => (
<DropdownMenu key={group.id}>
<DropdownMenuTrigger asChild>
<button
type="button"
className={`flex items-center gap-2 rounded-lg px-3 py-2.5 text-[13px] font-medium transition-colors hover:bg-white/5 focus-visible:outline-2 focus-visible:outline-white data-[state=open]:bg-white/10 ${activeGroup?.id === group.id ? 'bg-white/5 text-white' : 'text-slate-300'}`}
>
{user.name
.trim()
.split(/\s+/)
.slice(0, 2)
.map((part) => part[0])
.join('')}
</span>
<span className="hidden min-w-0 text-left xl:block">
<span className="block truncate text-xs font-medium">
{user.name}
<group.icon
className={`size-4 shrink-0 ${activeGroup?.id === group.id ? 'text-red-400' : 'text-slate-500'}`}
aria-hidden="true"
/>
<span>{group.label}</span>
<ChevronDown
className="size-3.5 shrink-0 text-slate-500"
aria-hidden="true"
/>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
sideOffset={8}
className="max-h-[min(70vh,32rem)] w-72 overflow-y-auto border-white/10 bg-[#101715] p-2 text-white"
>
{group.entries.map((entry) => (
<DropdownMenuItem
key={entry.id}
asChild
className={`rounded-md px-3 py-2 text-[13px] leading-5 focus:bg-white/5 focus:text-white ${activeId === entry.id ? 'bg-red-500/15 font-medium text-red-300' : 'text-slate-400'}`}
>
{entry.fullPage ? (
<a
href={entry.path}
onClick={navigate}
aria-current={
activeId ===
entry.id
? 'page'
: undefined
}
>
{entry.label}
</a>
) : (
<Link
href={entry.path}
onClick={navigate}
aria-current={
activeId ===
entry.id
? 'page'
: undefined
}
>
{entry.label}
</Link>
)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
))}
{query &&
visibleGroups.length === 0 &&
!normalize(
'Painel inicial Visão geral',
).includes(query) && (
<p className="px-3 py-2 text-sm text-slate-400">
Nenhuma opção encontrada.
</p>
)}
</nav>
</div>
<div className="flex min-h-18 items-center justify-between gap-4 px-5 py-3 lg:px-8">
<div className="flex min-w-0 items-center gap-3">
<Button
variant="outline"
size="icon"
aria-label="Abrir menu de Compras"
onClick={() => setMobileOpen(true)}
className="shrink-0 lg:hidden"
>
<Menu className="size-4" />
</Button>
<div className="min-w-0">
<p className="text-[10px] font-semibold tracking-[0.15em] text-muted-foreground">
COMPRAS
</p>
<p className="truncate text-sm font-medium">
{page.url.split('?')[0] === '/dashboard'
? 'Painel inicial'
: (activeGroup?.label ??
'Espaço de trabalho')}
</p>
</div>
</div>
{searchField()}
<div className="flex shrink-0 items-center gap-2">
<Button
asChild
size="sm"
className="hidden bg-[#ed1c24] text-white hover:bg-[#d01720] sm:inline-flex"
>
<Link href="/requisicoes/criar">
<Plus className="size-4" />
Nova requisição
</Link>
</Button>
{user && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
aria-label={`Conta de ${user.name}`}
className="h-auto max-w-52 gap-2 px-2 py-1.5"
>
<span
className="grid size-8 shrink-0 place-items-center rounded-full bg-muted text-xs font-semibold"
aria-hidden="true"
>
{user.name
.trim()
.split(/\s+/)
.slice(0, 2)
.map((part) => part[0])
.join('')}
</span>
<span className="block text-[10px] text-muted-foreground">
{roleLabels[
user.purchasing_role ?? ''
] ?? 'Utilizador'}
<span className="hidden min-w-0 text-left xl:block">
<span className="block truncate text-xs font-medium">
{user.name}
</span>
<span className="block text-[10px] text-muted-foreground">
{roleLabels[
user.purchasing_role ??
''
] ?? 'Utilizador'}
</span>
</span>
</span>
<ChevronDown className="size-3 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<UserMenuContent user={user} />
</DropdownMenuContent>
</DropdownMenu>
)}
<ChevronDown className="size-3 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<UserMenuContent user={user} />
</DropdownMenuContent>
</DropdownMenu>
)}
</div>
</div>
</header>
<main id="sgc-main" className="min-w-0 flex-1" tabIndex={-1}>
@@ -362,6 +460,15 @@ export function SgcWorkspaceShell({ children }: { children: ReactNode }) {
</main>
<footer className="mt-auto border-t border-border px-5 py-4 text-xs text-muted-foreground lg:px-8 print:hidden">
SGC · Sistema de Gestão de Compras
<Link
href="/settings/appearance"
className="ml-4 hover:text-foreground"
>
Aparência
</Link>
<Link href="/" className="ml-4 hover:text-foreground">
Página inicial
</Link>
</footer>
</div>
</div>