S SmartDocs
시리즈: Golden-Hour-backend-layer-2 typescript 328 줄 · 업데이트 2026-02-22

OrderManagement.tsx

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


import React, { useState } from 'react';

interface OrderManagementProps {
  view?: 'maisons' | 'experiences' | 'services' | 'products' | 'returns';
}

interface LogEntry {
  time: string;
  event: string;
  officer: string;
}

interface OrderDetail {
  id: string;
  customer: string;
  phone: string;
  email: string;
  orderName: string;
  status: string;
  statusTime: string;
  payment: string;
  details: Record<string, string>;
  logs: LogEntry[];
}

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 BookingLogModal: React.FC<{ order: OrderDetail | null; onClose: () => void }> = ({ order, onClose }) => {
  if (!order) return null;

  return (
    <div className="fixed inset-0 z-[110] flex items-center justify-center p-4 bg-slate-900/40 backdrop-blur-sm fade-in-section">
      <div className="bg-white rounded-[2rem] gallery-shadow w-full max-w-md overflow-hidden border border-[#c5a059]/20 flex flex-col">
        <div className="p-8 border-b border-[#c5a059]/10 flex items-center justify-between bg-[#fdfcf9]">
          <div>
            <span className="text-[7px] font-black uppercase tracking-[0.4em] text-[#c5a059] block mb-1 font-serif italic">Protocol Registry</span>
            <h3 className="font-serif italic text-2xl text-slate-900">Timeline: {order.id}</h3>
          </div>
          <button onClick={onClose} className="w-8 h-8 rounded-full border border-[#c5a059]/10 flex items-center justify-center text-slate-300 hover:text-slate-900 transition-all">
            <i className="fa-solid fa-xmark text-xs"></i>
          </button>
        </div>
        <div className="p-8 space-y-6 max-h-[60vh] overflow-y-auto no-scrollbar">
          {order.logs.map((log, i) => (
            <div key={i} className="flex group/log">
              <div className="w-16 flex-shrink-0 pt-1 text-right pr-6">
                <p className="serif-numeral text-[10px] text-slate-300 group-hover/log:text-[#c5a059] transition-colors">{log.time}</p>
              </div>
              <div className="flex-1 pb-6 border-l border-[#c5a059]/10 pl-6 relative">
                <div className="absolute -left-[3.5px] top-2 w-1.5 h-1.5 rounded-full bg-[#c5a059]/30 group-hover/log:bg-[#c5a059] transition-all"></div>
                <p className="text-[9px] font-black uppercase tracking-widest text-slate-800 leading-none mb-1.5">{log.event}</p>
                <p className="text-[8px] font-serif italic text-slate-400">Authorized by Officer {log.officer}</p>
              </div>
            </div>
          ))}
        </div>
        <div className="p-6 bg-[#fdfcf9] border-t border-[#c5a059]/10 flex justify-end">
          <button onClick={onClose} className="text-[8px] font-bold uppercase tracking-[0.2em] text-slate-400 hover:text-slate-900 transition-all">Close Registry</button>
        </div>
      </div>
    </div>
  );
};

