diff --git a/src/App.tsx b/src/App.tsx index 0120272..e8b8131 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,130 +5,120 @@ import { ConfigStep } from './components/ConfigStep'; import { LayoutStep } from './components/LayoutStep'; import { PreviewStep } from './components/PreviewStep'; import { parseShareUrl } from './utils/share'; -import { ChevronRight, ChevronLeft, Box } from 'lucide-react'; +import { ChevronRight, ChevronLeft, Box, AlertTriangle } from 'lucide-react'; + +// ВАЖНО: ErrorBoundary должен быть ЗДЕСЬ, снаружи компонента App +class ErrorBoundary extends React.Component<{children: React.ReactNode}, {hasError: boolean}> { + state = { hasError: false }; + static getDerivedStateFromError() { return { hasError: true }; } + render() { + if (this.state.hasError) return ( +
+ +

Ошибка отрисовки интерфейса.

+ +
+ ); + return this.props.children; + } +} const App = () => { const [step, setStep] = useState(1); - const [isLoadedFromUrl, setIsLoadedFromUrl] = useState(false); - // State const [config, setConfig] = useState({ drawer: { width: 300, depth: 400, height: 80 }, wallThickness: 1.2, printerTolerance: 0.5, - cornerRadius: 4, // <--- Дефолтное скругление (4мм) + cornerRadius: 4, }); const [splits, setSplits] = useState({ x: [], - y: [] + y: [], + partitions: {} }); - // --- ЛОГИКА ВОССТАНОВЛЕНИЯ ИЗ ССЫЛКИ --- useEffect(() => { - const sharedData = parseShareUrl(); - if (sharedData) { - setConfig(sharedData.config); - setSplits(sharedData.splits); - setStep(3); - setIsLoadedFromUrl(true); - window.history.replaceState({}, '', window.location.pathname); + try { + const sharedData = parseShareUrl(); + if (sharedData) { + setConfig(sharedData.config); + setSplits({ + x: Array.isArray(sharedData.splits.x) ? sharedData.splits.x : [], + y: Array.isArray(sharedData.splits.y) ? sharedData.splits.y : [], + partitions: sharedData.splits.partitions || {} + }); + setStep(3); + window.history.replaceState({}, '', window.location.pathname); + } + } catch (e) { + console.error("URL load error", e); } }, []); - // Derived State: Parts const parts: GeneratedPart[] = useMemo(() => { - return calculateParts(config, splits); + try { + return calculateParts(config, splits); + } catch (e) { + return []; + } }, [config, splits]); return ( -
+
{/* Header */} -
+
-
- -
-
-

PrintFit

-

Генератор органайзеров

-
+
+

PrintFit

Генератор органайзеров

- - {/* Progress Stepper */}
{[1, 2, 3].map((num) => ( - -
- - {num} - - - {num === 1 ? 'Настройки' : num === 2 ? 'Макет' : 'Экспорт'} - -
- {num < 3 &&
} - +
+ {num} + {num === 1 ? 'Настройки' : num === 2 ? 'Макет' : 'Экспорт'} +
))}
{/* Main Content */} -
+
{step === 1 && ( -
- -
+
+ +
)} - + {step === 2 && ( -
- +
+ + +
)} {step === 3 && ( -
+
)}
- {/* Footer Navigation */} -