Files
ICON-CRM/legacy/App.tsx
T
2026-09-14 16:55:29 +02:00

150 lines
5.9 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Sidebar } from './components/Sidebar';
import { Header } from './components/Header';
import { Dashboard } from './components/Dashboard';
import { KanbanBoard } from './components/KanbanBoard';
import { Tenders } from './components/Tenders';
import { Customers } from './components/Customers';
import { Experts } from './components/Experts';
import { Projects } from './components/Projects';
import { OrgChart } from './components/OrgChart';
import { Settings } from './components/Settings';
import { ViewState, Tender, Expert, Customer, Project } from './types';
import { supabase, TABLES } from './lib/supabase';
const App: React.FC = () => {
const [currentView, setCurrentView] = useState<ViewState>('Dashboard');
const [isDarkMode, setIsDarkMode] = useState<boolean>(true);
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [tenders, setTenders] = useState<Tender[]>([]);
const [customers, setCustomers] = useState<Customer[]>([]);
const [experts, setExperts] = useState<Expert[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const [isInitialLoading, setIsInitialLoading] = useState(true);
const [isRefreshing, setIsRefreshing] = useState(false);
const fetchData = async (showRefreshIndicator = false) => {
if (showRefreshIndicator) setIsRefreshing(true);
try {
const { data: customersData } = await supabase
.from(TABLES.CUSTOMERS)
.select('*, icon_contacts(*)');
const formattedCustomers = (customersData || []).map(c => ({
...c,
contacts: c.icon_contacts || []
}));
setCustomers(formattedCustomers);
const { data: expertsData } = await supabase.from(TABLES.EXPERTS).select('*');
setExperts(expertsData || []);
const { data: projectsData } = await supabase.from(TABLES.PROJECTS).select('*');
setProjects(projectsData || []);
const { data: tendersData } = await supabase
.from(TABLES.TENDERS)
.select(`
*,
selectedCustomer:icon_customers(*),
selectedExperts:icon_tender_experts(icon_experts(*))
`)
.neq('status', 'Archiviert');
if (tendersData) {
const formattedTenders: Tender[] = tendersData.map(t => ({
id: t.id,
title: t.title,
source: t.source,
link: t.link,
details: t.details,
notes: t.notes,
status: t.status,
kanbanStatus: t.kanban_status,
selectedCustomer: t.selectedCustomer,
selectedExperts: t.selectedExperts?.map((se: any) => se.icon_experts) || [],
applicantsCount: t.selectedExperts?.length || 0
}));
setTenders(formattedTenders);
}
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setIsInitialLoading(false);
setIsRefreshing(false);
}
};
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
if (isDarkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [isDarkMode]);
const toggleDarkMode = () => setIsDarkMode(!isDarkMode);
if (isInitialLoading) {
return (
<div className="h-screen w-screen flex items-center justify-center bg-slate-950">
<div className="flex flex-col items-center gap-4">
<div className="w-12 h-12 border-4 border-brand-500 border-t-transparent rounded-full animate-spin"></div>
<p className="text-white font-bold tracking-widest animate-pulse">ICON DATABASE CONNECTING...</p>
</div>
</div>
);
}
const renderContent = () => {
switch (currentView) {
case 'Dashboard': return <Dashboard />;
case 'Live!': return <KanbanBoard tenders={tenders} setTenders={setTenders} refreshData={() => fetchData(true)} />;
case 'Ausschreibungen': return <Tenders tenders={tenders} setTenders={setTenders} customers={customers} experts={experts} refreshData={() => fetchData(true)} />;
case 'Kunden': return <Customers customers={customers} refreshData={() => fetchData(true)} />;
case 'Experten': return <Experts experts={experts} setExperts={setExperts} refreshData={() => fetchData(true)} />;
case 'Laufende Projekte': return <Projects projects={projects} setProjects={setProjects} customers={customers} experts={experts} refreshData={() => fetchData(true)} />;
case 'Organigramm': return <OrgChart customers={customers} projects={projects} experts={experts} />;
case 'Einstellungen': return <Settings isDarkMode={isDarkMode} onToggleDarkMode={toggleDarkMode} />;
default: return <Dashboard />;
}
};
return (
<div className="flex h-screen overflow-hidden bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100 font-sans">
<Sidebar
currentView={currentView}
onViewChange={setCurrentView}
isOpen={isSidebarOpen}
onToggle={() => setIsSidebarOpen(!isSidebarOpen)}
/>
<main className="flex-1 flex flex-col min-w-0 overflow-hidden">
<Header
title={currentView}
isDarkMode={isDarkMode}
onToggleDarkMode={toggleDarkMode}
onToggleSidebar={() => setIsSidebarOpen(!isSidebarOpen)}
/>
<div className="flex-1 overflow-y-auto p-4 md:p-8 relative">
{isRefreshing && (
<div className="absolute top-4 right-8 z-50 flex items-center gap-2 bg-white/80 dark:bg-slate-900/80 px-4 py-2 rounded-full shadow-lg border border-slate-100 dark:border-slate-800 backdrop-blur-sm animate-in fade-in slide-in-from-top-2">
<div className="w-4 h-4 border-2 border-brand-500 border-t-transparent rounded-full animate-spin"></div>
<span className="text-[10px] font-bold uppercase tracking-widest">Synchronisiere...</span>
</div>
)}
{renderContent()}
</div>
</main>
</div>
);
};
export default App;