|
32 | 32 | interface OrderStatus { |
33 | 33 | order_id: number; |
34 | 34 | instance_id: number; |
35 | | - date: string; |
| 35 | + date: string | Date; |
36 | 36 | status: 'New' | 'Submitted' | 'Shipped' | 'Delivered' | 'InStorage' | 'In Storage'; |
37 | 37 | } |
38 | 38 | let statuses: OrderStatus[] = $state([]); |
|
49 | 49 | }); |
50 | 50 | statuses = body.statuses; |
51 | 51 | statuses = statuses.map((status) => { |
52 | | - status.date = new Date(status.date).toLocaleString('en-US', { |
53 | | - weekday: 'short', |
54 | | - year: 'numeric', |
55 | | - month: 'long', |
56 | | - day: 'numeric' |
57 | | - }); |
| 52 | + status.date = new Date(status.date); |
58 | 53 | if (status.status === 'InStorage') { |
59 | 54 | status.status = 'In Storage'; |
60 | 55 | } |
|
110 | 105 | out.sort((a, b) => (a.instance_id < b.instance_id ? -1 : 1)); |
111 | 106 | return out; |
112 | 107 | } |
| 108 | +
|
| 109 | + function exportCSV() { |
| 110 | + const header = ['Name', 'Vendor', 'Link', 'Count', 'Unit Cost', 'Store In', 'Team', 'Reason', 'Subtotal', 'Status']; |
| 111 | + const lines = [header.join(',')]; |
| 112 | +
|
| 113 | + for (const o of orders) { |
| 114 | + const st = statuses |
| 115 | + .filter(s => s.order_id === o.id) |
| 116 | + .map(s => { |
| 117 | + const date = s.date as Date; |
| 118 | + const day = String(date.getDate()).padStart(2, '0'); |
| 119 | + const month = String(date.getMonth() + 1).padStart(2, '0'); |
| 120 | + const year = String(date.getFullYear()).slice(-2); |
| 121 | + return `${s.status}: ${day}/${month}/${year}`; |
| 122 | + }) |
| 123 | + .join(','); |
| 124 | + const sub = (o.count * (o.unit_cost as number)).toFixed(2); |
| 125 | + lines.push([ |
| 126 | + o.name, |
| 127 | + o.vendor, |
| 128 | + o.link, |
| 129 | + o.count, |
| 130 | + o.unit_cost, |
| 131 | + o.store_in, |
| 132 | + o.team, |
| 133 | + o.reason, |
| 134 | + sub, |
| 135 | + st, |
| 136 | + ].map(String).join(',')); |
| 137 | + } |
| 138 | +
|
| 139 | + const csv = new Blob([lines.join('\n')], { type: 'text/csv' }); |
| 140 | + const url = URL.createObjectURL(csv); |
| 141 | + const link = document.createElement('a'); |
| 142 | + link.href = url; |
| 143 | + link.download = 'manifest.csv'; |
| 144 | + link.click(); |
| 145 | + URL.revokeObjectURL(url); |
| 146 | + } |
113 | 147 | </script> |
114 | 148 |
|
115 | 149 | <svelte:head> |
|
123 | 157 | Hide "In Storage" |
124 | 158 | </label> |
125 | 159 |
|
126 | | - {#if fetching} |
127 | | - <button disabled> Fetching... </button> |
128 | | - {:else} |
129 | | - <button onclick={refreshOrders}> Refresh </button> |
130 | | - {/if} |
| 160 | + <div class="flex flex-row gap-4"> |
| 161 | + {#if fetching} |
| 162 | + <button disabled> Fetching... </button> |
| 163 | + {:else} |
| 164 | + <button onclick={refreshOrders}> Refresh </button> |
| 165 | + {/if} |
| 166 | + <button onclick={exportCSV}> Export CSV </button> |
| 167 | + </div> |
131 | 168 | </div> |
132 | 169 | <table> |
133 | 170 | <thead> |
|
160 | 197 | <td class="order-name">{order.name}</td> |
161 | 198 | <td class="order-status"> |
162 | 199 | {#each statusesOf(order.id) as status} |
163 | | - <p>{status.status}: {status.date}</p> |
| 200 | + <p><span class="italic">{status.status}</span>: {status.date.toLocaleString('en-US', { |
| 201 | + weekday: 'short', |
| 202 | + year: 'numeric', |
| 203 | + month: 'long', |
| 204 | + day: 'numeric' |
| 205 | + })}</p> |
164 | 206 | {/each} |
165 | 207 | </td> |
166 | 208 | <td class="order-vendor">{order.vendor}</td> |
|
459 | 501 | <output>{orderOperationOutput}</output> |
460 | 502 | {/if} |
461 | 503 | {:else if tabIndex === 4} |
462 | | - {#if selectedOrderId === null} |
463 | | - {@render selectAnOrder()} |
464 | | - {:else} |
465 | | - <button |
466 | | - onclick={async () => { |
467 | | - const response = await fetch(`${PUBLIC_API_ENDPOINT}/api/manifest/del/order`, { |
468 | | - method: 'DELETE', |
469 | | - headers: { |
470 | | - 'Content-Type': 'application/json' |
471 | | - }, |
472 | | - body: JSON.stringify({ |
473 | | - id: selectedOrderId |
474 | | - }) |
475 | | - }); |
476 | | - if (response.ok) { |
477 | | - orderOperationOutput = ''; |
478 | | - refreshOrders(); |
479 | | - } else { |
480 | | - orderOperationOutput = await response.text(); |
481 | | - } |
482 | | - }} |
483 | | - > |
484 | | - Cancel Order |
485 | | - </button> |
486 | | - <output>{orderOperationOutput}</output> |
487 | | - {/if} |
| 504 | + {#snippet cost(team: string)} |
| 505 | + <p>{team} Total: {orders |
| 506 | + .filter(o => o.team === team) |
| 507 | + .reduce((acc, cur) => acc + cur.count * (cur.unit_cost as number), 0).toLocaleString('en-US', { style: 'currency', currency: 'USD' })}</p> |
| 508 | + {/snippet} |
| 509 | + {@render cost("Software")} |
| 510 | + {@render cost("Mechanical")} |
| 511 | + {@render cost("Electrical")} |
488 | 512 | {/if} |
489 | 513 | </section> |
490 | 514 | </section> |
|
0 commit comments