Files
com.gregkilmer.www/src/pages/store/order.astro
T
Greg Kilmer f4a49fe9bc Store base
2026-08-06 12:13:48 -04:00

141 lines
6.4 KiB
Plaintext

---
/**
* /store/order — Order request page
* ---------------------------------------------------------------------------
* Hard-gated by store-availability.json:
* - ordersOpen === false => renders a "closed" notice + email link, NO form.
* - ordersOpen === true => renders the OrderForm island + slot meters +
* the requirements list.
*
* The server endpoint /api/order re-checks the same file and returns 403 when
* closed, so the gate can't be bypassed by POSTing directly.
*/
import StoreLayout from '@layouts/StoreLayout.astro';
import Reveal from '@components/motion/Reveal.astro';
import SectionHeader from '@components/layout/SectionHeader.astro';
import Icon from '@components/Icon.astro';
import ObfuscatedEmail from '@components/ObfuscatedEmail.astro';
import OrderForm from '@components/store/OrderForm.tsx';
import SlotMeter from '@components/store/SlotMeter.astro';
import RequirementList from '@components/store/RequirementList.astro';
import { requirements } from '@data/store-products';
import { storeAvailability } from '@data/store-availability';
import { turnstileSiteKey } from '@lib/env';
const av = storeAvailability;
const open = av.ordersOpen;
const siteKey = turnstileSiteKey();
---
<StoreLayout
title="Order Request"
description="Submit a proxy-card or 3D-print order request. No payment is taken through the site."
>
<div class="container-prose flex flex-col gap-14 py-28 sm:py-32">
<section class="flex flex-col gap-5">
<Reveal>
<a
href="/store"
class="inline-flex items-center gap-1.5 font-mono text-xs uppercase tracking-[0.18em]"
style="color: var(--color-accent);"
>
<Icon name="arrow-left" class="h-4 w-4" />
Back to store
</a>
</Reveal>
<Reveal delay={60}>
<SectionHeader kicker="Order" icon="clipboard-list" title="Order request" />
</Reveal>
<Reveal delay={100}>
<p class="max-w-2xl text-lg leading-relaxed" style="color: var(--color-text-muted);">
This form sends me an order request by email. No payment is taken through the site.
{av.turnaround}
</p>
</Reveal>
</section>
{/* Availability meters — shown first so visitors immediately see whether
there's capacity before reading the rest of the page. */}
<section class="flex flex-col gap-5">
<Reveal><SectionHeader kicker="Availability" icon="gauge" title="Current order slots" /></Reveal>
<Reveal delay={60}>
<div class="grid gap-4 sm:grid-cols-2">
<SlotMeter label="Card orders" icon="cards-playing" open={av.slots.cards.open} total={av.slots.cards.total} />
<SlotMeter label="3D-print slots" icon="printer-3d-nozzle" open={av.slots.deckBoxes.open} total={av.slots.deckBoxes.total} />
</div>
</Reveal>
{/* Open/closed status banner (under the meters). */}
<Reveal delay={100}>
<div
class="flex flex-col gap-3 rounded-xl border p-5 sm:flex-row sm:items-center sm:justify-between"
style={open
? 'border-color: color-mix(in srgb, var(--color-accent) 35%, transparent); background: color-mix(in srgb, var(--color-accent) 8%, transparent);'
: 'border-color: color-mix(in srgb, #e55 40%, transparent); background: color-mix(in srgb, #e55 10%, transparent);'}
>
<div class="flex items-center gap-3">
<Icon name={open ? 'check-circle' : 'close-circle'} class="h-6 w-6 shrink-0" style={open ? 'color: var(--color-accent);' : 'color: #e55;'} />
<span class="text-sm sm:text-base" style="color: var(--color-text);">
{open ? 'Orders are open. Slots are limited.' : 'Orders are currently closed.'}
</span>
</div>
<span class="font-mono text-xs" style="color: var(--color-text-subtle);">
Updated {av.lastUpdated}
</span>
</div>
</Reveal>
</section>
{/* Either the form (open) or the closed notice (no form). */}
{open ? (
<section class="flex flex-col gap-8 lg:grid lg:grid-cols-[1.6fr_1fr] lg:items-start">
<Reveal>
<div class="card flex flex-col gap-2">
<h3 class="font-sans text-lg font-bold" style="color: var(--color-text);">Your request</h3>
<p class="text-sm" style="color: var(--color-text-muted);">
Fill in the details below. Remember: the card list is final once received, and
double-sided cards count as 2 slots.
</p>
<div class="mt-3">
<OrderForm client:idle turnstileSiteKey={siteKey} />
</div>
</div>
</Reveal>
<Reveal delay={80}>
<div class="flex flex-col gap-4 lg:sticky lg:top-24">
<RequirementList items={requirements} heading="Policies &amp; requirements" />
</div>
</Reveal>
</section>
) : (
<section class="flex flex-col gap-6">
<Reveal>
<div
class="flex flex-col items-center gap-4 rounded-2xl border p-8 text-center"
style="border-color: color-mix(in srgb, #e55 40%, transparent); background: color-mix(in srgb, #e55 8%, transparent);"
>
<span class="inline-flex h-12 w-12 items-center justify-center rounded-full" style="background: color-mix(in srgb, #e55 20%, transparent); color: #e55;" aria-hidden="true">
<Icon name="lock" class="h-6 w-6" />
</span>
<h3 class="font-sans text-xl font-bold" style="color: var(--color-text);">Orders are not being taken right now</h3>
<p class="max-w-md text-sm" style="color: var(--color-text-muted);">
I open a limited number of slots at a time. Check back here &mdash; the meters above
show live availability, and the order form appears as soon as orders reopen. You can
also email me directly with questions.
</p>
<div class="mt-1 flex flex-wrap items-center justify-center gap-2">
<Icon name="email" class="h-5 w-5" style="color: var(--color-accent);" />
<ObfuscatedEmail address="orders@gregkilmer.com" class="font-medium" style="color: var(--color-accent);" />
</div>
</div>
</Reveal>
<Reveal delay={60}>
<RequirementList items={requirements} heading="Policies &amp; requirements (for when orders open)" />
</Reveal>
</section>
)}
</div>
</StoreLayout>