Add giftcard management

This commit is contained in:
2026-06-06 14:26:42 +01:00
parent 310e6aaa80
commit 5258f90b06
3 changed files with 1748 additions and 0 deletions
@@ -0,0 +1,158 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import { Separator } from '$lib/components/ui/separator';
type CartItem = {
id: string;
label: string;
price: number;
qty: number;
};
let cart = $state<CartItem[]>([]);
let giftCardAmount = $state('25');
let showGiftCardInput = $state(false);
let subtotal = $derived(cart.reduce((sum, item) => sum + item.price * item.qty, 0));
let itemCount = $derived(cart.reduce((sum, item) => sum + item.qty, 0));
function formatCurrency(n: number): string {
return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(n);
}
function addItem(label: string, price: number) {
const existing = cart.find(i => i.label === label);
if (existing) {
existing.qty++;
} else {
cart = [...cart, { id: crypto.randomUUID(), label, price, qty: 1 }];
}
}
function addGiftCard() {
const amt = parseFloat(giftCardAmount);
if (isNaN(amt) || amt <= 0) return;
addItem('Gift Card', amt);
giftCardAmount = '25';
showGiftCardInput = false;
}
function removeItem(id: string) {
cart = cart.filter(i => i.id !== id);
}
function updateQty(id: string, delta: number) {
cart = cart.map(i => {
if (i.id !== id) return i;
const next = i.qty + delta;
return next <= 0 ? null : { ...i, qty: next };
}).filter((i): i is CartItem => i !== null);
}
</script>
<div class="rounded-xl border bg-card">
<div class="flex items-center justify-between border-b border-gray-200 px-5 py-4">
<h3 class="text-base font-semibold">Till Sales</h3>
</div>
<div class="grid grid-cols-2 gap-2 p-4 sm:grid-cols-3">
<Button variant="outline" size="sm" class="justify-start gap-2" onclick={() => addItem('Cuticle Oil', 8)}>
Cuticle Oil - &pound;8
</Button>
<Button variant="outline" size="sm" class="justify-start gap-2" onclick={() => addItem('Nail Files (Pack)', 5)}>
Nail Files - &pound;5
</Button>
<Button variant="outline" size="sm" class="justify-start gap-2" onclick={() => addItem('Hand Cream', 6)}>
Hand Cream - &pound;6
</Button>
<Button variant="outline" size="sm" class="justify-start gap-2" onclick={() => addItem('Base Coat', 7)}>
Base Coat - &pound;7
</Button>
<Button variant="outline" size="sm" class="justify-start gap-2" onclick={() => addItem('Top Coat', 7)}>
Top Coat - &pound;7
</Button>
<div class="relative">
{#if showGiftCardInput}
<div class="flex gap-1">
<div class="relative flex-1">
<span class="absolute left-2 top-1/2 -translate-y-1/2 text-xs text-gray-400">&pound;</span>
<Input
type="text"
inputmode="decimal"
bind:value={giftCardAmount}
class="h-9 pl-5 text-sm"
onkeydown={(e) => { if (e.key === 'Enter') addGiftCard(); }}
/>
</div>
<Button size="sm" variant="outline" onclick={addGiftCard} class="h-9 px-2 text-xs">Add</Button>
</div>
{:else}
<Button variant="outline" size="sm" class="w-full justify-start gap-2" onclick={() => showGiftCardInput = true}>
Gift Card
</Button>
{/if}
</div>
</div>
<Separator />
<div class="px-4 py-3">
{#if cart.length === 0}
<p class="py-6 text-center text-sm text-muted-foreground">Tap items above to add them to the sale.</p>
{:else}
<div class="max-h-48 space-y-1 overflow-y-auto">
{#each cart as item (item.id)}
<div class="flex items-center justify-between rounded-md border px-3 py-2 text-sm">
<div class="min-w-0 flex-1">
<span class="font-medium text-card-foreground">{item.label}</span>
<span class="ml-2 text-xs text-muted-foreground">{formatCurrency(item.price)} each</span>
</div>
<div class="flex items-center gap-2 shrink-0">
<button
type="button"
class="flex h-6 w-6 items-center justify-center rounded border text-xs text-muted-foreground hover:bg-accent"
onclick={() => updateQty(item.id, -1)}
>
&minus;
</button>
<span class="w-5 text-center text-sm font-semibold tabular-nums">{item.qty}</span>
<button
type="button"
class="flex h-6 w-6 items-center justify-center rounded border text-xs text-muted-foreground hover:bg-accent"
onclick={() => updateQty(item.id, 1)}
>
+
</button>
<span class="w-14 text-right text-sm font-semibold tabular-nums">{formatCurrency(item.price * item.qty)}</span>
<button
type="button"
aria-label="Remove item"
class="ml-1 flex h-6 w-6 items-center justify-center rounded text-xs text-muted-foreground hover:bg-red-50 hover:text-red-600"
onclick={() => removeItem(item.id)}
>
<svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
</div>
{/each}
</div>
<Separator class="my-3" />
<div class="flex items-center justify-between">
<span class="text-sm text-muted-foreground">
{itemCount} item{itemCount !== 1 ? 's' : ''}
</span>
<span class="text-lg font-bold tabular-nums">{formatCurrency(subtotal)}</span>
</div>
<Button class="mt-3 w-full" disabled>
Charge {formatCurrency(subtotal)}
</Button>
<p class="mt-1 text-xs text-muted-foreground">Payment flow and backend integration coming soon.</p>
{/if}
</div>
</div>