From 2a0d65de2fe0b555a9ddd3836b6f7c87da4c1d3f Mon Sep 17 00:00:00 2001 From: MarcWieland Date: Wed, 29 Jul 2026 17:24:34 +0200 Subject: [PATCH] Neuste Version der Seite mit Drag n Drop --- src/App.tsx | 17 +- src/components/CustomerModal.tsx | 371 +++++++ src/components/DocumentViewerModal.tsx | 215 +++++ src/components/InstalledPartModal.tsx | 357 +++++++ src/components/ScheduleItemModal.tsx | 431 +++++++++ src/components/Sidebar.tsx | 98 +- src/components/TechnicianModal.tsx | 319 ++++++ src/components/WorkOrderModal.tsx | 309 ++++++ src/index.css | 13 +- src/services/supabaseService.ts | 1125 +++++++++++++++++++++- src/types.ts | 75 +- src/views/CustomersView.tsx | 890 +++++++++++++++++ src/views/DashboardView.tsx | 185 +++- src/views/DispatchingInteractiveView.tsx | 678 +++++++++++++ src/views/DispatchingView.tsx | 291 +++++- src/views/EquipmentView.tsx | 813 ++++++++++++++++ src/views/InventoryView.tsx | 226 ++++- src/views/TechniciansView.tsx | 334 ++++++- 18 files changed, 6572 insertions(+), 175 deletions(-) create mode 100644 src/components/CustomerModal.tsx create mode 100644 src/components/DocumentViewerModal.tsx create mode 100644 src/components/InstalledPartModal.tsx create mode 100644 src/components/ScheduleItemModal.tsx create mode 100644 src/components/TechnicianModal.tsx create mode 100644 src/components/WorkOrderModal.tsx create mode 100644 src/views/CustomersView.tsx create mode 100644 src/views/DispatchingInteractiveView.tsx create mode 100644 src/views/EquipmentView.tsx diff --git a/src/App.tsx b/src/App.tsx index 4a83a64..d5cda4b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,24 +2,37 @@ import React, { useState } from 'react'; import { Menu, Wrench } from 'lucide-react'; import { Sidebar } from './components/Sidebar'; import { DashboardView } from './views/DashboardView'; +import { CustomersView } from './views/CustomersView'; import { DispatchingView } from './views/DispatchingView'; import { InventoryView } from './views/InventoryView'; +import { EquipmentView } from './views/EquipmentView'; import { TechniciansView } from './views/TechniciansView'; import { TimeTrackingView } from './views/TimeTrackingView'; import { SupabaseSyncView } from './views/SupabaseSyncView'; export const App: React.FC = () => { - const [activeTab, setActiveTab] = useState('dashboard'); + const [activeTab, setActiveTabState] = useState(() => { + return localStorage.getItem('handwerksfreund_active_tab') || 'dashboard'; + }); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const setActiveTab = (tab: string) => { + setActiveTabState(tab); + localStorage.setItem('handwerksfreund_active_tab', tab); + }; + const renderView = () => { switch (activeTab) { case 'dashboard': - return ; + return ; + case 'kunden': + return ; case 'dispatching': return ; case 'lager': return ; + case 'geraete': + return ; case 'monteure': return ; case 'zeiterfassung': diff --git a/src/components/CustomerModal.tsx b/src/components/CustomerModal.tsx new file mode 100644 index 0000000..e104ea7 --- /dev/null +++ b/src/components/CustomerModal.tsx @@ -0,0 +1,371 @@ +import React, { useState, useEffect } from 'react'; +import { X, Save, Building, MapPin, Phone, Mail, FileText } from 'lucide-react'; +import { Customer } from '../types'; + +interface CustomerModalProps { + isOpen: boolean; + onClose: () => void; + onSave: (customer: Omit | Customer) => Promise; + initialCustomer?: Customer | null; +} + +export const CustomerModal: React.FC = ({ + isOpen, + onClose, + onSave, + initialCustomer +}) => { + const [formData, setFormData] = useState({ + customerNumber: '', + name: '', + street: '', + zipCode: '', + city: '', + phone: '', + email: '', + notes: '', + latitude: 50.1109, + longitude: 8.6821, + distanceKm: 2.5 + }); + const [isSaving, setIsSaving] = useState(false); + const [errorMsg, setErrorMsg] = useState(''); + + useEffect(() => { + if (initialCustomer) { + setFormData({ + customerNumber: initialCustomer.customerNumber || '', + name: initialCustomer.name || '', + street: initialCustomer.street || '', + zipCode: initialCustomer.zipCode || '', + city: initialCustomer.city || '', + phone: initialCustomer.phone || '', + email: initialCustomer.email || '', + notes: initialCustomer.notes || '', + latitude: initialCustomer.latitude || 50.1109, + longitude: initialCustomer.longitude || 8.6821, + distanceKm: initialCustomer.distanceKm || 2.5 + }); + } else { + setFormData({ + customerNumber: `K-${Math.floor(10000 + Math.random() * 90000)}`, + name: '', + street: '', + zipCode: '', + city: '', + phone: '', + email: '', + notes: '', + latitude: 50.1109, + longitude: 8.6821, + distanceKm: 2.5 + }); + } + setErrorMsg(''); + }, [initialCustomer, isOpen]); + + if (!isOpen) return null; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!formData.name.trim() || !formData.street.trim() || !formData.city.trim()) { + setErrorMsg('Bitte Name, Straße und Ort ausfüllen.'); + return; + } + + try { + setIsSaving(true); + setErrorMsg(''); + if (initialCustomer) { + await onSave({ ...initialCustomer, ...formData }); + } else { + await onSave(formData); + } + onClose(); + } catch (err: any) { + setErrorMsg(err.message || 'Fehler beim Speichern des Kunden'); + } finally { + setIsSaving(false); + } + }; + + return ( +
+
+ {/* Modal Header */} +
+

+ + {initialCustomer ? 'Kunde bearbeiten' : 'Neuen Kunden anlegen'} +

+ +
+ + {/* Form Body */} +
+ {errorMsg && ( +
+ {errorMsg} +
+ )} + +
+
+ + setFormData({ ...formData, customerNumber: e.target.value })} + required + style={{ + width: '100%', + padding: '10px 14px', + borderRadius: '8px', + border: '1px solid #cbd5e1', + fontSize: '0.95rem' + }} + /> +
+ +
+ + setFormData({ ...formData, name: e.target.value })} + placeholder="z.B. Max Mustermann" + required + style={{ + width: '100%', + padding: '10px 14px', + borderRadius: '8px', + border: '1px solid #cbd5e1', + fontSize: '0.95rem' + }} + /> +
+
+ +
+ +
+ + setFormData({ ...formData, street: e.target.value })} + placeholder="Musterweg 12" + required + style={{ + width: '100%', + padding: '10px 14px 10px 38px', + borderRadius: '8px', + border: '1px solid #cbd5e1', + fontSize: '0.95rem' + }} + /> +
+
+ +
+
+ + setFormData({ ...formData, zipCode: e.target.value })} + placeholder="12345" + required + style={{ + width: '100%', + padding: '10px 14px', + borderRadius: '8px', + border: '1px solid #cbd5e1', + fontSize: '0.95rem' + }} + /> +
+ +
+ + setFormData({ ...formData, city: e.target.value })} + placeholder="Musterstadt" + required + style={{ + width: '100%', + padding: '10px 14px', + borderRadius: '8px', + border: '1px solid #cbd5e1', + fontSize: '0.95rem' + }} + /> +
+
+ +
+
+ +
+ + setFormData({ ...formData, phone: e.target.value })} + placeholder="0171 1234567" + style={{ + width: '100%', + padding: '10px 14px 10px 38px', + borderRadius: '8px', + border: '1px solid #cbd5e1', + fontSize: '0.95rem' + }} + /> +
+
+ +
+ +
+ + setFormData({ ...formData, email: e.target.value })} + placeholder="kunden@email.de" + style={{ + width: '100%', + padding: '10px 14px 10px 38px', + borderRadius: '8px', + border: '1px solid #cbd5e1', + fontSize: '0.95rem' + }} + /> +
+
+
+ +
+ +
+ +