Files
BoxGenerator/src/App.tsx
Халимов Рустам ac00068175 Rollback
2025-12-27 23:33:57 +03:00

139 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useMemo, useEffect } from 'react';
import { AppConfig, LayoutSplits, GeneratedPart } from './types';
import { calculateParts } from './services/geometryGenerator';
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';
const App = () => {
const [step, setStep] = useState(1);
const [isLoadedFromUrl, setIsLoadedFromUrl] = useState(false);
// State
const [config, setConfig] = useState<AppConfig>({
drawer: { width: 300, depth: 400, height: 80 },
wallThickness: 1.2,
printerTolerance: 0.5,
cornerRadius: 4,
});
const [splits, setSplits] = useState<LayoutSplits>({
x: [],
y: []
});
// --- ЛОГИКА ВОССТАНОВЛЕНИЯ ИЗ ССЫЛКИ ---
useEffect(() => {
const sharedData = parseShareUrl();
if (sharedData) {
setConfig(sharedData.config);
setSplits(sharedData.splits);
setStep(3);
setIsLoadedFromUrl(true);
window.history.replaceState({}, '', window.location.pathname);
}
}, []);
// Derived State: Parts
const parts: GeneratedPart[] = useMemo(() => {
return calculateParts(config, splits);
}, [config, splits]);
return (
<div className="min-h-screen flex flex-col font-sans text-gray-100 bg-slate-950">
{/* Header */}
<header className="bg-slate-900 border-b border-slate-800 p-4 shadow-md sticky top-0 z-50">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-2">
<div className="bg-primary p-2 rounded-lg">
<Box className="text-white" size={20} />
</div>
<div>
<h1 className="text-xl font-bold tracking-tight">PrintFit</h1>
<p className="text-xs text-gray-400">Генератор органайзеров</p>
</div>
</div>
{/* Progress Stepper */}
<div className="flex items-center gap-4 text-sm font-medium">
{[1, 2, 3].map((num) => (
<React.Fragment key={num}>
<div className={`flex items-center gap-2 ${step === num ? 'text-primary' : 'text-gray-500'}`}>
<span className={`w-6 h-6 rounded-full flex items-center justify-center text-xs border ${step === num ? 'border-primary bg-primary/10' : 'border-gray-600'}`}>
{num}
</span>
<span className="hidden md:inline">
{num === 1 ? 'Настройки' : num === 2 ? 'Макет' : 'Экспорт'}
</span>
</div>
{num < 3 && <div className="w-8 h-[1px] bg-slate-700" />}
</React.Fragment>
))}
</div>
</div>
</header>
{/* Main Content */}
<main className="flex-1 max-w-7xl mx-auto w-full p-4 md:p-8">
{step === 1 && (
<div className="max-w-4xl mx-auto animate-fade-in">
<ConfigStep config={config} onChange={setConfig} />
</div>
)}
{step === 2 && (
<div className="h-[calc(100vh-200px)] min-h-[500px] animate-fade-in">
<LayoutStep config={config} splits={splits} onChange={setSplits} />
</div>
)}
{step === 3 && (
<div className="h-[calc(100vh-200px)] min-h-[600px] animate-fade-in">
<PreviewStep parts={parts} config={config} splits={splits} />
</div>
)}
</main>
{/* Footer Navigation */}
<footer className="bg-slate-900 border-t border-slate-800 p-4 sticky bottom-0 z-50">
<div className="max-w-7xl mx-auto flex justify-between items-center">
<button
disabled={step === 1}
onClick={() => setStep(s => Math.max(1, s - 1))}
className="flex items-center gap-2 px-6 py-3 rounded-lg font-semibold bg-slate-800 text-white disabled:opacity-50 disabled:cursor-not-allowed hover:bg-slate-700 transition-colors"
>
<ChevronLeft size={18} /> Назад
</button>
<div className="text-sm text-gray-500">
{step === 2 && <span className="text-accent font-mono">Ячеек: {parts.length}</span>}
</div>
{step < 3 ? (
<button
onClick={() => setStep(s => Math.min(3, s + 1))}
className="flex items-center gap-2 px-6 py-3 rounded-lg font-semibold bg-primary text-white hover:bg-blue-600 shadow-lg shadow-blue-900/20 transition-all active:scale-95"
>
Далее <ChevronRight size={18} />
</button>
) : (
<button
onClick={() => {
setStep(1);
setSplits({x: [], y: []});
window.history.replaceState({}, '', window.location.pathname);
}}
className="flex items-center gap-2 px-6 py-3 rounded-lg font-semibold text-gray-400 hover:text-white transition-colors border border-transparent hover:border-slate-700"
>
Новый проект
</button>
)}
</div>
</footer>
</div>
);
};
export default App;