Files
Handwerksfreund_Frontend/src/components/InstalledPartModal.tsx
T
2026-08-05 18:51:37 +02:00

346 lines
12 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { X, Save, Wrench, User, Calendar, Tag, Hash, Building2 } from 'lucide-react';
import { InstalledPart, Customer } from '../types';
interface InstalledPartModalProps {
isOpen: boolean;
onClose: () => void;
onSave: (part: Omit<InstalledPart, 'id'> | InstalledPart) => Promise<void>;
customers: Customer[];
initialPart?: InstalledPart | null;
defaultCustomerId?: string;
}
export const InstalledPartModal: React.FC<InstalledPartModalProps> = ({
isOpen,
onClose,
onSave,
customers,
initialPart,
defaultCustomerId
}) => {
const [formData, setFormData] = useState({
customerId: '',
name: '',
category: 'Gas-Brennwertgerät',
manufacturer: 'Buderus',
serialNumber: '',
quantity: 1,
installedAt: new Date().toISOString().split('T')[0]
});
const [isSaving, setIsSaving] = useState(false);
const [errorMsg, setErrorMsg] = useState('');
useEffect(() => {
if (initialPart) {
setFormData({
customerId: initialPart.customerId || '',
name: initialPart.name || '',
category: initialPart.category || 'Gas-Brennwertgerät',
manufacturer: initialPart.manufacturer || 'Buderus',
serialNumber: initialPart.serialNumber || '',
quantity: initialPart.quantity || 1,
installedAt: initialPart.installedAt || new Date().toISOString().split('T')[0]
});
} else {
setFormData({
customerId: defaultCustomerId || (customers.length > 0 ? customers[0].id : ''),
name: '',
category: 'Gas-Brennwertgerät',
manufacturer: 'Buderus',
serialNumber: '',
quantity: 1,
installedAt: new Date().toISOString().split('T')[0]
});
}
setErrorMsg('');
}, [initialPart, defaultCustomerId, customers, isOpen]);
if (!isOpen) return null;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!formData.customerId) {
setErrorMsg('Bitte einen Kunden auswählen.');
return;
}
if (!formData.name.trim()) {
setErrorMsg('Bitte Produktbezeichnung angeben.');
return;
}
try {
setIsSaving(true);
setErrorMsg('');
if (initialPart) {
await onSave({ ...initialPart, ...formData });
} else {
await onSave(formData);
}
onClose();
} catch (err: any) {
setErrorMsg(err.message || 'Fehler beim Speichern des Geräts');
} finally {
setIsSaving(false);
}
};
return (
<div style={{
position: 'fixed',
inset: 0,
backgroundColor: 'rgba(15, 23, 42, 0.6)',
backdropFilter: 'blur(4px)',
zIndex: 100,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '20px'
}}>
<div style={{
backgroundColor: '#ffffff',
borderRadius: '16px',
maxWidth: '550px',
width: '100%',
maxHeight: '90vh',
overflowY: 'auto',
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
border: '1px solid #e2e8f0'
}}>
{/* Header */}
<div style={{
padding: '20px 24px',
borderBottom: '1px solid #e2e8f0',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#f8fafc',
borderTopLeftRadius: '16px',
borderTopRightRadius: '16px'
}}>
<h2 style={{ fontSize: '1.25rem', fontWeight: 700, color: '#0f172a', margin: 0, display: 'flex', alignItems: 'center', gap: '10px' }}>
<Wrench size={22} color="#2563eb" />
{initialPart ? 'Verbautes Gerät bearbeiten' : 'Gerät zur Kartei hinzufügen'}
</h2>
<button
onClick={onClose}
style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#64748b', borderRadius: '8px', padding: '4px' }}
>
<X size={20} />
</button>
</div>
{/* Form Body */}
<form onSubmit={handleSubmit} style={{ padding: '24px' }}>
{errorMsg && (
<div style={{
backgroundColor: '#fef2f2',
color: '#991b1b',
padding: '12px 16px',
borderRadius: '8px',
marginBottom: '20px',
fontSize: '0.9rem',
fontWeight: 500
}}>
{errorMsg}
</div>
)}
{/* Customer */}
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '6px' }}>
Zugehöriger Kunde *
</label>
<div style={{ position: 'relative' }}>
<User size={18} color="#94a3b8" style={{ position: 'absolute', left: '12px', top: '12px' }} />
<select
value={formData.customerId}
onChange={(e) => setFormData({ ...formData, customerId: e.target.value })}
required
style={{
width: '100%',
padding: '10px 14px 10px 38px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
fontSize: '0.95rem',
backgroundColor: '#ffffff'
}}
>
<option value="" disabled>-- Bitte Kunde auswählen --</option>
{customers.map((c) => (
<option key={c.id} value={c.id}>
{c.name} ({c.customerNumber})
</option>
))}
</select>
</div>
</div>
{/* Product Name */}
<div style={{ marginBottom: '16px' }}>
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '6px' }}>
Produktbezeichnung / Modell *
</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
placeholder="z.B. Logamax plus GB172-24"
required
style={{
width: '100%',
padding: '10px 14px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
fontSize: '0.95rem'
}}
/>
</div>
{/* Manufacturer & Category */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', marginBottom: '16px' }}>
<div>
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '6px' }}>
Hersteller / Marke *
</label>
<div style={{ position: 'relative' }}>
<Building2 size={18} color="#94a3b8" style={{ position: 'absolute', left: '12px', top: '12px' }} />
<input
type="text"
value={formData.manufacturer}
onChange={(e) => setFormData({ ...formData, manufacturer: e.target.value })}
placeholder="z.B. Buderus, Bosch, Viessmann"
required
style={{
width: '100%',
padding: '10px 14px 10px 38px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
fontSize: '0.95rem'
}}
/>
</div>
</div>
<div>
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '6px' }}>
Geräte-Typ / Kategorie
</label>
<div style={{ position: 'relative' }}>
<Tag size={18} color="#94a3b8" style={{ position: 'absolute', left: '12px', top: '12px' }} />
<input
type="text"
value={formData.category}
onChange={(e) => setFormData({ ...formData, category: e.target.value })}
placeholder="z.B. Gas-Brennwertgerät, Raumthermostat"
style={{
width: '100%',
padding: '10px 14px 10px 38px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
fontSize: '0.95rem'
}}
/>
</div>
</div>
</div>
{/* Serial Number & Quantity */}
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', marginBottom: '16px' }}>
<div>
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '6px' }}>
Seriennummer
</label>
<div style={{ position: 'relative' }}>
<Hash size={18} color="#94a3b8" style={{ position: 'absolute', left: '12px', top: '12px' }} />
<input
type="text"
value={formData.serialNumber}
onChange={(e) => setFormData({ ...formData, serialNumber: e.target.value })}
placeholder="8374747383"
style={{
width: '100%',
padding: '10px 14px 10px 38px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
fontSize: '0.95rem'
}}
/>
</div>
</div>
<div>
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '6px' }}>
Anzahl
</label>
<input
type="number"
min={1}
value={formData.quantity}
onChange={(e) => setFormData({ ...formData, quantity: parseInt(e.target.value) || 1 })}
style={{
width: '100%',
padding: '10px 14px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
fontSize: '0.95rem'
}}
/>
</div>
</div>
{/* Installed At */}
<div style={{ marginBottom: '24px' }}>
<label style={{ display: 'block', fontSize: '0.85rem', fontWeight: 600, color: '#475569', marginBottom: '6px' }}>
Einbaudatum
</label>
<div style={{ position: 'relative' }}>
<Calendar size={18} color="#94a3b8" style={{ position: 'absolute', left: '12px', top: '12px' }} />
<input
type="date"
value={formData.installedAt}
onChange={(e) => setFormData({ ...formData, installedAt: e.target.value })}
style={{
width: '100%',
padding: '10px 14px 10px 38px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
fontSize: '0.95rem'
}}
/>
</div>
</div>
{/* Actions */}
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '12px', paddingTop: '16px', borderTop: '1px solid #f1f5f9' }}>
<button
type="button"
onClick={onClose}
style={{
padding: '10px 18px',
borderRadius: '8px',
border: '1px solid #cbd5e1',
backgroundColor: '#ffffff',
color: '#475569',
fontWeight: 600,
cursor: 'pointer'
}}
>
Abbrechen
</button>
<button
type="submit"
disabled={isSaving}
className="btn-primary-cta"
>
<Save size={18} />
<span>{isSaving ? 'Speichert...' : 'Gerät Speichern'}</span>
</button>
</div>
</form>
</div>
</div>
);
};