S SmartDocs
Chuỗi bài: Golden-Hour-backend-layer-2 typescript 308 dòng · Cập nhật 2026-02-22

CustomerManagement.tsx

Golden-Hour-backend-layer-2/pages/CustomerManagement.tsx


import React, { useState } from 'react';

interface InvoiceRecord {
  id: string;
  date: string;
  amount: string;
  status: 'Paid' | 'Overdue' | 'Draft';
}

interface CustomerRecord {
  id: string;
  name: string;
  email: string;
  phone: string;
  address: string;
  tag: 'VIP' | 'Regular' | 'New';
  orders: number;
  spent: string;
  lastSeen: string;
  notes: string;
  invoices: InvoiceRecord[];
}

const ProtocolBar: React.FC = () => (
  <div className="fixed bottom-12 left-1/2 -translate-x-1/2 bg-white/90 backdrop-blur-md border border-[#c5a059]/20 px-10 py-5 rounded-full gallery-shadow flex items-center space-x-12 z-50 transition-all hover:border-[#c5a059]/40 group">
    <button className="flex items-center space-x-3 text-slate-400 hover:text-slate-900 transition-all">
      <i className="fa-solid fa-magnifying-glass text-[9px] text-[#c5a059]"></i>
      <span className="text-[8px] font-bold uppercase tracking-[0.3em]">Search</span>
    </button>
    <div className="w-[0.5px] h-4 bg-[#c5a059]/20"></div>
    <button className="flex items-center space-x-3 text-slate-400 hover:text-slate-900 transition-all">
      <i className="fa-solid fa-plus text-[9px] text-[#c5a059]"></i>
      <span className="text-[8px] font-bold uppercase tracking-[0.3em]">Create</span>
    </button>
  </div>
);

