157 lines
4.8 KiB
TypeScript
157 lines
4.8 KiB
TypeScript
import { useState, useEffect, lazy, Suspense } from 'react';
|
||
import { Truck, Loader2, LogOut } from 'lucide-react';
|
||
import { DeliveryForm } from './components/delivery/DeliveryForm';
|
||
import { LoginForm } from './components/auth/LoginForm';
|
||
import { ToastContainer } from './components/ui/Toast';
|
||
import { Button } from './components/ui/Button';
|
||
import { useDeliveryStore } from './stores/deliveryStore';
|
||
import { useAuthStore } from './stores/authStore';
|
||
|
||
// Lazy load pages for code splitting
|
||
const Dashboard = lazy(() => import('./pages/Dashboard'));
|
||
const DeliveryListPage = lazy(() => import('./pages/DeliveryListPage'));
|
||
|
||
// Fallback loading component
|
||
const PageLoader = () => (
|
||
<div className="flex items-center justify-center py-20">
|
||
<Loader2 className="w-8 h-8 animate-spin text-[#1B263B]" />
|
||
</div>
|
||
);
|
||
|
||
function App() {
|
||
const [view, setView] = useState<'dashboard' | 'delivery-list'>('dashboard');
|
||
const [selectedDate, setSelectedDate] = useState<string>('');
|
||
const [isFormOpen, setIsFormOpen] = useState(false);
|
||
const [formDate, setFormDate] = useState<string>('');
|
||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||
|
||
const { isAuthenticated, isAuthChecking, restoreAuth, logout } = useAuthStore();
|
||
const addDelivery = useDeliveryStore(state => state.addDelivery);
|
||
const fetchDeliveryCounts = useDeliveryStore(state => state.fetchDeliveryCounts);
|
||
|
||
// Restore auth on mount
|
||
useEffect(() => {
|
||
restoreAuth();
|
||
}, [restoreAuth]);
|
||
|
||
// Refresh counts when form closes (only when authenticated)
|
||
useEffect(() => {
|
||
if (isAuthenticated && !isFormOpen) {
|
||
fetchDeliveryCounts();
|
||
}
|
||
}, [isAuthenticated, isFormOpen, fetchDeliveryCounts]);
|
||
|
||
const handleDateSelect = (date: string) => {
|
||
setSelectedDate(date);
|
||
setView('delivery-list');
|
||
};
|
||
|
||
const handleBackToDashboard = () => {
|
||
setView('dashboard');
|
||
setSelectedDate('');
|
||
};
|
||
|
||
const handleAddDelivery = () => {
|
||
const today = new Date().toLocaleDateString('ru-RU').split('.').join('-');
|
||
setFormDate(today);
|
||
setIsFormOpen(true);
|
||
};
|
||
|
||
const handleFormSubmit = async (data: Parameters<typeof addDelivery>[0]) => {
|
||
setIsSubmitting(true);
|
||
try {
|
||
await addDelivery(data);
|
||
setIsFormOpen(false);
|
||
|
||
// If created for different date, navigate to that date
|
||
const today = new Date().toLocaleDateString('ru-RU').split('.').join('-');
|
||
if (data.date !== today) {
|
||
setSelectedDate(data.date);
|
||
setView('delivery-list');
|
||
}
|
||
} catch {
|
||
// Error is handled by store
|
||
} finally {
|
||
setIsSubmitting(false);
|
||
}
|
||
};
|
||
|
||
// Show loading while checking auth
|
||
if (isAuthChecking) {
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center bg-[#fbf8fb]">
|
||
<Loader2 className="w-8 h-8 animate-spin text-[#1B263B]" />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Show login form if not authenticated
|
||
if (!isAuthenticated) {
|
||
return (
|
||
<>
|
||
<LoginForm />
|
||
<ToastContainer />
|
||
</>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-[#fbf8fb]">
|
||
<header className="sticky top-0 z-40 bg-[#1B263B] text-white shadow-md">
|
||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||
<div className="flex items-center justify-between h-14">
|
||
<div className="flex items-center gap-3">
|
||
<div className="p-2 bg-white/10 rounded-lg">
|
||
<Truck size={24} className="text-white" />
|
||
</div>
|
||
<h1 className="text-lg font-semibold hidden sm:block">Delivery Tracker</h1>
|
||
</div>
|
||
<div className="flex items-center gap-4">
|
||
<div className="text-sm text-white/70">
|
||
{view === 'dashboard' ? 'Панель управления' : `Доставки на ${selectedDate}`}
|
||
</div>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
onClick={logout}
|
||
className="text-white hover:bg-white/10"
|
||
>
|
||
<LogOut size={18} className="mr-1" />
|
||
Выйти
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||
<Suspense fallback={<PageLoader />}>
|
||
{view === 'dashboard' ? (
|
||
<Dashboard
|
||
onDateSelect={handleDateSelect}
|
||
onAddDelivery={handleAddDelivery}
|
||
/>
|
||
) : (
|
||
<DeliveryListPage
|
||
selectedDate={selectedDate}
|
||
onBack={handleBackToDashboard}
|
||
/>
|
||
)}
|
||
</Suspense>
|
||
</main>
|
||
|
||
<DeliveryForm
|
||
isOpen={isFormOpen}
|
||
onClose={() => setIsFormOpen(false)}
|
||
onSubmit={handleFormSubmit}
|
||
defaultDate={formDate}
|
||
isSubmitting={isSubmitting}
|
||
/>
|
||
|
||
<ToastContainer />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default App;
|