PrintCal / src /components /CustomerManager.tsx
phucsd's picture
feat: PrintCal - Smart offset and digital print pricing calculator with ERP management
0a5bd33
Raw History Blame Contribute Delete
9.32 kB
'use client';
import React, { useState, useEffect } from 'react';
import { Customer } from '@/types';
import { Users, Plus, Phone, Mail, Building, MapPin, X, RefreshCw } from 'lucide-react';
export const CustomerManager: React.FC = () => {
const [customers, setCustomers] = useState<Customer[]>([]);
const [loading, setLoading] = useState(true);
const [isModalOpen, setIsModalOpen] = useState(false);
const [newCustomer, setNewCustomer] = useState({
name: '',
phone: '',
email: '',
company: '',
address: '',
notes: '',
});
const fetchCustomers = async () => {
try {
setLoading(true);
const res = await fetch('/api/customers');
const data = await res.json();
if (Array.isArray(data)) setCustomers(data);
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchCustomers();
}, []);
const handleSave = async (e: React.FormEvent) => {
e.preventDefault();
try {
const res = await fetch('/api/customers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newCustomer),
});
if (res.ok) {
setIsModalOpen(false);
setNewCustomer({ name: '', phone: '', email: '', company: '', address: '', notes: '' });
fetchCustomers();
}
} catch (err) {
console.error(err);
}
};
return (
<div className="space-y-4">
<div className="flex items-center justify-between bg-white p-4 rounded-xl border border-slate-200 shadow-xs">
<div>
<h2 className="text-base font-bold text-slate-800">Danh Sách Khách Hàng (CRM mini)</h2>
<p className="text-xs text-slate-500">Lưu trữ thông tin liên hệ, công ty và lịch sử đặt in của khách hàng</p>
</div>
<button
onClick={() => setIsModalOpen(true)}
className="flex items-center gap-1.5 px-3.5 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-xs font-semibold shadow transition-colors"
>
<Plus className="w-4 h-4" />
Thêm Khách Hàng
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{loading ? (
<div className="col-span-3 py-12 text-center text-slate-400">
<RefreshCw className="w-6 h-6 animate-spin mx-auto text-blue-500 mb-2" />
Đang tải danh sách khách hàng...
</div>
) : customers.length === 0 ? (
<div className="col-span-3 py-12 text-center text-slate-400 bg-white rounded-xl border border-slate-200">
<Users className="w-10 h-10 mx-auto text-slate-300 mb-2" />
Chưa có khách hàng nào trong hệ thống
</div>
) : (
customers.map((c) => (
<div key={c.id} className="bg-white p-4 rounded-xl border border-slate-200 shadow-xs space-y-2.5">
<div className="flex items-center justify-between">
<strong className="text-sm font-bold text-slate-900">{c.name}</strong>
<span className="text-[10px] text-slate-400 font-mono">#{c.id}</span>
</div>
{c.company && (
<div className="flex items-center gap-1.5 text-xs text-slate-600">
<Building className="w-3.5 h-3.5 text-slate-400 shrink-0" />
<span className="truncate">{c.company}</span>
</div>
)}
<div className="flex items-center gap-1.5 text-xs text-slate-700 font-medium">
<Phone className="w-3.5 h-3.5 text-blue-600 shrink-0" />
<span>{c.phone}</span>
</div>
{c.email && (
<div className="flex items-center gap-1.5 text-xs text-slate-600">
<Mail className="w-3.5 h-3.5 text-slate-400 shrink-0" />
<span className="truncate">{c.email}</span>
</div>
)}
{c.address && (
<div className="flex items-center gap-1.5 text-xs text-slate-500">
<MapPin className="w-3.5 h-3.5 text-slate-400 shrink-0" />
<span className="truncate">{c.address}</span>
</div>
)}
{c.notes && (
<p className="text-[11px] text-slate-500 bg-slate-50 p-2 rounded border border-slate-100 italic">
&ldquo;{c.notes}&rdquo;
</p>
)}
</div>
))
)}
</div>
{isModalOpen && (
<div className="fixed inset-0 z-50 bg-slate-900/50 backdrop-blur-xs flex items-center justify-center p-4">
<div className="bg-white rounded-xl shadow-xl max-w-md w-full overflow-hidden">
<div className="p-4 bg-slate-900 text-white flex items-center justify-between">
<h3 className="font-bold text-sm">Thêm Mới Khách Hàng</h3>
<button onClick={() => setIsModalOpen(false)} className="text-slate-400 hover:text-white">
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSave} className="p-5 space-y-3 text-xs">
<div>
<label className="block text-slate-600 font-medium mb-1">Tên Khách Hàng *</label>
<input
type="text"
required
value={newCustomer.name}
onChange={(e) => setNewCustomer({ ...newCustomer, name: e.target.value })}
placeholder="Ví dụ: Anh Hoàng / Chị Mai"
className="w-full border border-slate-300 rounded-lg p-2 focus:ring-1 focus:ring-blue-500"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-slate-600 font-medium mb-1">Số Điện Thoại *</label>
<input
type="tel"
required
value={newCustomer.phone}
onChange={(e) => setNewCustomer({ ...newCustomer, phone: e.target.value })}
placeholder="0901234567"
className="w-full border border-slate-300 rounded-lg p-2 focus:ring-1 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-slate-600 font-medium mb-1">Email</label>
<input
type="email"
value={newCustomer.email}
onChange={(e) => setNewCustomer({ ...newCustomer, email: e.target.value })}
placeholder="email@example.com"
className="w-full border border-slate-300 rounded-lg p-2 focus:ring-1 focus:ring-blue-500"
/>
</div>
</div>
<div>
<label className="block text-slate-600 font-medium mb-1">Tên Công Ty / Cửa Hàng</label>
<input
type="text"
value={newCustomer.company}
onChange={(e) => setNewCustomer({ ...newCustomer, company: e.target.value })}
placeholder="Công ty TNHH..."
className="w-full border border-slate-300 rounded-lg p-2 focus:ring-1 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-slate-600 font-medium mb-1">Địa Chỉ Giao Hàng</label>
<input
type="text"
value={newCustomer.address}
onChange={(e) => setNewCustomer({ ...newCustomer, address: e.target.value })}
placeholder="Số nhà, tên đường, quận/huyện..."
className="w-full border border-slate-300 rounded-lg p-2 focus:ring-1 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-slate-600 font-medium mb-1">Ghi Chú Về Khách Hàng</label>
<textarea
rows={2}
value={newCustomer.notes}
onChange={(e) => setNewCustomer({ ...newCustomer, notes: e.target.value })}
placeholder="Thói quen đặt hàng, lưu ý kỹ thuật..."
className="w-full border border-slate-300 rounded-lg p-2 focus:ring-1 focus:ring-blue-500"
/>
</div>
<div className="flex justify-end gap-2 pt-3 border-t border-slate-100">
<button
type="button"
onClick={() => setIsModalOpen(false)}
className="px-4 py-2 border border-slate-300 rounded-lg text-slate-600 hover:bg-slate-50"
>
Hủy
</button>
<button
type="submit"
className="px-4 py-2 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700"
>
Lưu Khách Hàng
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
};