mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-07 19:58:24 +00:00
151bed230a
Denser star field, the page nebula, a favicon package, the filament banner without its box, the About portrait, Kashi and tea photos and Scorpius, the Store's own header, a header logo glow, a resume PDF that fills its page, one divider on the Library and Shelf, star-chart art for coverless favorites, Shelf Players, Play time and blog filters with a Show all fold, make kind pills on craft pages, and full-width closing bands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
198 lines
6.2 KiB
Plaintext
198 lines
6.2 KiB
Plaintext
---
|
|
/**
|
|
* The order request form: lines that can take an order now, the chosen
|
|
* line's options, quantity, details, notes, contact and the terms. When no
|
|
* category takes orders, or every open line is full, a notice shows instead.
|
|
*/
|
|
import Button from "@gk/ui/components/Button.astro";
|
|
import Notice from "@gk/ui/components/Notice.astro";
|
|
import PageShell from "@gk/ui/components/PageShell.astro";
|
|
import Star from "@gk/ui/components/Star.astro";
|
|
import TickScale from "@gk/ui/components/TickScale.astro";
|
|
import Breadcrumb from "../../components/store/Breadcrumb.astro";
|
|
import LineStatus from "../../components/store/LineStatus.astro";
|
|
import OrderForm from "../../components/store/OrderForm.astro";
|
|
import { site } from "../../lib/page.ts";
|
|
import { formatDate } from "../../lib/site.ts";
|
|
import { canOrder, channelOf, storeNav, storeState } from "../../lib/store.ts";
|
|
import { errorMessages, parseDraft, parseErrorCode } from "../../lib/forms.ts";
|
|
import { turnstileSiteKey } from "../../lib/forms-server.ts";
|
|
|
|
const s = await site();
|
|
const st = storeState(s);
|
|
// A post that was turned down comes back with its reason and the answers given.
|
|
const draft = parseDraft(Astro.cookies.get("gk-order-draft")?.value);
|
|
if (draft) {
|
|
Astro.cookies.delete("gk-order-draft", { path: "/" });
|
|
Astro.response.headers.set("Cache-Control", "private, no-store");
|
|
}
|
|
const errorCode = draft?.code ?? parseErrorCode(Astro.url.searchParams.get("error"));
|
|
const turnstileKey = turnstileSiteKey();
|
|
const selected = draft?.values.line ?? Astro.url.searchParams.get("line") ?? undefined;
|
|
const orderable = st.lines.filter((l) => canOrder(l, st));
|
|
const selling = st.lines.filter((l) => channelOf(l) === "here");
|
|
const shown = orderable.length ? orderable : selling;
|
|
const turnarounds = [...new Set(st.groups.map(([c]) => st.category(c).turnaround).filter((t): t is string => !!t))];
|
|
const reopens = st.closed.filter((c) => c.reopens).map((c) => `${c.name} reopens ${formatDate(c.reopens)}.`);
|
|
---
|
|
<PageShell title="Order request" family="store" current="store" store={storeNav(st, "/store/order")} description="Send an order request for something from the Store.">
|
|
{turnstileKey && <script slot="head" src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer is:inline></script>}
|
|
<section class="intro" aria-labelledby="order-h1">
|
|
<Breadcrumb here="Order request" />
|
|
<h1 id="order-h1">Order request</h1>
|
|
<p class="lede">Send a request and I will confirm the details and the total with you. No payment is taken through the site.</p>
|
|
</section>
|
|
|
|
<div class="layout">
|
|
<div class="main">
|
|
{errorCode && (
|
|
<div class="problem">
|
|
<Notice role="alert" title={errorMessages[errorCode]}>
|
|
{draft && Object.keys(draft.errors).length > 0 ? "The marked answers are below." : ""}
|
|
</Notice>
|
|
</div>
|
|
)}
|
|
{
|
|
orderable.length > 0 ? (
|
|
<OrderForm lines={orderable} selected={selected} storeTerms={st.terms} values={draft?.values} errors={draft?.errors} turnstileKey={turnstileKey} />
|
|
) : (
|
|
<div class="closed">
|
|
{st.anyOpen ? (
|
|
<Notice title="All order slots are taken right now.">Slots reopen as current orders finish.</Notice>
|
|
) : (
|
|
<Notice title="Orders currently closed.">{reopens.join(" ")}</Notice>
|
|
)}
|
|
<p>{st.settings?.capacityNote}</p>
|
|
<div class="actions">
|
|
<Button href="/store">Back to the Store</Button>
|
|
<Button variant="secondary" href="/inquiries">Something custom? Start an inquiry</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
</div>
|
|
|
|
<aside class="side" aria-label="Order status">
|
|
{shown.map((l) => <LineStatus line={l} />)}
|
|
{
|
|
(turnarounds.length > 0 || st.settings?.capacityNote) && (
|
|
<div class="know">
|
|
<span class="know-head">
|
|
<Star size={11} />
|
|
<span>Good to know</span>
|
|
</span>
|
|
<TickScale width={160} height={10} count={24} />
|
|
{turnarounds.map((t) => <span>{t}</span>)}
|
|
{orderable.length > 0 && st.settings?.capacityNote && <span>{st.settings.capacityNote}</span>}
|
|
</div>
|
|
)
|
|
}
|
|
{orderable.length > 0 && <a href="/inquiries" class="gk-text inq">Something custom? Start an inquiry</a>}
|
|
</aside>
|
|
</div>
|
|
</PageShell>
|
|
|
|
<style>
|
|
.intro {
|
|
padding: 36px 0 8px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
h1 {
|
|
margin: 0;
|
|
font-size: clamp(32px, 5vw, 44px);
|
|
letter-spacing: 0.05em;
|
|
}
|
|
.lede {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
color: var(--gk-soft);
|
|
max-width: 64ch;
|
|
line-height: 1.6;
|
|
}
|
|
.layout {
|
|
padding: 16px 0 64px;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 32px;
|
|
align-items: flex-start;
|
|
}
|
|
.main {
|
|
flex: 3 1 600px;
|
|
min-width: 0;
|
|
}
|
|
.problem {
|
|
margin-bottom: 16px;
|
|
}
|
|
.closed {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
padding: 32px;
|
|
border-radius: var(--gk-radius);
|
|
background: var(--gk-midnight);
|
|
border: 1px solid rgba(201, 162, 74, 0.55);
|
|
}
|
|
.closed :global(.gk-note) {
|
|
font-size: 15px;
|
|
}
|
|
.closed p {
|
|
margin: 0;
|
|
font-size: 15px;
|
|
line-height: 1.6;
|
|
color: var(--gk-soft);
|
|
}
|
|
.closed p:empty {
|
|
display: none;
|
|
}
|
|
.actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
}
|
|
.side {
|
|
flex: 1 1 280px;
|
|
max-width: 360px;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
}
|
|
.know {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
padding: 18px 20px;
|
|
border-radius: var(--gk-radius);
|
|
background: var(--gk-midnight);
|
|
border: 1px solid var(--gk-lines);
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
color: var(--gk-soft);
|
|
}
|
|
.know-head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 11px;
|
|
letter-spacing: 0.3em;
|
|
font-weight: 600;
|
|
color: var(--gk-gold-bright);
|
|
text-transform: uppercase;
|
|
}
|
|
.inq {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
align-self: flex-start;
|
|
}
|
|
@media (max-width: 700px) {
|
|
.side {
|
|
max-width: none;
|
|
}
|
|
.closed {
|
|
padding: 22px 16px;
|
|
}
|
|
}
|
|
</style>
|