const OrderDetailModal: React.FC<{ order: OrderDetail | null; onClose: () => void }> = ({ order, onClose }) => {
  if (!order) 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-2xl 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>
            <span className="text-[8px] font-bold uppercase tracking-[0.5em] text-[#c5a059] block mb-2 font-serif italic">Transaction Dossier</span>
            <h3 className="font-serif italic text-3xl text-slate-900">Order {order.id}</h3>
          </div>
          <button onClick={onClose} className="w-10 h-10 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="p-10 grid grid-cols-2 gap-10">
          <div className="space-y-6">
            <section>
              <h4 className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 mb-3">Client Information</h4>
              <p className="font-serif italic text-2xl text-slate-900 leading-tight">{order.customer}</p>
              <div className="mt-3 space-y-1">
                <p className="text-[10px] text-slate-500 font-medium uppercase tracking-widest flex items-center">
                  <i className="fa-solid fa-phone text-[8px] mr-3 text-[#c5a059]/40"></i> {order.phone}
                </p>
                <p className="text-[10px] text-slate-500 font-medium uppercase tracking-widest flex items-center">
                  <i className="fa-solid fa-envelope text-[8px] mr-3 text-[#c5a059]/40"></i> {order.email}
                </p>
              </div>
            </section>
            <section>
              <h4 className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 mb-3">Provision Details</h4>
              <p className="font-serif italic text-xl text-[#c5a059]">{order.orderName}</p>
              <div className="mt-4 space-y-3">
                {Object.entries(order.details).map(([key, val]) => (
                  <div key={key} className="flex justify-between border-b border-[#c5a059]/5 pb-1">
                    <span className="text-[8px] font-bold uppercase tracking-widest text-slate-400">{key}</span>
                    <span className="text-[10px] font-medium text-slate-800">{val}</span>
                  </div>
                ))}
              </div>
            </section>
          </div>
          <div className="bg-[#fdfcf9] rounded-[2rem] p-8 border border-[#c5a059]/5 flex flex-col justify-between">
            <section>
              <h4 className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 mb-6">Execution Status</h4>
              <div className="flex items-center space-x-4 mb-4">
                 <div className="w-2 h-2 rounded-full bg-emerald-400 shadow-[0_0_10px_rgba(52,211,153,0.4)]"></div>
                 <div>
                    <p className="text-[10px] font-black uppercase tracking-widest text-slate-900">{order.status}</p>
                    <p className="text-[8px] font-serif italic text-slate-400 mt-1">Logged: {order.statusTime}</p>
                 </div>
              </div>
            </section>
            <section className="pt-8 border-t border-[#c5a059]/10 text-right">
              <h4 className="text-[8px] font-bold uppercase tracking-[0.3em] text-slate-300 mb-2">Total Acquisition</h4>
              <p className="font-serif italic text-5xl text-slate-900 tracking-tighter">{order.payment}</p>
            </section>
          </div>
        </div>
        <div className="p-8 bg-[#fdfcf9]/80 border-t border-[#c5a059]/10 flex justify-between">
           <button className="text-[8px] font-bold uppercase tracking-[0.2em] text-[#c5a059] hover:text-slate-900 transition-all">Download Receipt PDF</button>
           <button onClick={onClose} className="bg-black text-white px-8 py-3 rounded-full text-[8px] font-bold uppercase tracking-[0.3em] hover:bg-[#c5a059] transition-all">Close Entry</button>
        </div>
      </div>
    </div>
  );
};

const ActionButtons: React.FC = () => (
  <div className="flex justify-center items-center space-x-6">
    <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="Send Email Protocol">
      <i className="fa-solid fa-envelope text-sm"></i>
    </button>
    <button className="text-slate-300 hover:text-rose-600 transition-all" title="Delete Transaction">
      <i className="fa-solid fa-trash-can text-sm"></i>
    </button>
  </div>
);

