From d3b1597b745fe569df4acfe5d62cb60dacbee7c5 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 15 Jul 2026 11:22:37 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Auto-deploy:=20BrainWind=20atual?= =?UTF-8?q?izado=20em=2015/07/2026=2011:22:37?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/App.tsx | 77 ++++-- app/src/components/TermsOfUseDialog.tsx | 25 +- app/src/components/ui/dropdown-menu.tsx | 255 +++++++++++++++++++ app/src/lib/__tests__/import-project.test.ts | 24 +- 4 files changed, 331 insertions(+), 50 deletions(-) create mode 100644 app/src/components/ui/dropdown-menu.tsx diff --git a/app/src/App.tsx b/app/src/App.tsx index aa6fe76..4827a56 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -14,8 +14,15 @@ import PiperackModule from './pages/PiperackModule'; import { Home, Settings, Menu, Cylinder, Church, CircleDot, Square, Layers, BarChart3, Activity, Warehouse, - Settings2, Sun, Moon, Frame, FolderOpen, BookOpen + Settings2, Sun, Moon, Frame, FolderOpen, BookOpen, MoreVertical, Info } from 'lucide-react'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { useState } from 'react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; @@ -220,16 +227,48 @@ function AppLayout({ children }: { children: React.ReactNode }) { > {effectiveTheme === 'dark' ? : } - - - + + + + + + e.preventDefault()} className="cursor-pointer gap-2"> + Parâmetros do Vento + + } /> + + + Arquivos + + + + + Configurações + + + + + Glossário + + + + e.preventDefault()} className="cursor-pointer gap-2"> + Termos de Uso + + } /> + +
{children}
@@ -280,22 +319,6 @@ function AppLayout({ children }: { children: React.ReactNode }) { ); })} - - {/* Parâmetros do Vento (Fixo na direita) */} - -
- -
- Vento - - } - /> ); diff --git a/app/src/components/TermsOfUseDialog.tsx b/app/src/components/TermsOfUseDialog.tsx index 255b4e9..796666b 100644 --- a/app/src/components/TermsOfUseDialog.tsx +++ b/app/src/components/TermsOfUseDialog.tsx @@ -6,24 +6,27 @@ import { useState } from 'react'; interface TermsOfUseDialogProps { isCollapsed?: boolean; + trigger?: React.ReactNode; } -export function TermsOfUseDialog({ isCollapsed = false }: TermsOfUseDialogProps) { +export function TermsOfUseDialog({ isCollapsed = false, trigger }: TermsOfUseDialogProps) { const [open, setOpen] = useState(false); return ( - + {trigger ? trigger : ( + + )} diff --git a/app/src/components/ui/dropdown-menu.tsx b/app/src/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..492bef2 --- /dev/null +++ b/app/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,255 @@ +import * as React from "react" +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" +import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" + +function DropdownMenu({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + {children} + + + ) +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +} diff --git a/app/src/lib/__tests__/import-project.test.ts b/app/src/lib/__tests__/import-project.test.ts index eab1e9a..30a883d 100644 --- a/app/src/lib/__tests__/import-project.test.ts +++ b/app/src/lib/__tests__/import-project.test.ts @@ -82,7 +82,7 @@ describe('M9.7 — parseProjectJson', () => { describe('M9.7 — detectFormat', () => { it('Detecta SavedProject', () => { - expect(detectFormat({ module: 'galpao', inputs: {} })).toBe('saved-project'); + expect(detectFormat({ type: 'projeto', module: 'galpao', inputs: {} })).toBe('saved-project'); }); it('Detecta snapshot do windStore', () => { @@ -106,7 +106,7 @@ describe('M9.7 — validateSavedProject', () => { it('Aceita SavedProject válido', () => { const project = { name: 'Galpão Teste', - module: 'galpao', + type: 'projeto', module: 'galpao', inputs: {}, createdAt: 1000, updatedAt: 2000, @@ -117,28 +117,28 @@ describe('M9.7 — validateSavedProject', () => { }); it('Rejeita projeto sem name', () => { - const project = { module: 'galpao', inputs: {} }; + const project = { type: 'projeto', module: 'galpao', inputs: {} }; const v = validateSavedProject(project); expect(v.ok).toBe(false); expect(v.errors.some((e) => e.includes('name'))).toBe(true); }); it('Rejeita módulo inválido', () => { - const project = { name: 'X', module: 'invalido', inputs: {} }; + const project = { name: 'X', type: 'projeto', module: 'invalido', inputs: {} }; const v = validateSavedProject(project); expect(v.ok).toBe(false); expect(v.errors.some((e) => e.includes('module'))).toBe(true); }); it('Rejeita inputs não-objeto', () => { - const project = { name: 'X', module: 'galpao', inputs: 'não-objeto' }; + const project = { name: 'X', type: 'projeto', module: 'galpao', inputs: 'não-objeto' }; const v = validateSavedProject(project); expect(v.ok).toBe(false); expect(v.errors.some((e) => e.includes('inputs'))).toBe(true); }); it('Emite warning se timestamps faltarem', () => { - const project = { name: 'X', module: 'galpao', inputs: {} }; + const project = { name: 'X', type: 'projeto', module: 'galpao', inputs: {} }; const v = validateSavedProject(project); expect(v.warnings.length).toBeGreaterThan(0); }); @@ -177,7 +177,7 @@ describe('M9.7 — applySavedProject', () => { it('Aplica wind.v0 corretamente', () => { const project: SavedProject = { name: 'Teste', - module: 'galpao', + type: 'projeto', module: 'galpao', inputs: { wind: { v0: 50 } }, createdAt: Date.now(), updatedAt: Date.now(), @@ -190,7 +190,7 @@ describe('M9.7 — applySavedProject', () => { it('Aplica múltiplos campos do windStore', () => { const project: SavedProject = { name: 'Teste', - module: 'galpao', + type: 'projeto', module: 'galpao', inputs: { wind: { v0: 45, @@ -215,7 +215,7 @@ describe('M9.7 — applySavedProject', () => { it('Aplica galpaoStore quando module=galpao', () => { const project: SavedProject = { name: 'Galpão', - module: 'galpao', + type: 'projeto', module: 'galpao', inputs: { galpao: { width: 20, @@ -243,7 +243,7 @@ describe('M9.7 — applySavedProject', () => { it('Não aplica galpaoStore quando module ≠ galpao', () => { const project: SavedProject = { name: 'Cilindro', - module: 'cilindro', + type: 'projeto', module: 'cilindro', inputs: { galpao: { width: 20 } }, createdAt: Date.now(), updatedAt: Date.now(), @@ -255,7 +255,7 @@ describe('M9.7 — applySavedProject', () => { it('Adiciona warning para categoria inválida', () => { const project: SavedProject = { name: 'Teste', - module: 'galpao', + type: 'projeto', module: 'galpao', inputs: { wind: { terrainCategory: 'INVALID' } }, createdAt: Date.now(), updatedAt: Date.now(), @@ -281,7 +281,7 @@ describe('M9.7 — importProjectFromText (orquestrador)', () => { it('Roundtrip: export → import preserva campos principais', () => { const project: SavedProject = { name: 'Roundtrip', - module: 'galpao', + type: 'projeto', module: 'galpao', inputs: { wind: { v0: 45, s1: 1, s3Group: 2 }, galpao: { width: 18, length: 35, height: 7 },