Skip to content

feat: shipping methods, announcement banners, legal compliance#1

Merged
Lutherwaves merged 132 commits intomainfrom
feature/blox-349-vamyart-integration-for-selling-build-or-buy
Mar 13, 2026
Merged

feat: shipping methods, announcement banners, legal compliance#1
Lutherwaves merged 132 commits intomainfrom
feature/blox-349-vamyart-integration-for-selling-build-or-buy

Conversation

@Lutherwaves
Copy link
Copy Markdown
Contributor

Summary

  • Shipping methods: Admin-configurable shipping (Free/Custom/Paid) per product. Seeded with Free for prints, Custom for originals. Stripe checkout adds shipping line item only for paid type. Buyer sees shipping text on product page.
  • Announcement banners: Admin CRUD for global or page-scoped banners with live toggle. Rendered on every website page via _app.tsx.
  • Legal compliance: Privacy Policy page (/privacy), legal notice in footer (company EIK, address), Privacy link in footer, copyright year auto-increments, Terms corrected (Stripe-only payments, VAT language, company details added).

What's New

  • packages/db: shipping_methods + banners tables, tRPC routers for both, products.shippingMethodId FK
  • apps/admin: Shipping Methods page, Banners page, shipping dropdown on Artworks page, nav items
  • apps/website: AnnouncementBanner component, shipping display in ProductSelector, privacy page, footer updates

Test Plan

  • Admin: create/edit/delete a shipping method; verify Free → prints, Custom → originals seeded
  • Admin: create a global banner, toggle Live — verify it appears on website; create page-scoped banner for gallery, verify it shows only on /gallery
  • Admin: change a product's shipping method via dropdown on Artworks page
  • Website: product page shows correct shipping text (green for free, muted for custom)
  • Website: Stripe checkout for a paid-shipping product includes shipping line item
  • Website: /privacy page loads
  • Website: footer shows Terms + Privacy links, legal notice, current year in copyright
  • All 11 unit tests pass (pnpm test)

🤖 Generated with Claude Code

