From 3219011b330d1c3eaf05adeb1b10ab536bde80f2 Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 1 Sep 2026 16:55:27 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Auto-deploy:=20BrainWind=20atual?= =?UTF-8?q?izado=20em=2001/09/2026=2016:55:27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/hooks/usePersistentState.ts | 35 ++++++++++++++++++++++++++++ app/src/pages/BarSelectorModule.tsx | 1 + app/src/pages/BridgeModule.tsx | 3 ++- app/src/pages/CalcCaModule.tsx | 9 +++---- app/src/pages/CylinderModule.tsx | 1 + app/src/pages/DomeModule.tsx | 1 + app/src/pages/DynamicsModule.tsx | 3 ++- app/src/pages/GalpaoModule.tsx | 1 + app/src/pages/HomeMock.tsx | 1 + app/src/pages/IsolatedRoofModule.tsx | 7 +++--- app/src/pages/PiperackModule.tsx | 17 +++++++------- app/src/pages/SettingsModule.tsx | 3 ++- app/src/pages/ShelterModule.tsx | 27 ++++++++++----------- app/src/pages/SignModule.tsx | 1 + app/src/pages/TowerModule.tsx | 3 ++- app/src/pages/VaultModule.tsx | 1 + 16 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 app/src/hooks/usePersistentState.ts diff --git a/app/src/hooks/usePersistentState.ts b/app/src/hooks/usePersistentState.ts new file mode 100644 index 0000000..3a2bb45 --- /dev/null +++ b/app/src/hooks/usePersistentState.ts @@ -0,0 +1,35 @@ +import { useState, useEffect } from 'react'; +import { create } from 'zustand'; + +interface PersistentStore { + data: Record; + setValue: (key: string, value: any) => void; +} + +export const usePersistentStore = create((set) => ({ + data: {}, + setValue: (key, value) => set((state) => ({ data: { ...state.data, [key]: value } })), +})); + +/** + * Hook para substituir o useState e manter o valor salvo globalmente na memória (Zustand) + * durante a navegação entre os componentes. + */ +export function usePersistentState(key: string, initialValue: T): [T, (val: T | ((prev: T) => T)) => void] { + const storeValue = usePersistentStore((s) => s.data[key]); + const setValue = usePersistentStore((s) => s.setValue); + + // Como o store não tem o valor inicial no momento da criação, precisamos lidar com isso. + // Se o valor estiver no store, usamos ele. Senão, usamos initialValue. + const value = storeValue !== undefined ? storeValue : initialValue; + + const setter = (newVal: T | ((prev: T) => T)) => { + if (typeof newVal === 'function') { + setValue(key, (newVal as any)(value)); + } else { + setValue(key, newVal); + } + }; + + return [value, setter]; +} diff --git a/app/src/pages/BarSelectorModule.tsx b/app/src/pages/BarSelectorModule.tsx index fccdb5b..a515925 100644 --- a/app/src/pages/BarSelectorModule.tsx +++ b/app/src/pages/BarSelectorModule.tsx @@ -14,6 +14,7 @@ import { WindParametersSummary } from '@/components/WindParametersSummary'; import { EducationalManual } from '@/components/EducationalManual'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; const BarSelectorModule: React.FC = () => { const { vk, q } = useWindStore(); diff --git a/app/src/pages/BridgeModule.tsx b/app/src/pages/BridgeModule.tsx index 165f721..f3fb2d9 100644 --- a/app/src/pages/BridgeModule.tsx +++ b/app/src/pages/BridgeModule.tsx @@ -16,6 +16,7 @@ import ExportMenu from '../components/ExportMenu'; import { EducationalManual } from '@/components/EducationalManual'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; @@ -48,7 +49,7 @@ const BridgeModule: React.FC = () => { const [fv, setFv] = React.useState(0.6); const [vk, setVk] = React.useState(45); const [alpha, setAlpha] = React.useState(0); - const [viewMode, setViewMode] = useState<'solid' | 'airflow'>('solid'); + const [viewMode, setViewMode] = usePersistentState<'solid' | 'airflow'>('BridgeModule_viewMode', 'solid'); const [vf, setVf] = React.useState(100); // Velocidade crítica de flutter da seção const classification = useMemo( diff --git a/app/src/pages/CalcCaModule.tsx b/app/src/pages/CalcCaModule.tsx index 7d91032..c82ed38 100644 --- a/app/src/pages/CalcCaModule.tsx +++ b/app/src/pages/CalcCaModule.tsx @@ -9,12 +9,13 @@ import { EducationalManual } from '@/components/EducationalManual'; import { Wind, Box } from 'lucide-react'; import { Slider } from '@/components/ui/slider'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { usePersistentState } from '@/hooks/usePersistentState'; export default function CalcCaModule() { - const [l1, setL1] = useState(20); - const [l2, setL2] = useState(40); - const [h, setH] = useState(10); - const [viewMode, setViewMode] = useState<'solid' | 'airflow'>('solid'); + const [l1, setL1] = usePersistentState('CalcCaModule_l1', 20); + const [l2, setL2] = usePersistentState('CalcCaModule_l2', 40); + const [h, setH] = usePersistentState('CalcCaModule_h', 10); + const [viewMode, setViewMode] = usePersistentState<'solid' | 'airflow'>('CalcCaModule_viewMode', 'solid'); // Clamp inputs to avoid Infinity or negative values breaking the UI const safeL1 = Math.max(0.1, l1 || 1); diff --git a/app/src/pages/CylinderModule.tsx b/app/src/pages/CylinderModule.tsx index 54673bc..eb5e5c8 100644 --- a/app/src/pages/CylinderModule.tsx +++ b/app/src/pages/CylinderModule.tsx @@ -17,6 +17,7 @@ import { PressureLegend } from '@/components/PressureLegend'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; const CylinderModule: React.FC = () => { const { vk, cpi: globalCpi } = useWindStore(); diff --git a/app/src/pages/DomeModule.tsx b/app/src/pages/DomeModule.tsx index 06ec19f..a3e32c2 100644 --- a/app/src/pages/DomeModule.tsx +++ b/app/src/pages/DomeModule.tsx @@ -17,6 +17,7 @@ import { PressureLegend } from '@/components/PressureLegend'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; const DomeModule: React.FC = () => { const { vk, cpi } = useWindStore(); diff --git a/app/src/pages/DynamicsModule.tsx b/app/src/pages/DynamicsModule.tsx index 345fde0..94a645a 100644 --- a/app/src/pages/DynamicsModule.tsx +++ b/app/src/pages/DynamicsModule.tsx @@ -31,10 +31,11 @@ import { EducationalManual } from '@/components/EducationalManual'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; +import { usePersistentState } from '@/hooks/usePersistentState'; const DynamicsModule: React.FC = () => { const { v0, s1, s3, terrainCategory, structureClass, q } = useWindStore(); - const [activeTab, setActiveTab] = useState('edificio'); + const [activeTab, setActiveTab] = usePersistentState('DynamicsModule_activeTab', 'edificio'); const [structureType, setStructureType] = React.useState('concrete-shearwall'); const [height, setHeight] = React.useState(40); diff --git a/app/src/pages/GalpaoModule.tsx b/app/src/pages/GalpaoModule.tsx index 92cf955..10a059f 100644 --- a/app/src/pages/GalpaoModule.tsx +++ b/app/src/pages/GalpaoModule.tsx @@ -23,6 +23,7 @@ import { PressureLegend } from '@/components/PressureLegend'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { useHydrationStore } from '@/store/hydrationStore'; +import { usePersistentState } from '@/hooks/usePersistentState'; const GalpaoModule: React.FC = () => { const { diff --git a/app/src/pages/HomeMock.tsx b/app/src/pages/HomeMock.tsx index 2472353..53572f8 100644 --- a/app/src/pages/HomeMock.tsx +++ b/app/src/pages/HomeMock.tsx @@ -2,6 +2,7 @@ import { Link } from 'react-router-dom'; import { Warehouse, Cylinder, Church, CircleDot, Square, Layers, BarChart3, Frame, Tent, Calculator } from 'lucide-react'; import { useI18n } from '../store/i18nStore'; import { useWindStore } from '../store/appStore'; +import { usePersistentState } from '@/hooks/usePersistentState'; // Mocked icons for bridge and tower to match App.tsx const BridgeIcon = (props: React.SVGProps) => ( diff --git a/app/src/pages/IsolatedRoofModule.tsx b/app/src/pages/IsolatedRoofModule.tsx index 8cce3d7..bf36ab7 100644 --- a/app/src/pages/IsolatedRoofModule.tsx +++ b/app/src/pages/IsolatedRoofModule.tsx @@ -15,15 +15,16 @@ import { PressureLegend } from '@/components/PressureLegend'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; const IsolatedRoofModule: React.FC = () => { const { q } = useWindStore(); const [type, setType] = React.useState<'shed' | 'gable' | 'butterfly'>('shed'); const [theta, setTheta] = React.useState(15); const [height, setHeight] = React.useState(2); - const [width, setWidth] = useState(10); - const [depth, setDepth] = useState(10); - const [viewMode, setViewMode] = useState<'solid' | 'airflow'>('solid'); + const [width, setWidth] = usePersistentState('IsolatedRoofModule_width', 10); + const [depth, setDepth] = usePersistentState('IsolatedRoofModule_depth', 10); + const [viewMode, setViewMode] = usePersistentState<'solid' | 'airflow'>('IsolatedRoofModule_viewMode', 'solid'); const result = useMemo(() => { if (type === 'shed') { diff --git a/app/src/pages/PiperackModule.tsx b/app/src/pages/PiperackModule.tsx index 427855e..c3ff719 100644 --- a/app/src/pages/PiperackModule.tsx +++ b/app/src/pages/PiperackModule.tsx @@ -18,17 +18,18 @@ import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generi import { Plus, Trash2, Info, AlertTriangle } from 'lucide-react'; import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from '@/components/ui/tooltip'; import { Input } from '@/components/ui/input'; +import { usePersistentState } from '@/hooks/usePersistentState'; const PiperackModule: React.FC = () => { const { q } = useWindStore(); - const [width, setWidth] = useState(17); - const [height, setHeight] = useState(2); - const [elevation, setElevation] = useState(6); - const [spacing, setSpacing] = useState(2); - const [numFrames, setNumFrames] = useState(2); - const [phiStruct, setPhiStruct] = useState(0.5); - const [f1, setF1] = useState(1.5); // Frequência natural (Hz) - const [pipes, setPipes] = useState([]); + const [width, setWidth] = usePersistentState('PiperackModule_width', 17); + const [height, setHeight] = usePersistentState('PiperackModule_height', 2); + const [elevation, setElevation] = usePersistentState('PiperackModule_elevation', 6); + const [spacing, setSpacing] = usePersistentState('PiperackModule_spacing', 2); + const [numFrames, setNumFrames] = usePersistentState('PiperackModule_numFrames', 2); + const [phiStruct, setPhiStruct] = usePersistentState('PiperackModule_phiStruct', 0.5); + const [f1, setF1] = usePersistentState('PiperackModule_f1', 1.5); // Frequência natural (Hz) + const [pipes, setPipes] = usePersistentState('PiperackModule_pipes', []); const pendingLoad = useHydrationStore((s) => s.pendingLoad); const clearPendingLoad = useHydrationStore((s) => s.clearPendingLoad); diff --git a/app/src/pages/SettingsModule.tsx b/app/src/pages/SettingsModule.tsx index a9e289f..f2719b6 100644 --- a/app/src/pages/SettingsModule.tsx +++ b/app/src/pages/SettingsModule.tsx @@ -28,7 +28,7 @@ const SettingsModule: React.FC = () => { const fileInputRef = useRef(null); const logoInputRef = useRef(null); - const [importing, setImporting] = useState(false); + const [importing, setImporting] = usePersistentState('SettingsModule_importing', false); const { companyLogo, setCompanyLogo } = useWindStore(); const exportSnapshot = () => { @@ -93,6 +93,7 @@ const SettingsModule: React.FC = () => { await save(newProject as any); } else { // Old snapshot import fallback +import { usePersistentState } from '@/hooks/usePersistentState'; importProjectFromText(text); } } catch (e) { diff --git a/app/src/pages/ShelterModule.tsx b/app/src/pages/ShelterModule.tsx index f47cc8a..da86e63 100644 --- a/app/src/pages/ShelterModule.tsx +++ b/app/src/pages/ShelterModule.tsx @@ -22,6 +22,7 @@ import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { Eye, Box, Layers, ArrowUpRight, CheckCircle2, Wind, AlertTriangle } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { usePersistentState } from '@/hooks/usePersistentState'; const conditionLabels: Record = { cond1: '1. Estanque 3 lados (Fundo + Lados)', @@ -32,19 +33,19 @@ const conditionLabels: Record = { const ShelterModule: React.FC = () => { const { q } = useWindStore(); - const [condition, setCondition] = useState('cond1'); - const [windAngle, setWindAngle] = useState(0); - const [depth, setDepth] = useState(6); - const [width, setWidth] = useState(10); - const [height, setHeight] = useState(4.8); - const [theta, setTheta] = useState(5); - const [numColumns, setNumColumns] = useState(2); - const [backClosure, setBackClosure] = useState(75); - const [backRange, setBackRange] = useState<[number, number]>([0, 75]); - const [leftClosure, setLeftClosure] = useState(100); - const [rightClosure, setRightClosure] = useState(100); - const [surfaceMass, setSurfaceMass] = useState(15); // kg/m² para lona/telha leve - const [viewMode, setViewMode] = useState<'3d' | 'elevation' | 'airflow'>('3d'); + const [condition, setCondition] = usePersistentState('ShelterModule_condition', 'cond1'); + const [windAngle, setWindAngle] = usePersistentState('ShelterModule_windAngle', 0); + const [depth, setDepth] = usePersistentState('ShelterModule_depth', 6); + const [width, setWidth] = usePersistentState('ShelterModule_width', 10); + const [height, setHeight] = usePersistentState('ShelterModule_height', 4.8); + const [theta, setTheta] = usePersistentState('ShelterModule_theta', 5); + const [numColumns, setNumColumns] = usePersistentState('ShelterModule_numColumns', 2); + const [backClosure, setBackClosure] = usePersistentState('ShelterModule_backClosure', 75); + const [backRange, setBackRange] = usePersistentState<[number, number]>('ShelterModule_backRange', [0, 75]); + const [leftClosure, setLeftClosure] = usePersistentState('ShelterModule_leftClosure', 100); + const [rightClosure, setRightClosure] = usePersistentState('ShelterModule_rightClosure', 100); + const [surfaceMass, setSurfaceMass] = usePersistentState('ShelterModule_surfaceMass', 15); // kg/m² para lona/telha leve + const [viewMode, setViewMode] = usePersistentState<'3d' | 'elevation' | 'airflow'>('ShelterModule_viewMode', '3d'); const pendingLoad = useHydrationStore((s) => s.pendingLoad); const clearPendingLoad = useHydrationStore((s) => s.clearPendingLoad); diff --git a/app/src/pages/SignModule.tsx b/app/src/pages/SignModule.tsx index 8b08236..8393c62 100644 --- a/app/src/pages/SignModule.tsx +++ b/app/src/pages/SignModule.tsx @@ -16,6 +16,7 @@ import { PressureLegend } from '@/components/PressureLegend'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; const SignModule: React.FC = () => { const { q } = useWindStore(); diff --git a/app/src/pages/TowerModule.tsx b/app/src/pages/TowerModule.tsx index 8992069..47104bf 100644 --- a/app/src/pages/TowerModule.tsx +++ b/app/src/pages/TowerModule.tsx @@ -13,6 +13,7 @@ import { EducationalManual } from '@/components/EducationalManual'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; const TowerModule: React.FC = () => { const { q } = useWindStore(); @@ -23,7 +24,7 @@ const TowerModule: React.FC = () => { const [panels, setPanels] = React.useState(5); const [phi, setPhi] = React.useState(0.2); const [alphaWind, setAlphaWind] = React.useState<0 | 45 | 90>(0); - const [viewMode, setViewMode] = useState<'solid' | 'airflow'>('solid'); + const [viewMode, setViewMode] = usePersistentState<'solid' | 'airflow'>('TowerModule_viewMode', 'solid'); const result = useMemo(() => { const aFace = baseWidth * height; diff --git a/app/src/pages/VaultModule.tsx b/app/src/pages/VaultModule.tsx index 69ec345..b5e09cb 100644 --- a/app/src/pages/VaultModule.tsx +++ b/app/src/pages/VaultModule.tsx @@ -17,6 +17,7 @@ import { PressureLegend } from '@/components/PressureLegend'; import { WindParametersSummary } from '@/components/WindParametersSummary'; import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; +import { usePersistentState } from '@/hooks/usePersistentState'; const VaultModule: React.FC = () => { const { vk } = useWindStore();