feat: add layout and sidebar components with navigation structure
Some checks failed
CI / lint (push) Failing after 7s
CI / test-backend (push) Has been skipped
CI / test-frontend (push) Has been skipped
CI / test-e2e (push) Has been skipped

chore: update dependencies and configuration for Tailwind CSS
docs: create components.json and skills-lock.json for project structure
This commit is contained in:
2026-02-22 19:35:25 +01:00
parent 0c9c408564
commit 9ed4437906
9 changed files with 504 additions and 148 deletions

21
frontend/components.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/styles/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@@ -10,14 +10,18 @@
"preview": "vite preview"
},
"dependencies": {
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toast": "^1.2.15",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-form": "^1.28.3",
"@tanstack/react-query": "^5.90.21",
"@tanstack/react-router": "^1.162.2",
@@ -32,6 +36,7 @@
"@eslint/js": "^9.39.1",
"@radix-ui/react-slot": "^1.2.4",
"@tailwindcss/postcss": "^4.2.0",
"@tailwindcss/vite": "^4.2.0",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",

10
frontend/skills-lock.json Normal file
View File

@@ -0,0 +1,10 @@
{
"version": 1,
"skills": {
"shadcn-ui": {
"source": "google-labs-code/stitch-skills",
"sourceType": "github",
"computedHash": "dadbca54d35a33fe73e40018611af2179689305bf6da812ab2643987fefd9da3"
}
}
}

View File

@@ -1,38 +1,14 @@
import { useState } from 'react';
import { Outlet } from '@tanstack/react-router';
import { Header } from './Header';
import { Sidebar } from './Sidebar';
interface LayoutProps {
sidebarOpen?: boolean;
onSidebarChange?: (open: boolean) => void;
}
export function Layout({ sidebarOpen = false, onSidebarChange }: LayoutProps) {
const [isSidebarOpen, setIsSidebarOpen] = useState(sidebarOpen);
const handleSidebarToggle = (open: boolean) => {
setIsSidebarOpen(open);
onSidebarChange?.(open);
};
import { AppSidebar } from './Sidebar';
export function Layout() {
return (
<div className="min-h-screen bg-background">
{/* Sidebar */}
<Sidebar isOpen={isSidebarOpen} onClose={() => handleSidebarToggle(false)} />
{/* Contenido principal */}
<div className="md:pl-64">
{/* Header */}
<Header onMenuToggle={() => handleSidebarToggle(true)} />
{/* Main content */}
<main className="container mx-auto p-6">
<AppSidebar>
<main className="flex-1 p-6">
<div className="space-y-6">
<Outlet />
</div>
</main>
</div>
</div>
</AppSidebar>
);
}

View File

@@ -1,119 +1,309 @@
import { Link, useLocation } from '@tanstack/react-router';
import { Home, Gamepad2, FileText, Settings, Database, Tag, Download, Upload } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Badge } from '@/components/ui/badge';
'use client';
interface SidebarProps {
isOpen?: boolean;
onClose?: () => void;
}
import * as React from 'react';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarTrigger,
useSidebar,
} from '@/components/ui/sidebar';
import {
Gamepad2,
Home,
Settings,
User,
Database,
Star,
TrendingUp,
ChevronRight,
ChevronsUpDown,
} from 'lucide-react';
const navigation = [
{ name: 'Dashboard', href: '/', icon: Home, count: null },
{ name: 'Juegos', href: '/games', icon: Gamepad2, count: null },
const data = {
user: {
name: 'Game Library',
email: 'admin@gamelibrary.com',
avatar: '/avatars/default.jpg',
},
teams: [
{
name: 'Plataformas',
href: '/platforms',
icon: Database,
count: null,
name: 'Personal',
logo: Gamepad2,
plan: 'Standard',
},
{
name: 'Etiquetas',
href: '/tags',
icon: Tag,
count: null,
name: 'Work',
logo: Database,
plan: 'Professional',
},
{ name: 'Importar ROMs', href: '/import', icon: Download, count: null },
{ name: 'Exportar', href: '/export', icon: Upload, count: null },
{ name: 'Configuración', href: '/settings', icon: Settings, count: null },
];
],
navMain: [
{
title: 'Dashboard',
url: '/',
icon: Home,
isActive: true,
},
{
title: 'Games',
url: '/games',
icon: Gamepad2,
items: [
{
title: 'All Games',
url: '/games',
},
{
title: 'Favorites',
url: '/games/favorites',
},
{
title: 'Recently Played',
url: '/games/recent',
},
],
},
{
title: 'Collections',
url: '/collections',
icon: Star,
items: [
{
title: 'My Collections',
url: '/collections',
},
{
title: 'Shared Collections',
url: '/collections/shared',
},
],
},
{
title: 'Statistics',
url: '/stats',
icon: TrendingUp,
},
{
title: 'Settings',
url: '/settings',
icon: Settings,
},
],
};
export function Sidebar({ isOpen, onClose }: SidebarProps) {
const location = useLocation();
function TeamSwitcher({
teams,
}: {
teams: {
name: string;
logo: React.ElementType;
plan: string;
}[];
}) {
const { isMobile } = useSidebar();
const [activeTeam, setActiveTeam] = React.useState(teams[0]);
if (!activeTeam) {
return null;
}
return (
<>
{/* Overlay para móviles */}
{isOpen && <div className="fixed inset-0 z-40 bg-black/50 md:hidden" onClick={onClose} />}
{/* Sidebar */}
<div
className={cn(
'fixed left-0 top-0 z-50 h-full w-64 transform border-r bg-background transition-transform duration-300 ease-in-out md:relative md:translate-x-0',
isOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'
)}
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<div className="flex h-full flex-col">
{/* Header del sidebar */}
<div className="flex h-14 items-center border-b px-4">
<div className="flex items-center space-x-2">
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
<activeTeam.logo className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{activeTeam.name}</span>
<span className="truncate text-xs">{activeTeam.plan}</span>
</div>
<ChevronsUpDown className="ml-auto" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
align="start"
side={isMobile ? 'bottom' : 'right'}
sideOffset={4}
>
<DropdownMenuGroup>
<DropdownMenuLabel className="text-muted-foreground text-xs">Teams</DropdownMenuLabel>
{teams.map((team) => (
<DropdownMenuItem
key={team.name}
onClick={() => setActiveTeam(team)}
className="cursor-pointer"
>
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg mr-2">
<team.logo className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{team.name}</span>
<span className="truncate text-xs">{team.plan}</span>
</div>
</DropdownMenuItem>
))}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}
function NavMain({
items,
}: {
items: {
title: string;
url: string;
icon?: React.ElementType;
isActive?: boolean;
items?: {
title: string;
url: string;
}[];
}[];
}) {
return (
<SidebarGroup>
<SidebarGroupLabel>Main Navigation</SidebarGroupLabel>
<SidebarMenu>
{items.map((item) => (
<Collapsible key={item.title} defaultOpen={item.isActive} className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger render={<SidebarMenuButton tooltip={item.title} />}>
{item.icon && <item.icon />}
<span>{item.title}</span>
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton render={<a href={subItem.url} />}>
<span>{subItem.title}</span>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
</SidebarGroup>
);
}
function NavUser({ user }: { user: { name: string; email: string; avatar: string } }) {
const { isMobile } = useSidebar();
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-full">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>{user.name.charAt(0)}</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs text-muted-foreground">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
align="start"
side={isMobile ? 'bottom' : 'right'}
sideOffset={4}
>
<DropdownMenuLabel className="text-muted-foreground text-xs">
Logged in as
</DropdownMenuLabel>
<DropdownMenuGroup>
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
<span>Profile</span>
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
<span>Settings</span>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<span>Log out</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
<SidebarProvider>
<Sidebar collapsible="icon" {...props}>
<SidebarHeader>
<TeamSwitcher teams={data.teams} />
</SidebarHeader>
<SidebarContent>
<NavMain items={data.navMain} />
</SidebarContent>
<SidebarFooter>
<NavUser user={data.user} />
</SidebarFooter>
<SidebarRail />
</Sidebar>
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b bg-background px-4 transition-[width,height] ease-linear group-has-[collapsible=icon]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2">
<SidebarTrigger className="-ml-1" />
<div className="flex items-center gap-2">
<Gamepad2 className="h-6 w-6" />
<span className="font-bold">Quasar</span>
</div>
<Button variant="ghost" size="sm" className="ml-auto md:hidden" onClick={onClose}>
</Button>
</div>
{/* Contenido del sidebar */}
<ScrollArea className="flex-1">
<nav className="p-4 space-y-1">
{navigation.map((item) => {
const Icon = item.icon;
const isActive = location.pathname === item.href;
return (
<Link
key={item.name}
to={item.href}
onClick={onClose}
className={cn(
'flex items-center justify-between rounded-lg px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground',
isActive ? 'bg-accent text-accent-foreground' : 'text-muted-foreground'
)}
>
<div className="flex items-center space-x-3">
<Icon className="h-4 w-4" />
<span>{item.name}</span>
</div>
{item.count !== null && (
<Badge variant="secondary" className="text-xs">
{item.count}
</Badge>
)}
</Link>
);
})}
</nav>
{/* Sección adicional con información */}
<div className="mt-8 p-4 border-t">
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-3">
Información
</h3>
<div className="space-y-2 text-xs text-muted-foreground">
<div className="flex justify-between">
<span>Versión</span>
<span>1.0.0</span>
</div>
<div className="flex justify-between">
<span>Última actualización</span>
<span>2024-01-15</span>
<h1 className="text-lg font-semibold">Game Library</h1>
</div>
</div>
</div>
</ScrollArea>
{/* Footer del sidebar */}
<div className="flex h-14 items-center border-t px-4">
<div className="flex items-center space-x-2 text-xs text-muted-foreground">
<span>© 2024 Quasar</span>
</div>
</div>
</div>
</div>
</>
</header>
</SidebarInset>
</SidebarProvider>
);
}

View File

@@ -1,6 +1,5 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss/preflight";
@import "tailwindcss";
@layer base {
:root {

View File

@@ -38,6 +38,16 @@ const config: Config = {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
primaryForeground: 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
accentForeground: 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))',
},
},
borderRadius: {
lg: 'var(--radius)',

View File

@@ -1,10 +1,11 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import path from 'path';
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
plugins: [react(), tailwindcss()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),

146
yarn.lock
View File

@@ -1395,6 +1395,29 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-avatar@npm:^1.1.11":
version: 1.1.11
resolution: "@radix-ui/react-avatar@npm:1.1.11"
dependencies:
"@radix-ui/react-context": "npm:1.1.3"
"@radix-ui/react-primitive": "npm:2.1.4"
"@radix-ui/react-use-callback-ref": "npm:1.1.1"
"@radix-ui/react-use-is-hydrated": "npm:0.1.0"
"@radix-ui/react-use-layout-effect": "npm:1.1.1"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/b1b3d4b11a8e05a8479d2410fb4e7b1bf825135c4cd42f7e5152568a54a55a3073bd87d50325150417a29306e7b1b371289dc3c4f11739af8a2a7bb8dd7c38c9
languageName: node
linkType: hard
"@radix-ui/react-checkbox@npm:^1.3.3":
version: 1.3.3
resolution: "@radix-ui/react-checkbox@npm:1.3.3"
@@ -1421,6 +1444,32 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-collapsible@npm:^1.1.12":
version: 1.1.12
resolution: "@radix-ui/react-collapsible@npm:1.1.12"
dependencies:
"@radix-ui/primitive": "npm:1.1.3"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-id": "npm:1.1.1"
"@radix-ui/react-presence": "npm:1.1.5"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
"@radix-ui/react-use-layout-effect": "npm:1.1.1"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/777cced73fbbec9cfafe6325aa5605e90f49d889af2778f4c4a6be101c07cacd69ae817d0b41cc27e3181f49392e2c06db7f32d6b084db047a51805ec70729b3
languageName: node
linkType: hard
"@radix-ui/react-collection@npm:1.1.7":
version: 1.1.7
resolution: "@radix-ui/react-collection@npm:1.1.7"
@@ -1469,6 +1518,19 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-context@npm:1.1.3":
version: 1.1.3
resolution: "@radix-ui/react-context@npm:1.1.3"
peerDependencies:
"@types/react": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/0f271b4100dbb007ad2675f2529453f07454f214b7ce796d72680bf2dff050d0719083ee6e8962919a74048ff853eff2e50de07d8f8c674d6be91bfa76204cc2
languageName: node
linkType: hard
"@radix-ui/react-dialog@npm:^1.1.15":
version: 1.1.15
resolution: "@radix-ui/react-dialog@npm:1.1.15"
@@ -1865,6 +1927,25 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-separator@npm:^1.1.8":
version: 1.1.8
resolution: "@radix-ui/react-separator@npm:1.1.8"
dependencies:
"@radix-ui/react-primitive": "npm:2.1.4"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/92e1353f696a22167c90f2c610b440be1fae3c05128287560914f124eef83d74c06ad25431923f3595032e6d89c23d479c95434390f4c0d9d4a68ec8d563ae0c
languageName: node
linkType: hard
"@radix-ui/react-slot@npm:1.2.3":
version: 1.2.3
resolution: "@radix-ui/react-slot@npm:1.2.3"
@@ -1951,6 +2032,36 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-tooltip@npm:^1.2.8":
version: 1.2.8
resolution: "@radix-ui/react-tooltip@npm:1.2.8"
dependencies:
"@radix-ui/primitive": "npm:1.1.3"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-dismissable-layer": "npm:1.1.11"
"@radix-ui/react-id": "npm:1.1.1"
"@radix-ui/react-popper": "npm:1.2.8"
"@radix-ui/react-portal": "npm:1.1.9"
"@radix-ui/react-presence": "npm:1.1.5"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-slot": "npm:1.2.3"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
"@radix-ui/react-visually-hidden": "npm:1.2.3"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/de0cbae9c571a00671f160928d819e59502f59be8749f536ab4b180181d9d70aee3925a5b2555f8f32d0bea622bc35f65b70ca7ff0449e4844f891302310cc48
languageName: node
linkType: hard
"@radix-ui/react-use-callback-ref@npm:1.1.1":
version: 1.1.1
resolution: "@radix-ui/react-use-callback-ref@npm:1.1.1"
@@ -2010,6 +2121,21 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-use-is-hydrated@npm:0.1.0":
version: 0.1.0
resolution: "@radix-ui/react-use-is-hydrated@npm:0.1.0"
dependencies:
use-sync-external-store: "npm:^1.5.0"
peerDependencies:
"@types/react": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/635079bafe32829fc7405895154568ea94a22689b170489fd6d77668e4885e72ff71ed6d0ea3d602852841ef0f1927aa400fee2178d5dfbeb8bc9297da7d6498
languageName: node
linkType: hard
"@radix-ui/react-use-layout-effect@npm:1.1.1":
version: 1.1.1
resolution: "@radix-ui/react-use-layout-effect@npm:1.1.1"
@@ -2627,6 +2753,19 @@ __metadata:
languageName: node
linkType: hard
"@tailwindcss/vite@npm:^4.2.0":
version: 4.2.0
resolution: "@tailwindcss/vite@npm:4.2.0"
dependencies:
"@tailwindcss/node": "npm:4.2.0"
"@tailwindcss/oxide": "npm:4.2.0"
tailwindcss: "npm:4.2.0"
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
checksum: 10c0/4bd28ea2984907930a2ea4818581ce24bbb7276ffc4d316b32143eef2f2bf9f82bd2b937fe6bb78c9be96b61df5464d884b9c7b1e71f0eb3df2c4a890d34ff79
languageName: node
linkType: hard
"@tanstack/devtools-event-client@npm:^0.4.0":
version: 0.4.0
resolution: "@tanstack/devtools-event-client@npm:0.4.0"
@@ -5095,16 +5234,21 @@ __metadata:
resolution: "frontend@workspace:frontend"
dependencies:
"@eslint/js": "npm:^9.39.1"
"@radix-ui/react-avatar": "npm:^1.1.11"
"@radix-ui/react-checkbox": "npm:^1.3.3"
"@radix-ui/react-collapsible": "npm:^1.1.12"
"@radix-ui/react-dialog": "npm:^1.1.15"
"@radix-ui/react-dropdown-menu": "npm:^2.1.16"
"@radix-ui/react-label": "npm:^2.1.8"
"@radix-ui/react-scroll-area": "npm:^1.2.10"
"@radix-ui/react-select": "npm:^2.2.6"
"@radix-ui/react-separator": "npm:^1.1.8"
"@radix-ui/react-slot": "npm:^1.2.4"
"@radix-ui/react-tabs": "npm:^1.1.13"
"@radix-ui/react-toast": "npm:^1.2.15"
"@radix-ui/react-tooltip": "npm:^1.2.8"
"@tailwindcss/postcss": "npm:^4.2.0"
"@tailwindcss/vite": "npm:^4.2.0"
"@tanstack/react-form": "npm:^1.28.3"
"@tanstack/react-query": "npm:^5.90.21"
"@tanstack/react-router": "npm:^1.162.2"
@@ -7841,7 +7985,7 @@ __metadata:
languageName: node
linkType: hard
"use-sync-external-store@npm:^1.6.0":
"use-sync-external-store@npm:^1.5.0, use-sync-external-store@npm:^1.6.0":
version: 1.6.0
resolution: "use-sync-external-store@npm:1.6.0"
peerDependencies: