mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
92 lines
8.8 KiB
Markdown
92 lines
8.8 KiB
Markdown
# Store (store.gregkilmer.com) — MTG Proxy Cards & 3D-Printed Deck Boxes / Counters
|
||
|
||
## Summary
|
||
Add a self-contained **store** section to this repo that renders a commerce-style storefront for your proxy-card printing and 3D-printing services, with a gated email-submission order form. It lives at `/store` (and is reachable via `store.gregkilmer.com`), reuses the existing mystic night-sky aesthetic, and shares **no nav links** with the main site. A single JSON file you edit controls order-open/closed, per-category slot counts, and turnaround messaging.
|
||
|
||
**Key constraint honored:** no payment is taken; the order form is **hard-locked** until you flip a flag in the JSON.
|
||
|
||
---
|
||
|
||
## Architecture decision (defaulting, since the question went unanswered)
|
||
- **Same app, `/store` route set, with a store-only nav and zero cross-links from the main site.** Store pages are reachable only by direct URL or the `store.gregkilmer.com` subdomain (the subdomain routing is a reverse-proxy concern, not code). This reuses BaseLayout, the theme system, Icon, Reveal, SectionHeader, the contact/email pipeline, and Turnstile — no new dependencies.
|
||
- **Order lock = hard gate.** When `ordersOpen` is false the order page shows a clear "orders currently closed" notice, links to your email, and **renders no form at all**. When true, the full order form appears.
|
||
- **Capacity JSON drives:** overall store open/closed flag, open-slot counts for cards and for 3D-print jobs (shown as "X of Y open"), an estimated turnaround string, and a last-updated timestamp.
|
||
|
||
---
|
||
|
||
## Files to create
|
||
|
||
### 1. Store data + availability (the "simple JSON file you edit")
|
||
- **`src/data/store-availability.json`** — the single file you edit to control the storefront. Schema:
|
||
```json
|
||
{
|
||
"ordersOpen": false, // master gate — false = form hidden
|
||
"lastUpdated": "2026-08-04",
|
||
"turnaround": "Estimated turnaround: ~2 weeks once a list is confirmed.",
|
||
"maxNote": "I take a limited number of orders at a time so each gets full attention.",
|
||
"slots": {
|
||
"cards": { "open": 0, "total": 5 },
|
||
"deckBoxes": { "open": 0, "total": 4 }
|
||
}
|
||
}
|
||
```
|
||
- **`src/data/store-products.ts`** — typed product/pricing data (cards, deck boxes, finishes, sizing), authored in TS for type-safety and kept here (not JSON) so it's version-controlled with the components that render it. Pricing tables and product specs read from here.
|
||
|
||
### 2. Store-only layout + nav
|
||
- **`src/layouts/StoreLayout.astro`** — wraps `BaseLayout` with `surface="interest"` (warm candlelit tone, clearly distinct from the dark professional wing but still on-brand). Passes a `noindex` option (store can be noindexed until you're ready to promote). Renders a **store-specific top nav** (`/store`, `/store/cards`, `/store/deck-boxes`, `/store/order`) with a store wordmark and a small "store.gregkilmer.com" label, and a store footer variant that points to your email — **no links back to the main site**, per your request.
|
||
- **`src/components/store/StoreNav.astro`** — the store nav bar (mirrors `NavBar.astro`'s scroll-blur treatment and StarBurst mark, but store items only).
|
||
|
||
### 3. Reusable store UI components
|
||
- **`src/components/store/ProductCard.astro`** — image + spec list (size, thickness, finish, white back, etc.) for a product.
|
||
- **`src/components/store/PricingTable.astro`** — generic pricing-by-quantity table (takes rows of `{ quantity, price, note? }`).
|
||
- **`src/components/store/SlotMeter.astro`** — visual "X of Y slots open" meter with open/closed pill.
|
||
- **`src/components/store/RequirementList.astro`** — the rules/requirements list with mdi check/alert icons.
|
||
|
||
### 4. Pages
|
||
- **`src/pages/store/index.astro`** — Storefront home: hero (uses existing `/images/proxies.jpg` / `/images/deckboxes.jpg`), intro, **clear disclaimer block** (not affiliated with WotC/any game company; these are clearly-marked proxies, not counterfeits; will not print offensive content or custom art), capacity/availability summary from the JSON, links to the two product lines + order page.
|
||
- **`src/pages/store/cards.astro`** — Proxy MTG cards product line: example images, spec details (size = standard MTG 63×88mm, cardstock thickness/finish options, white back, etc.), pricing table for sets (e.g. 1–9 / 10–39 / 40–99 / 100+ tiers), double-sided-counts-as-two rule callout.
|
||
- **`src/pages/store/deck-boxes.astro`** — 3D-printed deck box & counter product line: example images (`/images/deckboxes.jpg`), sizing/finish/material details, pricing table for prints.
|
||
- **`src/pages/store/order.astro`** — Order page. Reads `store-availability.json`; when `ordersOpen === false`, renders the **closed notice + email link, no form**. When open, renders the order form island and shows the slot meters + requirements inline.
|
||
|
||
### 5. Order form + API
|
||
- **`src/components/store/OrderForm.tsx`** — React island modeled on the existing `ContactForm.tsx` (same idle/submitting/success/error states, honeypot, optional Turnstile, aria-live, same input styling). Fields: name, email, order type (cards / deck-box print / both), card list / deck-box spec (textarea), quantity/finish selectors, notes, honeypot, Turnstile token. **Posts to `/api/order`.**
|
||
- **`src/pages/api/order.ts`** — new POST endpoint cloned from `/api/contact.ts`'s proven pipeline: Zod validation, per-IP rate limit, Turnstile verification (reusing `@lib/env`), SMTP-or-webhook delivery to your inbox with a subject like `New store order — ${type}`. **Server-side hard gate:** even if a client POSTs while `ordersOpen` is false, the endpoint reads the JSON and returns 403 "Orders are currently closed." This makes the lock enforced both in UI and on the server.
|
||
|
||
### 6. Env additions
|
||
- Append to **`.env.example`** (and document in the new docs file): an optional `ORDER_TO` inbox (defaults to `CONTACT_TO`), and reuse existing `TURNSTILE_*`, `SMTP_*`, and rate-limit vars — **no new dependencies**. Order emails reuse the same `deliver()` pattern.
|
||
|
||
### 7. Docs
|
||
- **`docs/STORE.md`** — how to edit `store-availability.json` to open/close orders and update slots/turnaround, how the hard gate works (UI + API), how to point `store.gregkilmer.com` at the same server via your reverse proxy, and the "no cross-links" rule.
|
||
|
||
---
|
||
|
||
## Files modified
|
||
- **`src/lib/env.ts`** — add `orderTo()` helper (falls back to `contactTo()`).
|
||
- **`.env.example`** — add `ORDER_TO=` with a comment.
|
||
- **`src/middleware.ts`** — no change needed; CSP already allows `'self'` form-action, Turnstile origins, and same-origin connect. (I'll add a comment noting `/api/order` reuses the existing policy.)
|
||
- No change to the main site nav (`site.ts`), Footer, or any main-site page — **zero cross-links**, as requested.
|
||
|
||
---
|
||
|
||
## How each of your requirements is met
|
||
| Requirement | Where |
|
||
|---|---|
|
||
| Order form submits an email | `OrderForm.tsx` → `/api/order` → SMTP/webhook (cloned from working `/api/contact`) |
|
||
| Example images + product details | `cards.astro` / `deck_boxes.astro` + `ProductCard.astro` (size, thickness, finishes, white back, etc.) |
|
||
| Pricing tables for card sets & deck-box prints | `PricingTable.astro` fed by `store-products.ts` |
|
||
| Hosted at store.gregkilmer.com, no clear link between sites | `/store` route set, store-only nav, no main-site links; subdomain is a reverse-proxy mapping to the same server |
|
||
| No payment taken | Form collects a *request* only; no payment fields, no checkout |
|
||
| Order form locked until you choose to accept | Hard gate: `ordersOpen` in JSON hides the form (UI) **and** 403s the API (server) |
|
||
| Number of card + 3D-print slots, updatable from a simple JSON | `slots.cards` / `slots.deckBoxes` in `store-availability.json`, rendered by `SlotMeter.astro` |
|
||
| Clear "I can only take so many at a time" | `maxNote` + slot meters prominently on storefront and order page |
|
||
| Stated requirements (card list final, double-sided = 2 slots, payment is final, not affiliated with WotC, no counterfeits — clearly proxies, no offensive/custom art, etc.) | `RequirementList.astro` on storefront + repeated on the order page; order form requires an explicit acknowledgment checkbox |
|
||
|
||
---
|
||
|
||
## Defaults I'll use (flag any you want changed)
|
||
- **Disclaimer copy** drafted to match your wording (not affiliated with WotC / any game company; clearly-marked proxies, never counterfeits; no offensive or unauthorized custom art; card list is final once received; double-sided = two cards/two slots; all sales final). You can tweak exact phrasing after.
|
||
- **Pricing & specs** are placeholder structure with clearly-marked `[Greg to fill]` values (you'll swap in your real tier prices, cardstock GSM, finish names, deck-box dimensions). I won't invent real prices.
|
||
- **Slot counts** default to `{ cards: 0/5, deckBoxes: 0/4 }` and `ordersOpen: false`, so the site launches visibly "closed."
|
||
- **`noindex`** on by default for store pages until you say otherwise.
|
||
|
||
I will not commit or push anything. Ready to implement on approval. |