Quellcode des ICON CRM (1)
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Customer, Contact } from '../types';
|
||||
import { supabase, TABLES } from '../lib/supabase';
|
||||
|
||||
interface CustomersProps {
|
||||
customers: Customer[];
|
||||
refreshData: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const Customers: React.FC<CustomersProps> = ({ customers, refreshData }) => {
|
||||
const [selectedCustomerId, setSelectedCustomerId] = useState<string | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [editingCustomer, setEditingCustomer] = useState<Customer | null>(null);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [customerToDeleteId, setCustomerToDeleteId] = useState<string | null>(null);
|
||||
|
||||
const [customerForm, setCustomerForm] = useState({
|
||||
company: '',
|
||||
name: '',
|
||||
logo: '',
|
||||
notes: ''
|
||||
});
|
||||
|
||||
const [contactsForm, setContactsForm] = useState<Partial<Contact>[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedCustomerId && customers.length > 0) {
|
||||
setSelectedCustomerId(customers[0].id);
|
||||
}
|
||||
}, [customers, selectedCustomerId]);
|
||||
|
||||
const activeCustomer = customers.find(c => c.id === selectedCustomerId) || customers[0];
|
||||
|
||||
const handleOpenCreate = () => {
|
||||
setEditingCustomer(null);
|
||||
setCustomerForm({ company: '', name: '', logo: '', notes: '' });
|
||||
setContactsForm([{ name: '', role: '', email: '', phone: '', avatar: '' }]);
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const handleOpenEdit = (customer: Customer) => {
|
||||
setEditingCustomer(customer);
|
||||
setCustomerForm({
|
||||
company: customer.company || '',
|
||||
name: customer.name || '',
|
||||
logo: customer.logo || '',
|
||||
notes: customer.notes || ''
|
||||
});
|
||||
setContactsForm(customer.contacts && customer.contacts.length > 0
|
||||
? customer.contacts
|
||||
: [{ name: '', role: '', email: '', phone: '', avatar: '' }]
|
||||
);
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const handleLogoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => setCustomerForm(prev => ({ ...prev, logo: reader.result as string }));
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
const addContactRow = () => {
|
||||
setContactsForm([...contactsForm, { name: '', role: '', email: '', phone: '', avatar: '' }]);
|
||||
};
|
||||
|
||||
const removeContactRow = (index: number) => {
|
||||
setContactsForm(contactsForm.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
const updateContactField = (index: number, field: keyof Contact, value: string) => {
|
||||
const newContacts = [...contactsForm];
|
||||
newContacts[index] = { ...newContacts[index], [field]: value };
|
||||
setContactsForm(newContacts);
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!customerForm.company.trim()) {
|
||||
alert("Bitte geben Sie einen Firmennamen an.");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSaving(true);
|
||||
|
||||
try {
|
||||
let finalCustomerId = editingCustomer?.id;
|
||||
|
||||
if (editingCustomer) {
|
||||
const { error } = await supabase.from(TABLES.CUSTOMERS).update(customerForm).eq('id', editingCustomer.id);
|
||||
if (error) throw error;
|
||||
} else {
|
||||
const { data, error } = await supabase.from(TABLES.CUSTOMERS).insert([customerForm]).select();
|
||||
if (error) throw error;
|
||||
finalCustomerId = data[0].id;
|
||||
}
|
||||
|
||||
if (finalCustomerId) {
|
||||
await supabase.from(TABLES.CONTACTS).delete().eq('customer_id', finalCustomerId);
|
||||
|
||||
const contactsToInsert = contactsForm
|
||||
.filter(c => c.name && c.name.trim() !== '')
|
||||
.map(c => {
|
||||
const { id, ...cleanContact } = c as any;
|
||||
return {
|
||||
...cleanContact,
|
||||
customer_id: finalCustomerId
|
||||
};
|
||||
});
|
||||
|
||||
if (contactsToInsert.length > 0) {
|
||||
const { error: cError } = await supabase.from(TABLES.CONTACTS).insert(contactsToInsert);
|
||||
if (cError) throw cError;
|
||||
}
|
||||
}
|
||||
|
||||
setIsModalOpen(false);
|
||||
await refreshData();
|
||||
if (!editingCustomer && finalCustomerId) setSelectedCustomerId(finalCustomerId);
|
||||
} catch (error: any) {
|
||||
console.error('Save error:', error);
|
||||
alert('Fehler beim Speichern: ' + (error.message || 'Unbekannter Datenbankfehler'));
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDeleteCustomer = async () => {
|
||||
if (!customerToDeleteId) return;
|
||||
try {
|
||||
await supabase.from(TABLES.CUSTOMERS).delete().eq('id', customerToDeleteId);
|
||||
if (selectedCustomerId === customerToDeleteId) setSelectedCustomerId(null);
|
||||
await refreshData();
|
||||
} catch (error) {
|
||||
console.error('Delete error:', error);
|
||||
} finally {
|
||||
setCustomerToDeleteId(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8 h-full">
|
||||
{/* Sidebar: Firmenliste */}
|
||||
<div className="lg:col-span-1 space-y-4 flex flex-col min-h-0">
|
||||
<button
|
||||
onClick={handleOpenCreate}
|
||||
className="w-full py-4 bg-brand-600 text-white font-extrabold rounded-2xl shadow-xl-brand hover:bg-brand-700 transition-all shrink-0 flex items-center justify-center gap-2 active:scale-95"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M12 4v16m8-8H4" /></svg>
|
||||
Neues Unternehmen
|
||||
</button>
|
||||
|
||||
<div className="flex-1 bg-white dark:bg-slate-900 rounded-[2rem] border-2 border-slate-200 dark:border-slate-800 overflow-hidden flex flex-col shadow-card">
|
||||
<div className="p-5 border-b-2 border-slate-100 dark:border-slate-800 bg-slate-50/50 dark:bg-slate-800/30">
|
||||
<h3 className="font-bold text-[10px] uppercase tracking-[0.2em] text-slate-400">Kundenstamm</h3>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto divide-y divide-slate-100 dark:divide-slate-800">
|
||||
{customers.map(c => (
|
||||
<div
|
||||
key={c.id}
|
||||
onClick={() => setSelectedCustomerId(c.id)}
|
||||
className={`p-5 cursor-pointer transition-all flex items-center justify-between group ${selectedCustomerId === c.id ? 'bg-brand-50/50 dark:bg-brand-500/10' : 'hover:bg-slate-50 dark:hover:bg-slate-800'}`}
|
||||
>
|
||||
<div className="flex items-center gap-4 min-w-0">
|
||||
<div className={`w-12 h-12 rounded-xl overflow-hidden shrink-0 border-2 transition-colors flex items-center justify-center ${selectedCustomerId === c.id ? 'bg-brand-500 text-white border-brand-500 shadow-lg' : 'bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700'}`}>
|
||||
{c.logo ? (
|
||||
<img src={c.logo} alt={c.company} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
<span className={`font-extrabold text-lg ${selectedCustomerId === c.id ? 'text-white' : 'text-brand-500'}`}>{c.company.charAt(0)}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="truncate">
|
||||
<p className={`font-bold text-sm truncate ${selectedCustomerId === c.id ? 'text-brand-700 dark:text-brand-400' : 'text-slate-800 dark:text-slate-200'}`}>{c.company}</p>
|
||||
<p className="text-[10px] text-slate-400 font-bold uppercase mt-0.5 tracking-wider">{c.contacts?.length || 0} Personen</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setCustomerToDeleteId(c.id); }}
|
||||
className="opacity-0 group-hover:opacity-100 p-2 text-slate-300 hover:text-red-500 transition-all hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-3 min-w-0">
|
||||
{activeCustomer ? (
|
||||
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-6 duration-500">
|
||||
<div className="bg-white dark:bg-slate-900 rounded-[3rem] p-10 border-2 border-slate-200 dark:border-slate-800 shadow-card relative overflow-hidden">
|
||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-8">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="w-28 h-28 rounded-[2.5rem] bg-brand-500 text-white flex items-center justify-center font-extrabold text-5xl shadow-xl-brand overflow-hidden border-4 border-white dark:border-slate-800">
|
||||
{activeCustomer.logo ? (
|
||||
<img src={activeCustomer.logo} alt={activeCustomer.company} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
activeCustomer.company.charAt(0)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-4xl font-extrabold text-slate-900 dark:text-white leading-tight tracking-tight">{activeCustomer.company}</h2>
|
||||
<div className="flex items-center gap-3 mt-3">
|
||||
<span className="px-3 py-1 bg-brand-50 dark:bg-brand-500/10 text-brand-600 dark:text-brand-400 text-[10px] font-extrabold uppercase tracking-widest rounded-lg border border-brand-100 dark:border-brand-500/20">PREMIUM KUNDE</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleOpenEdit(activeCustomer)}
|
||||
className="px-10 py-4 bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-200 border-2 border-slate-300 dark:border-slate-700 font-extrabold rounded-2xl hover:border-brand-500 hover:text-brand-600 transition-all shadow-soft active:scale-95"
|
||||
>
|
||||
Firma bearbeiten
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pt-10 border-t-2 border-slate-100 dark:border-slate-800">
|
||||
<h3 className="text-xs font-extrabold text-slate-400 uppercase tracking-[0.25em] mb-6">Unternehmensprofil & Insights</h3>
|
||||
<div className="p-8 bg-slate-50 dark:bg-slate-800/40 rounded-[2rem] border-2 border-slate-200 dark:border-slate-800 text-sm leading-relaxed text-slate-600 dark:text-slate-300 min-h-[120px] whitespace-pre-wrap font-medium">
|
||||
{activeCustomer.notes || 'Keine Firmennotizen hinterlegt.'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="w-2 h-10 bg-brand-500 rounded-full"></span>
|
||||
<h3 className="text-2xl font-extrabold dark:text-white tracking-tight text-slate-900">Ansprechpartner ({activeCustomer.contacts?.length || 0})</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-8 pb-10">
|
||||
{activeCustomer.contacts?.map(contact => (
|
||||
<div key={contact.id} className="bg-white dark:bg-slate-900 p-8 rounded-[2.5rem] border-2 border-slate-200 dark:border-slate-800 shadow-heavy flex flex-col items-center text-center group hover:border-brand-500/50 transition-all relative">
|
||||
<div className="w-24 h-24 rounded-full overflow-hidden mb-6 border-4 border-slate-100 dark:border-slate-800 shadow-lg group-hover:border-brand-500 transition-all shrink-0">
|
||||
{contact.avatar ? (
|
||||
<img src={contact.avatar} alt={contact.name} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
<div className="w-full h-full bg-slate-100 dark:bg-slate-800 flex items-center justify-center font-extrabold text-2xl text-slate-400 uppercase">{contact.name.charAt(0)}</div>
|
||||
)}
|
||||
</div>
|
||||
<h4 className="font-extrabold text-xl dark:text-white leading-tight group-hover:text-brand-600 transition-colors text-slate-900">{contact.name}</h4>
|
||||
<p className="text-[10px] text-brand-600 font-extrabold uppercase tracking-[0.2em] mt-2 mb-8">{contact.role || 'Mitarbeiter'}</p>
|
||||
|
||||
<div className="w-full space-y-3 pt-8 border-t-2 border-slate-50 dark:border-slate-800 mt-auto">
|
||||
<div className="flex items-center gap-3 text-xs font-semibold text-slate-500 justify-center">
|
||||
<svg className="w-4 h-4 text-brand-500 opacity-60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /></svg>
|
||||
<span className="truncate">{contact.email || 'N/A'}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-xs font-semibold text-slate-500 justify-center">
|
||||
<svg className="w-4 h-4 text-brand-500 opacity-60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" /></svg>
|
||||
<span>{contact.phone || 'N/A'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full flex flex-col items-center justify-center p-20 bg-white dark:bg-slate-900 rounded-[3rem] border-2 border-dashed border-slate-300 dark:border-slate-800 text-slate-400 shadow-inner">
|
||||
<div className="w-20 h-20 bg-slate-50 dark:bg-slate-800 rounded-3xl flex items-center justify-center mb-6 text-slate-200">
|
||||
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg>
|
||||
</div>
|
||||
<p className="font-extrabold tracking-widest text-xs uppercase opacity-50">Bereit zur Auswahl</p>
|
||||
<p className="mt-2 font-medium">Wählen Sie ein Unternehmen aus der Liste links aus.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isModalOpen && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/80 backdrop-blur-md animate-in fade-in duration-300">
|
||||
<div className="bg-white dark:bg-slate-900 w-full max-w-5xl rounded-[3rem] shadow-heavy p-10 max-h-[90vh] flex flex-col border border-slate-200 dark:border-slate-800">
|
||||
<div className="flex justify-between items-center mb-10 shrink-0">
|
||||
<h3 className="text-3xl font-black text-slate-900 dark:text-white tracking-tight">
|
||||
{editingCustomer ? 'Firma aktualisieren' : 'Neues Unternehmen anlegen'}
|
||||
</h3>
|
||||
<button onClick={() => setIsModalOpen(false)} className="p-3 bg-slate-50 dark:bg-slate-800 rounded-2xl text-slate-400 hover:text-slate-900 dark:hover:text-white transition-all">
|
||||
<svg className="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M6 18L18 6M6 6l12 12" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="flex-1 overflow-y-auto space-y-10 pr-2">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{/* Logo & Basic Info */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col items-center gap-4 bg-slate-50 dark:bg-slate-800/50 p-8 rounded-[2.5rem] border-2 border-dashed border-slate-200 dark:border-slate-700">
|
||||
<div className="relative group">
|
||||
{customerForm.logo ? (
|
||||
<img src={customerForm.logo} alt="Preview" className="w-32 h-32 rounded-[2rem] object-cover shadow-xl border-4 border-white" />
|
||||
) : (
|
||||
<div className="w-32 h-32 rounded-[2rem] bg-slate-200 dark:bg-slate-700 flex items-center justify-center text-slate-400">
|
||||
<svg className="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>
|
||||
</div>
|
||||
)}
|
||||
<label className="absolute inset-0 flex items-center justify-center bg-black/40 text-white rounded-[2rem] opacity-0 group-hover:opacity-100 cursor-pointer transition-opacity font-bold text-xs uppercase">
|
||||
Hochladen
|
||||
<input type="file" className="hidden" accept="image/*" onChange={handleLogoUpload} />
|
||||
</label>
|
||||
</div>
|
||||
<p className="text-[10px] text-slate-400 uppercase font-black tracking-widest">Firmenlogo (optional)</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 block">Unternehmensname*</label>
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
value={customerForm.company}
|
||||
onChange={e => setCustomerForm({...customerForm, company: e.target.value})}
|
||||
className="w-full px-6 py-4 bg-slate-100 dark:bg-slate-800 border-2 border-transparent focus:border-brand-500 rounded-2xl outline-none font-bold text-slate-900 dark:text-white transition-all shadow-inner"
|
||||
placeholder="Z.B. Acme Corp"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 block">Hauptansprechpartner</label>
|
||||
<input
|
||||
type="text"
|
||||
value={customerForm.name}
|
||||
onChange={e => setCustomerForm({...customerForm, name: e.target.value})}
|
||||
className="w-full px-6 py-4 bg-slate-100 dark:bg-slate-800 border-2 border-transparent focus:border-brand-500 rounded-2xl outline-none font-bold text-slate-900 dark:text-white transition-all shadow-inner"
|
||||
placeholder="Z.B. Max Mustermann"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col h-full">
|
||||
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 block">Firmenprofil & CRM-Notizen</label>
|
||||
<textarea
|
||||
value={customerForm.notes}
|
||||
onChange={e => setCustomerForm({...customerForm, notes: e.target.value})}
|
||||
className="flex-1 w-full px-6 py-6 bg-slate-100 dark:bg-slate-800 border-2 border-transparent focus:border-brand-500 rounded-[2rem] outline-none font-medium text-slate-800 dark:text-slate-200 resize-none transition-all shadow-inner min-h-[200px]"
|
||||
placeholder="Wichtige Infos zur Firma, Rahmenverträge, etc..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Optimized Dynamic Contacts Section */}
|
||||
<div className="pt-10 border-t-2 border-slate-100 dark:border-slate-800">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h4 className="text-xl font-black text-slate-900 dark:text-white tracking-tight">Zusätzliche Ansprechpartner</h4>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addContactRow}
|
||||
className="px-6 py-2 bg-brand-500 text-white font-bold rounded-xl shadow-lg shadow-brand-500/20 hover:bg-brand-600 transition-all flex items-center gap-2 text-sm"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M12 4v16m8-8H4" /></svg>
|
||||
Hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{contactsForm.map((contact, idx) => (
|
||||
<div key={idx} className="grid grid-cols-12 gap-3 p-4 bg-slate-50 dark:bg-slate-800/50 rounded-2xl border-2 border-slate-100 dark:border-slate-700/50 items-center">
|
||||
<div className="col-span-12 md:col-span-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Name"
|
||||
value={contact.name}
|
||||
onChange={(e) => updateContactField(idx, 'name', e.target.value)}
|
||||
className="w-full px-4 py-3 bg-white dark:bg-slate-800 rounded-xl outline-none font-bold text-xs border-2 border-transparent focus:border-brand-500 shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-12 md:col-span-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Rolle"
|
||||
value={contact.role}
|
||||
onChange={(e) => updateContactField(idx, 'role', e.target.value)}
|
||||
className="w-full px-4 py-3 bg-white dark:bg-slate-800 rounded-xl outline-none font-bold text-xs border-2 border-transparent focus:border-brand-500 shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-12 md:col-span-3">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="E-Mail"
|
||||
value={contact.email}
|
||||
onChange={(e) => updateContactField(idx, 'email', e.target.value)}
|
||||
className="w-full px-4 py-3 bg-white dark:bg-slate-800 rounded-xl outline-none font-bold text-xs border-2 border-transparent focus:border-brand-500 shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-10 md:col-span-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Telefon"
|
||||
value={contact.phone}
|
||||
onChange={(e) => updateContactField(idx, 'phone', e.target.value)}
|
||||
className="w-full px-4 py-3 bg-white dark:bg-slate-800 rounded-xl outline-none font-bold text-xs border-2 border-transparent focus:border-brand-500 shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 md:col-span-1 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeContactRow(idx)}
|
||||
className="p-3 text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-xl transition-all"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Area */}
|
||||
<div className="pt-10 flex gap-4 shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(false)}
|
||||
className="flex-1 py-5 bg-slate-100 dark:bg-slate-800 text-slate-500 dark:text-slate-300 font-black rounded-2xl hover:bg-slate-200 transition-all uppercase tracking-widest text-xs"
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSaving}
|
||||
className="flex-[2] py-5 bg-brand-600 text-white font-black rounded-2xl shadow-xl-brand hover:bg-brand-700 transition-all uppercase tracking-widest text-xs flex items-center justify-center gap-3 disabled:opacity-50"
|
||||
>
|
||||
{isSaving ? <div className="w-5 h-5 border-4 border-white/30 border-t-white rounded-full animate-spin"></div> : 'Unternehmen Speichern'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{customerToDeleteId && (
|
||||
<div className="fixed inset-0 z-[120] flex items-center justify-center p-4 bg-slate-950/80 backdrop-blur-md">
|
||||
<div className="bg-white dark:bg-slate-900 w-full max-w-sm rounded-[2.5rem] p-10 text-center shadow-heavy border-2 border-red-500/20">
|
||||
<div className="w-20 h-20 bg-red-100 dark:bg-red-900/30 text-red-600 rounded-3xl flex items-center justify-center mx-auto mb-8 shadow-inner">
|
||||
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
|
||||
</div>
|
||||
<h3 className="text-2xl font-black mb-3 text-slate-900 dark:text-white tracking-tight">Firma löschen?</h3>
|
||||
<p className="text-slate-500 dark:text-slate-400 mb-10 text-sm font-medium leading-relaxed">Alle verknüpften Ansprechpartner und Journal-Einträge werden dauerhaft entfernt.</p>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<button onClick={() => setCustomerToDeleteId(null)} className="py-4 bg-slate-100 dark:bg-slate-800 rounded-2xl font-black text-slate-500 dark:text-slate-300 uppercase tracking-widest text-[10px]">Abbruch</button>
|
||||
<button onClick={confirmDeleteCustomer} className="py-4 bg-red-600 text-white rounded-2xl font-black shadow-lg shadow-red-500/30 hover:bg-red-700 transition-all uppercase tracking-widest text-[10px]">Jetzt Löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user