|
| 1 | +<script> |
| 2 | + /** @type {import('./$types').PageData} - for external reference */ |
| 3 | + export let data; |
| 4 | +
|
| 5 | + /** |
| 6 | + * @param {string} status |
| 7 | + */ |
| 8 | + function getStatusClass(status) { |
| 9 | + /** @type {{ [key: string]: string }} */ |
| 10 | + const classes = { |
| 11 | + 'ACCEPTED': 'bg-green-100 text-green-700', |
| 12 | + 'PRESENTED': 'bg-blue-100 text-blue-700', |
| 13 | + 'DRAFT': 'bg-gray-100 text-gray-700', |
| 14 | + 'APPROVED': 'bg-purple-100 text-purple-700', |
| 15 | + 'REJECTED': 'bg-red-100 text-red-700' |
| 16 | + }; |
| 17 | + return classes[status] || 'bg-gray-100 text-gray-700'; |
| 18 | + } |
| 19 | +
|
| 20 | + /** |
| 21 | + * @param {number} amount |
| 22 | + */ |
| 23 | + function formatCurrency(amount) { |
| 24 | + return new Intl.NumberFormat('en-US', { |
| 25 | + style: 'currency', |
| 26 | + currency: 'USD' |
| 27 | + }).format(amount); |
| 28 | + } |
| 29 | +</script> |
| 30 | + |
1 | 31 | <!-- Super Rich Invoice List Page - Uniform Blue-Purple Theme --> |
2 | 32 | <div class="min-h-screen bg-gradient-to-br from-blue-100 to-purple-100 p-8"> |
3 | 33 | <div class="max-w-5xl mx-auto"> |
4 | 34 | <div class="flex justify-between items-center mb-10"> |
5 | 35 | <h1 class="text-4xl font-extrabold text-blue-900 tracking-tight">Invoices</h1> |
6 | | - <a href="." class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-700 to-purple-700 text-white text-lg font-semibold rounded-xl shadow-lg hover:from-blue-800 hover:to-purple-800 transition">+ New Invoice</a> |
| 36 | + <a href="/app/invoices/new" class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-700 to-purple-700 text-white text-lg font-semibold rounded-xl shadow-lg hover:from-blue-800 hover:to-purple-800 transition">+ New Invoice</a> |
7 | 37 | </div> |
| 38 | + |
| 39 | + <!-- Search and Filter Controls --> |
8 | 40 | <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-8"> |
9 | 41 | <!-- Search --> |
10 | 42 | <div class="flex-1 flex items-center bg-white/80 backdrop-blur-md rounded-xl shadow px-4 py-2 border border-blue-200"> |
11 | | - <svg class="w-5 h-5 text-blue-400 mr-2" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> |
| 43 | + <svg class="w-5 h-5 text-blue-400 mr-2" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> |
| 44 | + <circle cx="11" cy="11" r="8"/> |
| 45 | + <line x1="21" y1="21" x2="16.65" y2="16.65"/> |
| 46 | + </svg> |
12 | 47 | <label for="invoice-search" class="sr-only">Search invoices</label> |
13 | | - <input id="invoice-search" type="text" placeholder="Search invoices..." class="bg-transparent outline-none flex-1 text-blue-900 placeholder-blue-400" /> |
| 48 | + <input |
| 49 | + id="invoice-search" |
| 50 | + type="text" |
| 51 | + placeholder="Search invoices..." |
| 52 | + class="bg-transparent outline-none flex-1 text-blue-900 placeholder-blue-400" /> |
14 | 53 | </div> |
| 54 | + |
15 | 55 | <!-- Status Filter --> |
16 | 56 | <div class="flex items-center bg-white/80 backdrop-blur-md rounded-xl shadow px-4 py-2 border border-blue-200"> |
17 | | - <svg class="w-5 h-5 text-purple-400 mr-2" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="7" width="18" height="13" rx="2"/><path d="M16 3v4M8 3v4"/></svg> |
| 57 | + <svg class="w-5 h-5 text-purple-400 mr-2" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> |
| 58 | + <rect x="3" y="7" width="18" height="13" rx="2"/> |
| 59 | + <path d="M16 3v4M8 3v4"/> |
| 60 | + </svg> |
18 | 61 | <label for="invoice-status-filter" class="sr-only">Filter by status</label> |
19 | | - <select id="invoice-status-filter" class="bg-transparent outline-none text-blue-900 font-semibold"> |
| 62 | + <select |
| 63 | + id="invoice-status-filter" |
| 64 | + class="bg-transparent outline-none text-blue-900 font-semibold"> |
20 | 65 | <option>All Statuses</option> |
21 | 66 | <option>Paid</option> |
22 | 67 | <option>Unpaid</option> |
23 | 68 | <option>Overdue</option> |
24 | 69 | </select> |
25 | 70 | </div> |
| 71 | + |
26 | 72 | <!-- Date Range --> |
27 | 73 | <div class="flex items-center bg-white/80 backdrop-blur-md rounded-xl shadow px-4 py-2 border border-blue-200"> |
28 | | - <svg class="w-5 h-5 text-blue-400 mr-2" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg> |
| 74 | + <svg class="w-5 h-5 text-blue-400 mr-2" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> |
| 75 | + <rect x="3" y="4" width="18" height="18" rx="2"/> |
| 76 | + <path d="M16 2v4M8 2v4M3 10h18"/> |
| 77 | + </svg> |
29 | 78 | <label for="invoice-date-range" class="sr-only">Date range filter</label> |
30 | | - <input id="invoice-date-range" type="text" placeholder="Date range" class="bg-transparent outline-none text-blue-900 placeholder-blue-400 w-28" /> |
| 79 | + <input |
| 80 | + id="invoice-date-range" |
| 81 | + type="text" |
| 82 | + placeholder="Date range" |
| 83 | + class="bg-transparent outline-none text-blue-900 placeholder-blue-400 w-28" /> |
31 | 84 | </div> |
32 | 85 | </div> |
| 86 | + |
| 87 | + <!-- Invoice Cards --> |
33 | 88 | <div class="flex flex-col gap-5"> |
34 | | - <!-- Invoice Card 1 --> |
35 | | - <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl p-5 border-t-8 border-blue-600 relative overflow-hidden flex flex-col md:flex-row md:items-center md:justify-between gap-4"> |
36 | | - <div class="flex-1 flex flex-col md:flex-row md:items-center gap-4"> |
37 | | - <div class="flex flex-col gap-1 min-w-[120px]"> |
38 | | - <span class="text-xs font-bold uppercase tracking-widest text-blue-700 bg-blue-100 px-2 py-0.5 rounded-full w-fit">Unpaid</span> |
39 | | - <span class="text-blue-500 text-xs">Due: 15 Apr 2025</span> |
40 | | - </div> |
41 | | - <div class="flex-1"> |
42 | | - <h2 class="text-xl font-bold text-blue-900 mb-0.5">INV-001</h2> |
43 | | - <p class="text-blue-500 mb-1 text-sm">Acme Corp</p> |
44 | | - <div class="mb-1"> |
45 | | - <div class="flex justify-between text-blue-700 text-xs mb-0.5"> |
46 | | - <span>Service</span><span>$1,000</span> |
47 | | - </div> |
48 | | - <div class="flex justify-between text-blue-700 text-xs mb-0.5"> |
49 | | - <span>Hosting</span><span>$200</span> |
| 89 | + {#each data.invoices as invoice} |
| 90 | + <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl p-5 border-t-8 border-blue-600 relative overflow-hidden flex flex-col md:flex-row md:items-center md:justify-between gap-4"> |
| 91 | + <div class="flex-1 flex flex-col md:flex-row md:items-center gap-4"> |
| 92 | + <div class="flex flex-col gap-1 min-w-[120px]"> |
| 93 | + <span class="text-xs font-bold uppercase tracking-widest px-2 py-0.5 rounded-full w-fit {getStatusClass(invoice.status)}">{invoice.status.toLowerCase()}</span> |
| 94 | + <span class="text-blue-500 text-xs">Due: {invoice.expirationDate ? new Date(invoice.expirationDate).toLocaleDateString() : 'N/A'}</span> |
| 95 | + </div> |
| 96 | + <div class="flex-1"> |
| 97 | + <h2 class="text-xl font-bold text-blue-900 mb-0.5">{invoice.quoteNumber}</h2> |
| 98 | + <p class="text-blue-500 mb-1 text-sm">{invoice.account.name}</p> |
| 99 | + <div class="mb-1"> |
| 100 | + {#each invoice.lineItems as item} |
| 101 | + <div class="flex justify-between text-blue-700 text-xs mb-0.5"> |
| 102 | + <span>{item.description || item.product?.name}</span> |
| 103 | + <span>{formatCurrency(Number(item.totalPrice))}</span> |
| 104 | + </div> |
| 105 | + {/each} |
50 | 106 | </div> |
51 | 107 | </div> |
52 | 108 | </div> |
53 | | - </div> |
54 | | - <div class="flex flex-col items-end gap-2 min-w-[120px]"> |
55 | | - <div class="flex items-center gap-1"> |
56 | | - <span class="font-bold text-blue-800 text-sm">Total</span> |
57 | | - <span class="text-base font-extrabold text-purple-700">$1,200</span> |
58 | | - </div> |
59 | | - <div class="flex gap-2"> |
60 | | - <button class="px-4 py-1.5 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-full shadow hover:from-blue-700 hover:to-purple-700 transition font-semibold text-sm">Edit</button> |
61 | | - <button class="px-4 py-1.5 bg-blue-100 text-blue-700 rounded-full font-semibold hover:bg-blue-200 transition text-sm">Download</button> |
62 | | - </div> |
63 | | - </div> |
64 | | - <div class="absolute right-4 top-1 opacity-10 text-[5rem] font-black text-blue-200 select-none pointer-events-none">💸</div> |
65 | | - </div> |
66 | | - <!-- Invoice Card 2 --> |
67 | | - <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl p-5 border-t-8 border-green-500 relative overflow-hidden flex flex-col md:flex-row md:items-center md:justify-between gap-4"> |
68 | | - <div class="flex-1 flex flex-col md:flex-row md:items-center gap-4"> |
69 | | - <div class="flex flex-col gap-1 min-w-[120px]"> |
70 | | - <span class="text-xs font-bold uppercase tracking-widest text-green-700 bg-green-100 px-2 py-0.5 rounded-full w-fit">Paid</span> |
71 | | - <span class="text-green-500 text-xs">Due: 10 Mar 2025</span> |
72 | | - </div> |
73 | | - <div class="flex-1"> |
74 | | - <h2 class="text-xl font-bold text-green-900 mb-0.5">INV-002</h2> |
75 | | - <p class="text-green-500 mb-1 text-sm">Beta LLC</p> |
76 | | - <div class="mb-1"> |
77 | | - <div class="flex justify-between text-green-700 text-xs mb-0.5"> |
78 | | - <span>Design</span><span>$800</span> |
79 | | - </div> |
| 109 | + <div class="text-right flex-shrink-0"> |
| 110 | + <div class="text-2xl font-extrabold text-purple-700 mb-1">{formatCurrency(Number(invoice.grandTotal))}</div> |
| 111 | + <div class="flex gap-2"> |
| 112 | + <a href="/app/invoices/{invoice.id}" class="px-3 py-1 bg-blue-600 text-white rounded-full text-xs font-semibold hover:bg-blue-700 transition">View</a> |
| 113 | + <a href="/app/invoices/{invoice.id}/edit" class="px-3 py-1 bg-purple-600 text-white rounded-full text-xs font-semibold hover:bg-purple-700 transition">Edit</a> |
80 | 114 | </div> |
81 | 115 | </div> |
| 116 | + <!-- Decorative gradient --> |
| 117 | + <div class="absolute top-0 right-0 w-32 h-32 bg-gradient-to-bl from-purple-200/30 to-transparent rounded-full -translate-y-16 translate-x-16"></div> |
82 | 118 | </div> |
83 | | - <div class="flex flex-col items-end gap-2 min-w-[120px]"> |
84 | | - <div class="flex items-center gap-1"> |
85 | | - <span class="font-bold text-green-800 text-sm">Total</span> |
86 | | - <span class="text-base font-extrabold text-green-700">$800</span> |
87 | | - </div> |
88 | | - <div class="flex gap-2"> |
89 | | - <button class="px-4 py-1.5 bg-gradient-to-r from-green-600 to-green-400 text-white rounded-full shadow hover:from-green-700 hover:to-green-500 transition font-semibold text-sm">View</button> |
90 | | - </div> |
| 119 | + {/each} |
| 120 | + |
| 121 | + <!-- Empty State --> |
| 122 | + {#if data.invoices.length === 0} |
| 123 | + <div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl p-12 text-center"> |
| 124 | + <div class="text-6xl mb-4">📄</div> |
| 125 | + <h3 class="text-2xl font-bold text-blue-900 mb-2">No invoices yet</h3> |
| 126 | + <p class="text-blue-600 mb-6">Create your first invoice to get started</p> |
| 127 | + <a href="/app/invoices/new" class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-700 to-purple-700 text-white font-semibold rounded-xl shadow-lg hover:from-blue-800 hover:to-purple-800 transition"> |
| 128 | + Create Invoice |
| 129 | + </a> |
91 | 130 | </div> |
92 | | - <div class="absolute right-4 top-1 opacity-10 text-[5rem] font-black text-green-200 select-none pointer-events-none">✅</div> |
93 | | - </div> |
94 | | - </div> |
95 | | - <!-- Pagination --> |
96 | | - <div class="max-w-5xl mx-auto mt-12 flex justify-center"> |
97 | | - <nav class="inline-flex items-center space-x-2 bg-white/80 backdrop-blur-md rounded-xl shadow px-6 py-3 border border-blue-200"> |
98 | | - <button class="px-3 py-1 rounded-full text-blue-400 hover:bg-blue-100 transition" disabled>«</button> |
99 | | - <button class="px-3 py-1 rounded-full bg-gradient-to-r from-blue-600 to-purple-600 text-white font-bold shadow">1</button> |
100 | | - <button class="px-3 py-1 rounded-full text-blue-700 hover:bg-blue-100 transition">2</button> |
101 | | - <button class="px-3 py-1 rounded-full text-blue-700 hover:bg-blue-100 transition">3</button> |
102 | | - <span class="px-2 text-blue-400">...</span> |
103 | | - <button class="px-3 py-1 rounded-full text-blue-700 hover:bg-blue-100 transition">10</button> |
104 | | - <button class="px-3 py-1 rounded-full text-blue-700 hover:bg-blue-100 transition">»</button> |
105 | | - </nav> |
| 131 | + {/if} |
106 | 132 | </div> |
107 | 133 | </div> |
108 | 134 | </div> |
0 commit comments