🚀 Auto-deploy: BrainWind atualizado em 01/09/2026 16:55:27

This commit is contained in:
2026-09-01 16:55:27 +00:00
parent f66ae1a3aa
commit 3219011b33
16 changed files with 82 additions and 32 deletions
+35
View File
@@ -0,0 +1,35 @@
import { useState, useEffect } from 'react';
import { create } from 'zustand';
interface PersistentStore {
data: Record<string, any>;
setValue: (key: string, value: any) => void;
}
export const usePersistentStore = create<PersistentStore>((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<T>(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];
}
+1
View File
@@ -14,6 +14,7 @@ import { WindParametersSummary } from '@/components/WindParametersSummary';
import { EducationalManual } from '@/components/EducationalManual'; import { EducationalManual } from '@/components/EducationalManual';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { usePersistentState } from '@/hooks/usePersistentState';
const BarSelectorModule: React.FC = () => { const BarSelectorModule: React.FC = () => {
const { vk, q } = useWindStore(); const { vk, q } = useWindStore();
+2 -1
View File
@@ -16,6 +16,7 @@ import ExportMenu from '../components/ExportMenu';
import { EducationalManual } from '@/components/EducationalManual'; import { EducationalManual } from '@/components/EducationalManual';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; 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 [fv, setFv] = React.useState(0.6);
const [vk, setVk] = React.useState(45); const [vk, setVk] = React.useState(45);
const [alpha, setAlpha] = React.useState(0); 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 [vf, setVf] = React.useState(100); // Velocidade crítica de flutter da seção
const classification = useMemo( const classification = useMemo(
+5 -4
View File
@@ -9,12 +9,13 @@ import { EducationalManual } from '@/components/EducationalManual';
import { Wind, Box } from 'lucide-react'; import { Wind, Box } from 'lucide-react';
import { Slider } from '@/components/ui/slider'; import { Slider } from '@/components/ui/slider';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { usePersistentState } from '@/hooks/usePersistentState';
export default function CalcCaModule() { export default function CalcCaModule() {
const [l1, setL1] = useState<number>(20); const [l1, setL1] = usePersistentState<number>('CalcCaModule_l1', 20);
const [l2, setL2] = useState<number>(40); const [l2, setL2] = usePersistentState<number>('CalcCaModule_l2', 40);
const [h, setH] = useState<number>(10); const [h, setH] = usePersistentState<number>('CalcCaModule_h', 10);
const [viewMode, setViewMode] = useState<'solid' | 'airflow'>('solid'); const [viewMode, setViewMode] = usePersistentState<'solid' | 'airflow'>('CalcCaModule_viewMode', 'solid');
// Clamp inputs to avoid Infinity or negative values breaking the UI // Clamp inputs to avoid Infinity or negative values breaking the UI
const safeL1 = Math.max(0.1, l1 || 1); const safeL1 = Math.max(0.1, l1 || 1);
+1
View File
@@ -17,6 +17,7 @@ import { PressureLegend } from '@/components/PressureLegend';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { usePersistentState } from '@/hooks/usePersistentState';
const CylinderModule: React.FC = () => { const CylinderModule: React.FC = () => {
const { vk, cpi: globalCpi } = useWindStore(); const { vk, cpi: globalCpi } = useWindStore();
+1
View File
@@ -17,6 +17,7 @@ import { PressureLegend } from '@/components/PressureLegend';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { usePersistentState } from '@/hooks/usePersistentState';
const DomeModule: React.FC = () => { const DomeModule: React.FC = () => {
const { vk, cpi } = useWindStore(); const { vk, cpi } = useWindStore();
+2 -1
View File
@@ -31,10 +31,11 @@ import { EducationalManual } from '@/components/EducationalManual';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { usePersistentState } from '@/hooks/usePersistentState';
const DynamicsModule: React.FC = () => { const DynamicsModule: React.FC = () => {
const { v0, s1, s3, terrainCategory, structureClass, q } = useWindStore(); 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<StructureDynamicType>('concrete-shearwall'); const [structureType, setStructureType] = React.useState<StructureDynamicType>('concrete-shearwall');
const [height, setHeight] = React.useState(40); const [height, setHeight] = React.useState(40);
+1
View File
@@ -23,6 +23,7 @@ import { PressureLegend } from '@/components/PressureLegend';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { useHydrationStore } from '@/store/hydrationStore'; import { useHydrationStore } from '@/store/hydrationStore';
import { usePersistentState } from '@/hooks/usePersistentState';
const GalpaoModule: React.FC = () => { const GalpaoModule: React.FC = () => {
const { const {
+1
View File
@@ -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 { Warehouse, Cylinder, Church, CircleDot, Square, Layers, BarChart3, Frame, Tent, Calculator } from 'lucide-react';
import { useI18n } from '../store/i18nStore'; import { useI18n } from '../store/i18nStore';
import { useWindStore } from '../store/appStore'; import { useWindStore } from '../store/appStore';
import { usePersistentState } from '@/hooks/usePersistentState';
// Mocked icons for bridge and tower to match App.tsx // Mocked icons for bridge and tower to match App.tsx
const BridgeIcon = (props: React.SVGProps<SVGSVGElement>) => ( const BridgeIcon = (props: React.SVGProps<SVGSVGElement>) => (
+4 -3
View File
@@ -15,15 +15,16 @@ import { PressureLegend } from '@/components/PressureLegend';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { usePersistentState } from '@/hooks/usePersistentState';
const IsolatedRoofModule: React.FC = () => { const IsolatedRoofModule: React.FC = () => {
const { q } = useWindStore(); const { q } = useWindStore();
const [type, setType] = React.useState<'shed' | 'gable' | 'butterfly'>('shed'); const [type, setType] = React.useState<'shed' | 'gable' | 'butterfly'>('shed');
const [theta, setTheta] = React.useState(15); const [theta, setTheta] = React.useState(15);
const [height, setHeight] = React.useState(2); const [height, setHeight] = React.useState(2);
const [width, setWidth] = useState(10); const [width, setWidth] = usePersistentState('IsolatedRoofModule_width', 10);
const [depth, setDepth] = useState(10); const [depth, setDepth] = usePersistentState('IsolatedRoofModule_depth', 10);
const [viewMode, setViewMode] = useState<'solid' | 'airflow'>('solid'); const [viewMode, setViewMode] = usePersistentState<'solid' | 'airflow'>('IsolatedRoofModule_viewMode', 'solid');
const result = useMemo(() => { const result = useMemo(() => {
if (type === 'shed') { if (type === 'shed') {
+9 -8
View File
@@ -18,17 +18,18 @@ import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generi
import { Plus, Trash2, Info, AlertTriangle } from 'lucide-react'; import { Plus, Trash2, Info, AlertTriangle } from 'lucide-react';
import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from '@/components/ui/tooltip';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { usePersistentState } from '@/hooks/usePersistentState';
const PiperackModule: React.FC = () => { const PiperackModule: React.FC = () => {
const { q } = useWindStore(); const { q } = useWindStore();
const [width, setWidth] = useState(17); const [width, setWidth] = usePersistentState('PiperackModule_width', 17);
const [height, setHeight] = useState(2); const [height, setHeight] = usePersistentState('PiperackModule_height', 2);
const [elevation, setElevation] = useState(6); const [elevation, setElevation] = usePersistentState('PiperackModule_elevation', 6);
const [spacing, setSpacing] = useState(2); const [spacing, setSpacing] = usePersistentState('PiperackModule_spacing', 2);
const [numFrames, setNumFrames] = useState(2); const [numFrames, setNumFrames] = usePersistentState('PiperackModule_numFrames', 2);
const [phiStruct, setPhiStruct] = useState(0.5); const [phiStruct, setPhiStruct] = usePersistentState('PiperackModule_phiStruct', 0.5);
const [f1, setF1] = useState(1.5); // Frequência natural (Hz) const [f1, setF1] = usePersistentState('PiperackModule_f1', 1.5); // Frequência natural (Hz)
const [pipes, setPipes] = useState<PipeDef[]>([]); const [pipes, setPipes] = usePersistentState<PipeDef[]>('PiperackModule_pipes', []);
const pendingLoad = useHydrationStore((s) => s.pendingLoad); const pendingLoad = useHydrationStore((s) => s.pendingLoad);
const clearPendingLoad = useHydrationStore((s) => s.clearPendingLoad); const clearPendingLoad = useHydrationStore((s) => s.clearPendingLoad);
+2 -1
View File
@@ -28,7 +28,7 @@ const SettingsModule: React.FC = () => {
const fileInputRef = useRef<HTMLInputElement | null>(null); const fileInputRef = useRef<HTMLInputElement | null>(null);
const logoInputRef = useRef<HTMLInputElement | null>(null); const logoInputRef = useRef<HTMLInputElement | null>(null);
const [importing, setImporting] = useState(false); const [importing, setImporting] = usePersistentState('SettingsModule_importing', false);
const { companyLogo, setCompanyLogo } = useWindStore(); const { companyLogo, setCompanyLogo } = useWindStore();
const exportSnapshot = () => { const exportSnapshot = () => {
@@ -93,6 +93,7 @@ const SettingsModule: React.FC = () => {
await save(newProject as any); await save(newProject as any);
} else { } else {
// Old snapshot import fallback // Old snapshot import fallback
import { usePersistentState } from '@/hooks/usePersistentState';
importProjectFromText(text); importProjectFromText(text);
} }
} catch (e) { } catch (e) {
+14 -13
View File
@@ -22,6 +22,7 @@ import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { Eye, Box, Layers, ArrowUpRight, CheckCircle2, Wind, AlertTriangle } from 'lucide-react'; import { Eye, Box, Layers, ArrowUpRight, CheckCircle2, Wind, AlertTriangle } from 'lucide-react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { usePersistentState } from '@/hooks/usePersistentState';
const conditionLabels: Record<ShelterCondition, string> = { const conditionLabels: Record<ShelterCondition, string> = {
cond1: '1. Estanque 3 lados (Fundo + Lados)', cond1: '1. Estanque 3 lados (Fundo + Lados)',
@@ -32,19 +33,19 @@ const conditionLabels: Record<ShelterCondition, string> = {
const ShelterModule: React.FC = () => { const ShelterModule: React.FC = () => {
const { q } = useWindStore(); const { q } = useWindStore();
const [condition, setCondition] = useState<ShelterCondition>('cond1'); const [condition, setCondition] = usePersistentState<ShelterCondition>('ShelterModule_condition', 'cond1');
const [windAngle, setWindAngle] = useState<ShelterWindAngle>(0); const [windAngle, setWindAngle] = usePersistentState<ShelterWindAngle>('ShelterModule_windAngle', 0);
const [depth, setDepth] = useState(6); const [depth, setDepth] = usePersistentState('ShelterModule_depth', 6);
const [width, setWidth] = useState(10); const [width, setWidth] = usePersistentState('ShelterModule_width', 10);
const [height, setHeight] = useState(4.8); const [height, setHeight] = usePersistentState('ShelterModule_height', 4.8);
const [theta, setTheta] = useState(5); const [theta, setTheta] = usePersistentState('ShelterModule_theta', 5);
const [numColumns, setNumColumns] = useState(2); const [numColumns, setNumColumns] = usePersistentState('ShelterModule_numColumns', 2);
const [backClosure, setBackClosure] = useState(75); const [backClosure, setBackClosure] = usePersistentState('ShelterModule_backClosure', 75);
const [backRange, setBackRange] = useState<[number, number]>([0, 75]); const [backRange, setBackRange] = usePersistentState<[number, number]>('ShelterModule_backRange', [0, 75]);
const [leftClosure, setLeftClosure] = useState(100); const [leftClosure, setLeftClosure] = usePersistentState('ShelterModule_leftClosure', 100);
const [rightClosure, setRightClosure] = useState(100); const [rightClosure, setRightClosure] = usePersistentState('ShelterModule_rightClosure', 100);
const [surfaceMass, setSurfaceMass] = useState(15); // kg/m² para lona/telha leve const [surfaceMass, setSurfaceMass] = usePersistentState('ShelterModule_surfaceMass', 15); // kg/m² para lona/telha leve
const [viewMode, setViewMode] = useState<'3d' | 'elevation' | 'airflow'>('3d'); const [viewMode, setViewMode] = usePersistentState<'3d' | 'elevation' | 'airflow'>('ShelterModule_viewMode', '3d');
const pendingLoad = useHydrationStore((s) => s.pendingLoad); const pendingLoad = useHydrationStore((s) => s.pendingLoad);
const clearPendingLoad = useHydrationStore((s) => s.clearPendingLoad); const clearPendingLoad = useHydrationStore((s) => s.clearPendingLoad);
+1
View File
@@ -16,6 +16,7 @@ import { PressureLegend } from '@/components/PressureLegend';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { usePersistentState } from '@/hooks/usePersistentState';
const SignModule: React.FC = () => { const SignModule: React.FC = () => {
const { q } = useWindStore(); const { q } = useWindStore();
+2 -1
View File
@@ -13,6 +13,7 @@ import { EducationalManual } from '@/components/EducationalManual';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { usePersistentState } from '@/hooks/usePersistentState';
const TowerModule: React.FC = () => { const TowerModule: React.FC = () => {
const { q } = useWindStore(); const { q } = useWindStore();
@@ -23,7 +24,7 @@ const TowerModule: React.FC = () => {
const [panels, setPanels] = React.useState(5); const [panels, setPanels] = React.useState(5);
const [phi, setPhi] = React.useState(0.2); const [phi, setPhi] = React.useState(0.2);
const [alphaWind, setAlphaWind] = React.useState<0 | 45 | 90>(0); 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 result = useMemo(() => {
const aFace = baseWidth * height; const aFace = baseWidth * height;
+1
View File
@@ -17,6 +17,7 @@ import { PressureLegend } from '@/components/PressureLegend';
import { WindParametersSummary } from '@/components/WindParametersSummary'; import { WindParametersSummary } from '@/components/WindParametersSummary';
import { SaveModuleDialog } from '@/components/SaveModuleDialog'; import { SaveModuleDialog } from '@/components/SaveModuleDialog';
import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf'; import { exportGenericToPDF, type GenericPDFSection } from '../lib/export-generic-pdf';
import { usePersistentState } from '@/hooks/usePersistentState';
const VaultModule: React.FC = () => { const VaultModule: React.FC = () => {
const { vk } = useWindStore(); const { vk } = useWindStore();