// RentFlow Manager Desktop — App shell: macOS window chrome + sidebar nav + state. const { useState, useEffect, useMemo, useRef } = React; const CATEGORIES = ['Tontechnik', 'Anhänger', 'Licht', 'Sonstiges']; const NAV = [ { id: 'dashboard', label: 'Übersicht', icon: icons.home }, { id: 'rentals', label: 'Mietaufträge', icon: icons.doc }, { id: 'calendar', label: 'Kalender', icon: icons.cal }, { id: 'equipment', label: 'Equipment', icon: icons.truck }, { id: 'finance', label: 'Finanzen', icon: icons.euro }, { id: 'inbox', label: 'Posteingang', icon: icons.mail }, { id: 'documents', label: 'Dokumente', icon: icons.fileText }, ]; // ── Convert email → rental dialog ── function ConvertSheet({ open, email, equipment, onClose, onCreate }) { const t = useTheme(); const p = email && email.parsed; const guessId = p ? matchEquipmentId(p.equipmentHint, equipment) : (equipment[0] && equipment[0].id); const [form, setForm] = useState({}); useEffect(() => { if (open && email) { const eqId = guessId || (equipment[0] && equipment[0].id); const eq = equipment.find((e) => e.id === eqId); setForm({ tenantName: email.from, email: email.fromEmail, phone: (p && p.phone) || '', address: (p && p.deliveryAddress) || '', equipmentId: eqId, purpose: (p && p.purpose) || '', start: (p && p.startISO) || todayISO(), end: (p && p.endISO) || tomorrowISO(), startTime: '14:00', endTime: '11:00', dailyRate: eq ? eq.price : 0, deposit: 100, quantity: 1, depositStatus: 'offen', paymentStatus: 'offen', }); } }, [open, email && email.id]); if (!email) return null; const set = (k, v) => setForm((f) => ({ ...f, [k]: v })); const eq = equipment.find((e) => e.id === form.equipmentId); const days = daysBetween(form.start || todayISO(), form.end || todayISO()); return (
Anfrage umwandeln
Aus der E-Mail einen Mietvorgang erstellen.
Abbrechen
set('tenantName', e.target.value)}/>
{equipment.map((e) => ( { set('equipmentId', e.id); set('dailyRate', e.price); }} scale={0.96}>
{e.name}
))}
set('start', e.target.value)}/> set('end', e.target.value)}/>
set('purpose', e.target.value)}/>
{days} Tag{days > 1 ? 'e' : ''} × {form.dailyRate || 0} €
{rentalTotal(form)} €
{ const status = autoRentalStatus(form.start, form.end); onCreate({ ...form, id: 'r-' + Date.now(), status, equipmentName: eq ? eq.name : '', idCard: '' }); onClose(); }} scale={0.98} style={{ marginTop: 16 }}>
Mietvorgang erstellen
); } function App() { const [dark, setDark] = useLocal('rf-d-dark', false); const theme = dark ? darkTheme : lightTheme; const [screen, setScreen] = useState('dashboard'); const [nav, setNav] = useState({}); const [equipment, setEquipment] = useLocal('rf-d-equipment', SEED_EQUIPMENT); const [rentals, setRentals] = useLocal('rf-d-rentals', SEED_RENTALS); const [events, setEvents] = useLocal('rf-d-events', SEED_EVENTS); const [tasks, setTasks] = useLocal('rf-d-tasks', { items: [], autoDone: {} }); const [company, setCompany] = useLocal('rf-d-company', DEFAULT_COMPANY); const [categories, setCategories] = useLocal('rf-d-categories', CATEGORIES); const [docs, setDocs] = useLocal('rf-d-docs-v3', typeof SEED_DOCS !== 'undefined' ? SEED_DOCS : []); const [blocked, setBlocked] = useLocal('rf-d-blocked', []); const [convert, setConvert] = useState(null); // recompute auto statuses on load useEffect(() => { setRentals((prev) => prev.map((r) => ({ ...r, status: autoRentalStatus(r.start, r.end) }))); }, []); const go = (s, params = {}) => { setScreen(s); setNav({ ...params, _t: Date.now() }); }; const onConvert = (email) => setConvert(email); const createFromEmail = (r) => { setRentals((prev) => [r, ...prev]); setConvert(null); go('rentals', { rentalId: r.id }); }; const reqCount = useMemo(() => window.withParsed(window.SEED_EMAILS).filter((m) => !m.archived && m.kind === 'request').length, []); const activeCount = rentals.filter((r) => r.status === 'aktiv').length; const navBadge = { rentals: activeCount, inbox: reqCount }; return (
{/* Sidebar */}
{/* sidebar header */}
RentFlow
Verwaltung
{NAV.map((n) => { const sel = screen === n.id; return ( go(n.id)} scale={0.99} hoverBg={sel ? undefined : theme.chip} style={{ borderRadius: 8, marginBottom: 1 }}>
{n.label}
); })}
{/* footer: profile + theme */}
go('profile')} scale={0.99} hoverBg={theme.chip} style={{ borderRadius: 10 }}>
{initials(company.owner)}
{company.owner}
{company.name}
{/* Main content */}
{screen === 'dashboard' && setDark((v) => !v)}/>} {screen === 'rentals' && } {screen === 'calendar' && } {screen === 'equipment' && } {screen === 'finance' && } {screen === 'inbox' && } {screen === 'documents' && } {screen === 'profile' && setDark((v) => !v)} data={{ equipment, setEquipment, rentals, setRentals, events, setEvents, tasks, setTasks, categories, setCategories, docs, setDocs, blocked, setBlocked, }}/>}
setConvert(null)} onCreate={createFromEmail}/>
); } ReactDOM.createRoot(document.getElementById('root')).render();