const OrderManagement: React.FC<OrderManagementProps> = ({ view = 'maisons' }) => {
  const [selectedOrder, setSelectedOrder] = useState<OrderDetail | null>(null);
  const [selectedLogOrder, setSelectedLogOrder] = useState<OrderDetail | null>(null);

  const mockData: Record<string, OrderDetail[]> = {
    maisons: [
      { id: '#M-2025-01', customer: 'Alice Wong', phone: '+33 6 12 34 56 78', email: 'alice@example.com', orderName: 'Villa Golden Hour', status: 'Confirmed', statusTime: 'Today, 10:24 AM', payment: '€2,500.00', details: { 'Check In': '12/05/2025', 'Check Out': '15/05/2025', 'Guests': '4 Adults' }, logs: [{time: '08:00', event: 'Draft Initialized', officer: 'AD-01'}, {time: '10:24', event: 'Payment Secured', officer: 'FIN-2'}, {time: '10:30', event: 'Confirmed Protocol', officer: 'GH-MGR'}] },
      { id: '#M-2025-02', customer: 'David Miller', phone: '+44 7700 900123', email: 'd.miller@uk.co', orderName: 'Azure Suite 4B', status: 'Draft', statusTime: 'Yesterday, 04:15 PM', payment: '€1,800.00', details: { 'Check In': '18/06/2025', 'Check Out': '20/06/2025', 'Guests': '2 Adults' }, logs: [{time: '16:15', event: 'Draft Initialized', officer: 'CL-04'}] },
    ],
    experiences: [
      { id: '#E-2025-01', customer: 'Bob Chen', phone: '+44 7700 900000', email: 'bob.c@web.com', orderName: 'Wine Masterclass', status: 'Pending', statusTime: 'Yesterday, 4:15 PM', payment: '€45.00', details: { 'Time Slot': '14:00 - 16:30', 'Location': 'Tuscan Vineyard', 'Participants': '2' }, logs: [{time: '15:00', event: 'Inquiry Logged', officer: 'EX-01'}] },
    ],
    services: [
      { id: '#S-2025-01', customer: 'Sarah Miller', phone: '+1 202 555 0123', email: 'sm@agency.com', orderName: 'Luxury Shuttle', status: 'Dispatched', statusTime: '2h ago', payment: '€120.00', details: { 'Pickup Time': '09:00 AM', 'Route': 'Station to Villa', 'Type': 'Mercedes V-Class' }, logs: [{time: '07:00', event: 'Driver Assigned', officer: 'OPS-01'}, {time: '09:00', event: 'Dispatched', officer: 'OPS-01'}] },
    ],
    products: [
      { id: '#P-2025-01', customer: 'Mark Ross', phone: '+33 1 45 67 89 10', email: 'mark@ross.co', orderName: 'Huile d\'Olive Set', status: 'Shipped', statusTime: 'Yesterday, 11:30 AM', payment: '€298.47', details: { 'Amount': 'x 3 Units', 'Address': '123 Rue de Rivoli, Paris', 'Courier': 'DHL Express' }, logs: [{time: '10:00', event: 'Order Picked', officer: 'WH-01'}, {time: '11:30', event: 'Shipped Protocol', officer: 'WH-01'}] },
    ],
    returns: [
      { id: '#R-2025-01', customer: 'David Smith', phone: '+44 20 7946 0000', email: 'd.smith@mail.com', orderName: 'Gourmet Box', status: 'Processing', statusTime: 'Today, 09:45 AM', payment: '€85.00', details: { 'Reason': 'Damaged in transit', 'Return Method': 'Pick-up', 'Request Date': '10/05/2025' }, logs: [{time: '09:45', event: 'Claim Logged', officer: 'CS-01'}] },
    ]
  };

  const currentData = mockData[view] || [];

  return (
    <div className="relative pb-32">
      <div className="space-y-12 fade-in-section">
        <div className="flex justify-between items-end border-b border-[#c5a059]/10 pb-10">
          <div>
            <h2 className="text-[#c5a059] font-black text-[10px] uppercase tracking-[0.5em] mb-4">Transaction Protocol</h2>
            <h3 className="text-4xl font-serif italic text-slate-900">
              {view === 'returns' ? 'Returns & Refunds' : `${view.charAt(0).toUpperCase() + view.slice(1)} Orders`}
            </h3>
          </div>
          <div className="flex space-x-4">
            <button className="bg-white border border-[#c5a059]/10 text-slate-400 px-8 py-3 rounded-full text-[8px] font-bold uppercase tracking-[0.3em] hover:text-black transition-all">
              <i className="fa-solid fa-file-export mr-3"></i> Export Ledger
            </button>
          </div>
        </div>

        <div className="bg-white rounded-xl border border-[#c5a059]/5 overflow-hidden gallery-shadow">
          <table className="w-full text-left">
            <thead className="text-stone-300 text-[8px] font-bold uppercase tracking-[0.2em] border-b border-[#c5a059]/5 font-serif italic">
              <tr>
                <th className="px-8 py-5">ID</th>
                <th className="px-8 py-5">Customer</th>
                <th className="px-8 py-5">Contact</th>
                <th className="px-8 py-5">Order</th>
                
                {view === 'maisons' && (
                  <>
                    <th className="px-8 py-5">Check in</th>
                    <th className="px-8 py-5">Check out</th>
                  </>
                )}
                {view === 'experiences' && (
                  <>
                    <th className="px-8 py-5">Time Slot</th>
                  </>
                )}
                {view === 'services' && (
                  <>
                    <th className="px-8 py-5">Time</th>
                  </>
                )}
                {view === 'products' && (
                  <>
                    <th className="px-8 py-5">Amount</th>
                    <th className="px-8 py-5">Address</th>
                  </>
                )}

                {view !== 'returns' && <th className="px-8 py-5">Payment</th>}
                
                <th className="px-8 py-5">Booking Status</th>
                <th className="px-8 py-5 text-center">Action</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-[#c5a059]/5">
              {currentData.map((order) => (
                <tr key={order.id} className="hover:bg-[#fdfcf9] transition-all group">
                  <td className="px-8 py-7 serif-numeral text-sm text-[#c5a059]">{order.id}</td>
                  <td className="px-8 py-7">
                    <p className="font-serif italic text-lg text-slate-900 leading-none">{order.customer}</p>
                  </td>
                  <td className="px-8 py-7">
                    <div className="space-y-1">
                      <p className="text-[9px] text-slate-500 uppercase tracking-wider flex items-center">
                        <i className="fa-solid fa-phone text-[7px] mr-2 opacity-30"></i> {order.phone}
                      </p>
                      <p className="text-[9px] text-slate-400 uppercase tracking-wider flex items-center">
                        <i className="fa-solid fa-envelope text-[7px] mr-2 opacity-30"></i> {order.email}
                      </p>
                    </div>
                  </td>
                  <td className="px-8 py-7">
                    <button 
                      onClick={() => setSelectedOrder(order)}
                      className="text-[10px] font-bold uppercase tracking-[0.15em] text-[#c5a059] hover:text-slate-900 transition-all text-left decoration-[#c5a059]/20 hover:underline underline-offset-4 font-serif italic"
                    >
                      {order.orderName}
                    </button>
                  </td>

                  {view === 'maisons' && (
                    <>
                      <td className="px-8 py-7 serif-numeral text-sm text-stone-500">{order.details['Check In']}</td>
                      <td className="px-8 py-7 serif-numeral text-sm text-stone-500">{order.details['Check Out']}</td>
                    </>
                  )}

                  {view === 'experiences' && (
                    <td className="px-8 py-7 serif-numeral text-sm text-stone-500">{order.details['Time Slot']}</td>
                  )}

                  {view === 'services' && (
                    <td className="px-8 py-7 serif-numeral text-sm text-stone-500">{order.details['Pickup Time']}</td>
                  )}

                  {view === 'products' && (
                    <>
                      <td className="px-8 py-7 serif-numeral text-sm text-stone-500">{order.details['Amount']}</td>
                      <td className="px-8 py-7 text-[10px] text-slate-400 uppercase tracking-widest font-medium truncate max-w-[150px]">{order.details['Address']}</td>
                    </>
                  )}

                  {view !== 'returns' && (
                    <td className="px-8 py-7 font-serif italic text-lg text-slate-900 tracking-tighter">{order.payment}</td>
                  )}

                  <td className="px-8 py-7">
                    <div className="flex items-center space-x-3">
                      <div>
                        <span className={`text-[7px] font-bold uppercase tracking-widest px-2 py-0.5 rounded-full border ${
                          order.status === 'Confirmed' ? 'border-emerald-100 text-emerald-600 bg-emerald-50/5' :
                          order.status === 'Dispatched' ? 'border-sky-100 text-sky-600 bg-sky-50/5' :
                          'border-slate-100 text-slate-400'
                        }`}>{order.status}</span>
                        <p className="text-[6px] uppercase tracking-widest text-slate-300 mt-1 font-medium">{order.statusTime}</p>
                      </div>
                      <button 
                        onClick={() => setSelectedLogOrder(order)}
                        className="w-5 h-5 rounded-full border border-[#c5a059]/10 flex items-center justify-center text-slate-300 hover:text-[#c5a059] transition-all"
                        title="View Protocol Log"
                      >
                        <i className="fa-solid fa-clock-rotate-left text-[8px]"></i>
                      </button>
                    </div>
                  </td>

                  <td className="px-8 py-7">
                    <ActionButtons />
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
      <OrderDetailModal order={selectedOrder} onClose={() => setSelectedOrder(null)} />
      <BookingLogModal order={selectedLogOrder} onClose={() => setSelectedLogOrder(null)} />
      <ProtocolBar />
    </div>
  );
};

export default OrderManagement;

관련 글