Lutherwaves and others added 30 commits March 5, 2026 14:54
Full architecture decision record covering monorepo restructure,
tRPC+Drizzle+Supabase stack, bidding system, product catalog with
JSONB variants, guest checkout, admin panel, i18n, and Netlify deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
22-task plan covering monorepo restructure, tRPC+Drizzle+Supabase stack,
bidding system, Stripe checkout, admin panel, i18n, and Netlify deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Publishes from apps/website/.next with turbo filter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8 tables: artworks, products, product_variants, orders, auctions, bids,
inquiries, newsletter_subscribers. Migration applied to Supabase.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Includes context, router factory, inquiries (Resend emails) and
newsletter (Buttondown sync) routers. Tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
validateBid is a pure function — 5 unit tests covering deadline,
min bid, and increment enforcement. All 7 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds @vamy/db exports for ./trpc and ./trpc/context.
Stripe initialized lazily to avoid module-load errors in tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- App Router: /api/trpc/[trpc] + /api/webhooks/stripe
- src/lib/trpc.ts client
- Lazy DB + Resend init to avoid build-time throws
- next.config.js: ignoreBuildErrors (type-check via tsc --noEmit in CI)
- tsconfig paths for @vamy/* workspace packages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- tests/ workspace: Vitest for API, Playwright for UI
- API tests: inquiries.create + newsletter.subscribe (6/6 pass)
- E2E tests: page smoke tests + inquiry form prefill (6/6 pass)
- tRPC v11 wire format: raw JSON body (no superjson wrapper)
- apps/website/.env.example: documents all required env vars
- pnpm-workspace.yaml: includes tests/ package

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Blocks direct anon-key access to every table. Server-side tRPC uses
DATABASE_URL (postgres superuser) so it bypasses RLS unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove HubSpot integration from FormBlock, replace with trpc.inquiries.create
- Add tRPC + React Query provider to _app.tsx (replaced _app.js)
- Add NewsletterSignup component to Footer via trpc.newsletter.subscribe

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tripe

- BidWidget: live bid display with Supabase Realtime + 30s polling fallback, countdown timer, bid modal
- ProductSelector: variant picker with Stripe Checkout redirect
- Install @supabase/supabase-js in website package

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cher

- Install next-intl, configure withNextIntl plugin in next.config.js
- Add i18n Pages Router config (locales: en/de/bg, default: en)
- Create src/i18n/routing.ts and request.ts for App Router integration
- Add LocaleSwitcher dropdown to Header (uses next/router locale switching)
- Inline locale names in Header to avoid webpack bundling TS workspace source

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Task 15 — Bootstrap:
- Next.js 15 App Router, TypeScript strict mode, Tailwind, autoprefixer
- Route group (dashboard)/ for protected pages, login/ for public auth
- tRPC API route at /api/trpc (reuses appRouter from @vamy/db)
- Providers client component (tRPC + React Query)
- Netlify config, .gitignore, .env.example

Task 16 — Auth:
- @supabase/ssr (recommended package for App Router, not deprecated auth-helpers)
- lib/supabase/server.ts + client.ts (server/browser split)
- middleware.ts: redirects unauthenticated users, refreshes session cookies
- Login page with email/password form

Tasks 17-20 — Dashboard views:
- /auctions: open/close auctions, bid history table
- /orders: fulfillment table with tracking number input + mark shipped
- /artworks: product list with inline variant management (stock, price)
- /inquiries: table with mailto: reply link, mark-handled action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The withNextIntl plugin is App Router only. Using it alongside the Pages
Router i18n config caused a routing conflict that 404'd the root URL.
Removed the plugin wrapper; Pages Router handles locale routing natively
via the i18n config in next.config.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…for locale pref

The Pages Router i18n config conflicts with Stackbit's getStaticPaths URL string
format, causing GET / to 404. Removed i18n config; locale switcher now stores
preference in localStorage. URL-based locale routing requires a deeper content
architecture change and is deferred.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ield names

- Add Vamy logo to admin sidebar header
- Seed 3 artworks (Whispers, First Contact, On the Horizon) as available
- Seed original painting product (€2,500 placeholder, 1 of 1) per artwork
- Seed fine art print product per artwork with 4 giclée variants:
    A4 21×30cm €75 (ed. 50), A3 30×42cm €120 (ed. 50),
    50×70cm €200 (ed. 30), 70×100cm €350 (ed. 20)
  Industry standard: 300gsm archival cotton, signed & numbered limited edition
- Fix orders page: productVariant.name, amountPaid (not totalAmount/variantId)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without relations defined, all db.query calls using `with: {}` returned 500.
Added relations for artworks, products, productVariants, orders, auctions, bids.
Fixed inquiries page: inq.handled → inq.handledAt (actual column name).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ess page

- PostLayout: dynamically imports ProductSelector + BidWidget (ssr:false),
  extracts artwork slug from URL path, renders commerce widgets after content
- /order/success: confirmation page shown after Stripe checkout completes
- Gallery markdown: rename CTA from "PREORDER A PRINT" to "INQUIRE ABOUT THE
  ORIGINAL" — disambiguates from the new print purchase selector

Purchase flow is now end-to-end:
  artwork page → ProductSelector → Stripe Checkout → webhook → order in DB
  → confirmation email to buyer + notification to artist → /order/success

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New /get-a-piece page (bypasses catch-all, owns its own route)
- Two-column layout: artwork context + 4-step process guide on left,
  form on right
- When arrived from artwork page (?piece=), shows artwork thumbnail,
  medium, dimensions, and pre-fills + locks the piece field with
  an escape hatch to change it
- Labelled fields (not just placeholders), optional message with
  contextual placeholder examples
- Personalised success state ("Thank you, {name}")
- Micro-copy: "Maeve will reply personally — no bots, no templates"
- Exclude /get-a-piece from [[...slug]] to avoid path conflict

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds optional userId field (default null) to the tRPC context, laying
the groundwork for protectedProcedure auth checks. Updates inquiry
test mocks to satisfy the updated context shape.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lutherwaves and others added 29 commits March 11, 2026 02:00
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mplementation details

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds ISR to the Pages Router catch-all: new paths render on demand
(fallback:blocking) and cached pages revalidate every hour.

Also adds tRPC v11 server caller (appRouter.createCaller) for use in
getStaticProps in upcoming data-fetching tasks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET (query) responses are cached for 1 hour at the CDN edge with
stale-while-revalidate of 24 hours. POST (mutation) responses are
passed through unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Accepts POST with x-revalidate-secret header and a comma-separated
paths query param. Calls res.revalidate() for each path in parallel.
REVALIDATION_SECRET and NEXT_PUBLIC_WEBSITE_URL added to .env.local (gitignored).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vamy/db imports postgres (Node.js-only) which webpack tries to bundle
for the browser when pages import from @vamy/db/trpc. Adding webpack
externals for postgres/resend/stripe in browser builds prevents the
'can't resolve fs/perf_hooks' errors while keeping transpilePackages
intact for TypeScript compilation.

Also adds server-only to @vamy/db devDependencies for future use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n homepage

Replaces the client-side tRPC hook in AnnouncementBanner with a prop-driven
component fed from getStaticProps. Banner and featured artwork image are now
embedded in the static HTML at build/revalidation time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes client-side ArtworkCardInfo dynamic import. Gallery index now fetches
product data per artwork in getStaticProps and passes it via props. ArtworkCardInfoStatic
renders medium, dimensions, price, and availability without any client-side JS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes client-side ArtworkDetails dynamic import. Detail pages now receive
artworkProduct via getStaticProps and render medium, dimensions, price, and
availability through ArtworkDetailsStatic with no client-side data fetching.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ce post mutation

Extract shared variant-derivation logic from ArtworkCardInfoStatic and
ArtworkDetailsStatic into apps/website/src/utils/artwork-product.ts.
Replace in-place post.artworkProduct mutation in [[...slug]].js with a
non-mutating map that returns new post objects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ro components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…policies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The release workflow was using npm (causing lock file errors) and
triggering on PRs where semantic-release has nothing to do.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Lutherwaves Lutherwaves merged commit 9f1e3dd into main Mar 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant