mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
Store base
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
---
|
||||
/**
|
||||
* /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 & 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 — 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 & requirements (for when orders open)" />
|
||||
</Reveal>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
</StoreLayout>
|
||||
Reference in New Issue
Block a user