Files
BoxGenerator/src/App.tsx
Халимов Рустам 8010c172c7 Add share
2025-12-27 19:04:48 +03:00

144 lines
5.9 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,
});
const [splits, setSplits] = useState<LayoutSplits>({
x: [],
y: []
});
// --- ЛОГИКА ВОССТАНОВЛЕНИЯ ИЗ ССЫЛКИ ---
useEffect(() => {
const sharedData = parseShareUrl();
if (sharedData) {
setConfig(sharedData.config);
setSplits(sharedData.splits);
setStep(3); // Сразу прыгаем на превью
setIsLoadedFromUrl(true);
// Очищаем URL, чтобы он не мозолил глаза (опционально)
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">
{/* Передаем splits, чтобы кнопка Share могла их использовать */}
<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: []});
// Сбрасываем URL если он был
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;