const CustomerDetailModal: React.FC<{ customer: CustomerRecord | null; onClose: () => void }> = ({ customer, onClose }) => {
  const [note, setNote] = useState(customer?.notes || '');
  if (!customer) return null;

  return (
    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-md fade-in-section">
      <div className="bg-white rounded-[3rem] gallery-shadow w-full max-w-4xl max-h-[90vh] overflow-hidden border border-[#c5a059]/20 flex flex-col">
        <div className="p-10 border-b border-[#c5a059]/10 flex items-center justify-between bg-[#fdfcf9]">
          <div className="flex items-center space-x-8">
            <div className="w-24 h-24 rounded-full border border-[#c5a059]/20 p-1 overflow-hidden gallery-shadow grayscale hover:grayscale-0 transition-all duration-700">
              <img src={`https://picsum.photos/seed/${customer.id}/200/200`} alt={customer.name} className="w-full h-full object-cover rounded-full" />
            </div>
            <div>
              <span className="text-[8px] font-bold uppercase tracking-[0.5em] text-[#c5a059] block mb-2 font-serif italic">Identity Ledger</span>
              <h3 className="font-serif italic text-4xl text-slate-900">{customer.name}</h3>
              <p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest mt-1">{customer.tag} Portfolio Member</p>
            </div>
          </div>
          <button onClick={onClose} className="w-12 h-12 rounded-full border border-[#c5a059]/10 flex items-center justify-center text-slate-400 hover:text-slate-900 transition-all">
            <i className="fa-solid fa-xmark"></i>
          </button>
        </div>

        <div className="flex-1 overflow-y-auto no-scrollbar grid grid-cols-1 lg:grid-cols-3 gap-0">
          {/* Personal Info Sidebar */}
          <div className="p-10 bg-[#fdfcf9]/50 border-r border-[#c5a059]/5 space-y-10">
            <section>
              <h4 className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 mb-4">Contact Protocol</h4>
              <div className="space-y-4">
                <div className="flex flex-col">
                  <span className="text-[7px] font-black uppercase text-[#c5a059]/50 mb-1">Email</span>
                  <span className="text-sm font-medium text-slate-800">{customer.email}</span>
                </div>
                <div className="flex flex-col">
                  <span className="text-[7px] font-black uppercase text-[#c5a059]/50 mb-1">Phone</span>
                  <span className="text-sm font-medium text-slate-800">{customer.phone}</span>
                </div>
                <div className="flex flex-col">
                  <span className="text-[7px] font-black uppercase text-[#c5a059]/50 mb-1">Residence</span>
                  <span className="text-sm font-serif italic text-slate-600 leading-tight">{customer.address}</span>
                </div>
              </div>
            </section>

            <section>
              <h4 className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 mb-4">Internal Client Notes</h4>
              <textarea 
                value={note}
                onChange={(e) => setNote(e.target.value)}
                placeholder="Add special requests, preferences, or observations here..."
                className="w-full h-40 bg-white border border-[#c5a059]/10 rounded-2xl p-4 text-[11px] font-medium leading-relaxed outline-none focus:border-[#c5a059] transition-all resize-none font-serif italic text-slate-600 shadow-inner"
              />
              <button className="mt-3 w-full py-2 bg-[#c5a059]/10 text-[#c5a059] rounded-xl text-[8px] font-bold uppercase tracking-widest hover:bg-[#c5a059] hover:text-white transition-all">
                Update Records
              </button>
            </section>
          </div>

          {/* Invoice Ledger Main */}
          <div className="lg:col-span-2 p-10 space-y-8">
            <section className="mb-10">
              <h4 className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 mb-4">Account Analytics</h4>
              <div className="grid grid-cols-2 gap-6">
                <div className="p-6 rounded-2xl bg-[#fdfcf9] border border-[#c5a059]/5">
                  <span className="text-[7px] font-black uppercase text-slate-300 block mb-1">Total Spent</span>
                  <span className="text-2xl font-serif italic text-slate-900 leading-none tracking-tighter">{customer.spent}</span>
                </div>
                <div className="p-6 rounded-2xl bg-[#fdfcf9] border border-[#c5a059]/5">
                  <span className="text-[7px] font-black uppercase text-slate-300 block mb-1">Acquisitions</span>
                  <span className="text-2xl font-serif italic text-slate-900 leading-none tracking-tighter">{customer.orders}</span>
                </div>
              </div>
            </section>

            <div className="flex items-center justify-between">
              <h4 className="font-serif italic text-3xl text-slate-900">Invoice Registry</h4>
              <span className="text-[8px] font-bold uppercase tracking-[0.2em] text-slate-300">Edition Log</span>
            </div>
            
            <div className="bg-white rounded-[2rem] border border-[#c5a059]/5 overflow-hidden">
              <table className="w-full text-left">
                <thead className="bg-[#fdfcf9] border-b border-[#c5a059]/5 text-[7px] font-bold uppercase tracking-widest text-slate-300 italic font-serif">
                  <tr>
                    <th className="px-6 py-4">Ref. ID</th>
                    <th className="px-6 py-4">Date</th>
                    <th className="px-6 py-4">Value</th>
                    <th className="px-6 py-4">State</th>
                    <th className="px-6 py-4 text-center">Protocol</th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-[#c5a059]/5">
                  {customer.invoices.map((inv) => (
                    <tr key={inv.id} className="hover:bg-[#fdfcf9] transition-all group">
                      <td className="px-6 py-4 serif-numeral text-xs text-[#c5a059]">{inv.id}</td>
                      <td className="px-6 py-4 text-[9px] font-bold uppercase tracking-widest text-slate-400">{inv.date}</td>
                      <td className="px-6 py-4 font-serif italic text-base text-slate-900 tracking-tighter">{inv.amount}</td>
                      <td className="px-6 py-4">
                        <span className={`text-[6px] font-bold uppercase tracking-widest px-2 py-0.5 rounded-full border ${
                          inv.status === 'Paid' ? 'border-emerald-100 text-emerald-600 bg-emerald-50/5' :
                          inv.status === 'Overdue' ? 'border-rose-100 text-rose-600 bg-rose-50/5' : 'border-slate-100 text-slate-400'
                        }`}>
                          {inv.status}
                        </span>
                      </td>
                      <td className="px-6 py-4 text-center">
                        <button className="text-slate-200 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-file-invoice text-xs"></i></button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        </div>

        <div className="p-8 bg-[#fdfcf9] border-t border-[#c5a059]/10 flex justify-between items-center">
          <span className="text-[7px] font-serif italic text-slate-400 uppercase tracking-widest">Profile Last Seen: {customer.lastSeen}</span>
          <div className="flex space-x-4">
             <button className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 hover:text-slate-900 transition-all">Audit Trails</button>
             <button onClick={onClose} className="bg-black text-white px-10 py-3 rounded-full text-[8px] font-bold uppercase tracking-[0.4em] hover:bg-[#c5a059] transition-all">Close Dossier</button>
          </div>
        </div>
      </div>
    </div>
  );
};

const CustomerManagement: React.FC = () => {
  const [selectedCustomer, setSelectedCustomer] = useState<CustomerRecord | null>(null);

  const customers: CustomerRecord[] = [
    { 
      id: 'C-9281', name: 'John Doe', email: 'john@example.com', phone: '+33 6 81 22 93 40', address: '42 Avenue de la Grand Armée, Paris',
      tag: 'VIP', orders: 12, spent: '€14,200', lastSeen: '2 hours ago', notes: 'Prefers high floors and natural lighting. VIP pickup required.',
      invoices: [
        { id: 'INV-800', date: '12/05/2025', amount: '€2,500.00', status: 'Paid' },
        { id: 'INV-752', date: '01/04/2025', amount: '€1,800.00', status: 'Paid' }
      ]
    },
    { 
      id: 'C-9282', name: 'Sarah Wilson', email: 'sarah.w@email.com', phone: '+44 7700 900555', address: '12 Kensington High St, London',
      tag: 'Regular', orders: 4, spent: '€4,100', lastSeen: '1 day ago', notes: 'Allergic to shellfish. Frequent weekend traveler.',
      invoices: [
        { id: 'INV-812', date: '14/05/2025', amount: '€850.00', status: 'Overdue' }
      ]
    },
    { 
      id: 'C-9283', name: 'Mike Johnson', email: 'mj88@gmail.com', phone: '+1 202 555 0998', address: '988 Sunset Blvd, Los Angeles',
      tag: 'New', orders: 1, spent: '€250', lastSeen: '5 mins ago', notes: 'Interested in wine experiences.',
      invoices: [
        { id: 'INV-815', date: '15/05/2025', amount: '€250.00', status: 'Draft' }
      ]
    },
    { 
      id: 'C-9284', name: 'Emma Watson', email: 'emma@star.com', phone: '+44 7911 123456', address: 'The Shard, London SE1',
      tag: 'VIP', orders: 28, spent: '€42,400', lastSeen: '3 days ago', notes: 'Requires private chauffeur for all bookings.',
      invoices: [
        { id: 'INV-790', date: '10/05/2025', amount: '€12,000.00', status: 'Paid' },
        { id: 'INV-700', date: '01/03/2025', amount: '€8,500.00', status: 'Paid' }
      ]
    },
  ];

  return (
    <div className="relative pb-32">
      <div className="space-y-12 fade-in-section">
        <div className="flex flex-col md:flex-row md:items-end justify-between border-b border-[#c5a059]/10 pb-10 gap-6">
          <div>
            <h2 className="text-4xl font-serif italic text-slate-900 tracking-tight">Customer Insights</h2>
            <p className="text-[10px] font-bold uppercase tracking-[0.4em] text-[#c5a059] mt-3 font-serif italic">Relational Repository & Behavioral Data</p>
          </div>
          <div className="flex items-center">
            <div className="flex -space-x-3 items-center mr-8">
              {[1,2,3,4,5].map(i => (
                <div key={i} className="w-10 h-10 rounded-full border-2 border-white ring-1 ring-[#c5a059]/5 overflow-hidden grayscale opacity-40 hover:opacity-100 hover:grayscale-0 transition-all cursor-pointer">
                  <img src={`https://picsum.photos/seed/${i*20}/40/40`} className="w-full h-full object-cover" />
                </div>
              ))}
              <div className="w-10 h-10 rounded-full bg-[#fdfcf9] border border-[#c5a059]/10 flex items-center justify-center text-[8px] font-black text-[#c5a059] shadow-sm ml-2">+2.4k</div>
            </div>
            <button className="bg-black text-white px-8 py-3 rounded-full text-[8px] font-black uppercase tracking-[0.3em] hover:bg-[#c5a059] transition-all">Export Index</button>
          </div>
        </div>

        <div className="bg-white rounded-[3rem] border border-[#c5a059]/10 shadow-sm overflow-hidden p-2">
          <div className="p-10 border-b border-[#c5a059]/5 flex items-center justify-between bg-[#fdfcf9]/30">
            <h3 className="text-slate-900 font-serif italic text-2xl">Active Customer Registry</h3>
            <div className="flex items-center space-x-6">
              <div className="flex items-center space-x-2">
                <span className="text-[8px] text-slate-300 font-bold uppercase tracking-widest font-serif italic">Sort:</span>
                <select className="text-[9px] font-bold text-slate-700 outline-none cursor-pointer uppercase tracking-widest bg-transparent font-serif italic">
                  <option>Activity Protocol</option>
                  <option>Capital Contribution</option>
                </select>
              </div>
              <div className="w-[0.5px] h-4 bg-[#c5a059]/20"></div>
              <button className="text-slate-300 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-filter text-[10px]"></i></button>
            </div>
          </div>
          
          <div className="overflow-x-auto">
            <table className="w-full text-left">
              <thead>
                <tr className="text-[8px] text-slate-300 uppercase tracking-[0.3em] border-b border-[#c5a059]/5 font-serif italic">
                  <th className="px-10 py-6">Customer Profile</th>
                  <th className="px-10 py-6">Tier Segment</th>
                  <th className="px-10 py-6">Order Vol.</th>
                  <th className="px-10 py-6">Cumulative Value</th>
                  <th className="px-10 py-6">Last Engagement</th>
                  <th className="px-10 py-6 text-center">Action Protocol</th>
                </tr>
              </thead>
              <tbody className="divide-y divide-[#c5a059]/5">
                {customers.map((c, idx) => (
                  <tr key={idx} className="hover:bg-[#fdfcf9] group transition-all duration-700">
                    <td className="px-10 py-8">
                      <div className="flex items-center group/profile">
                        <div 
                          onClick={() => setSelectedCustomer(c)}
                          className="w-12 h-12 rounded-full border border-[#c5a059]/10 p-0.5 mr-5 grayscale group-hover:grayscale-0 transition-all duration-[1.5s] cursor-pointer hover:rotate-[10deg] hover:scale-110 shadow-sm"
                        >
                          <img src={`https://picsum.photos/seed/${c.id}/48/48`} className="w-full h-full object-cover rounded-full" />
                        </div>
                        <div onClick={() => setSelectedCustomer(c)} className="cursor-pointer">
                          <p className="text-lg font-serif italic text-slate-900 leading-none group-hover:text-[#c5a059] transition-colors">{c.name}</p>
                          <p className="text-[9px] text-slate-400 font-bold uppercase tracking-widest mt-1.5">{c.email}</p>
                        </div>
                      </div>
                    </td>
                    <td className="px-10 py-8">
                      <span className={`text-[8px] font-black uppercase tracking-[0.15em] px-3 py-1 rounded-full border ${
                        c.tag === 'VIP' ? 'bg-[#c5a059]/10 border-[#c5a059]/20 text-[#c5a059]' :
                        c.tag === 'New' ? 'bg-emerald-50 border-emerald-100 text-emerald-700' : 'bg-slate-50 border-slate-100 text-slate-400'
                      }`}>
                        {c.tag}
                      </span>
                    </td>
                    <td className="px-10 py-8 text-lg font-serif italic text-slate-400 serif-numeral">{c.orders} units</td>
                    <td className="px-10 py-8 text-xl font-serif italic text-slate-900 tracking-tighter">{c.spent}</td>
                    <td className="px-10 py-8 text-[9px] font-bold uppercase tracking-widest text-slate-300">{c.lastSeen}</td>
                    <td className="px-10 py-8">
                      <div className="flex justify-center space-x-6 opacity-0 group-hover:opacity-100 transition-all duration-700 translate-x-4 group-hover:translate-x-0">
                        <button onClick={() => setSelectedCustomer(c)} className="text-slate-300 hover:text-[#c5a059] transition-all" title="View Profile">
                          <i className="fa-solid fa-address-card text-sm"></i>
                        </button>
                        <button className="text-slate-300 hover:text-[#c5a059] transition-all" title="Modify Details">
                          <i className="fa-solid fa-pen-nib text-sm"></i>
                        </button>
                        <button className="text-slate-300 hover:text-[#c5a059] transition-all" title="Email Protocol">
                          <i className="fa-solid fa-envelope text-sm"></i>
                        </button>
                        <button className="text-slate-300 hover:text-rose-600 transition-all" title="Archive Identity">
                          <i className="fa-solid fa-trash-can text-sm"></i>
                        </button>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
      <CustomerDetailModal customer={selectedCustomer} onClose={() => setSelectedCustomer(null)} />
      <ProtocolBar />
    </div>
  );
};

export default CustomerManagement;

Bài viết liên quan