A minimal Next.js website for a bespoke garden design service. Visitors upload a photo of their flower bed, share preferences, and choose one of three plans via Stripe — Blueprint (£10), Blueprint + Photo (£25), or Full Plan (£75).
npm install
cp .env.example .env.local # fill in values (see below)
npm run dev- Create a project at https://supabase.com
- Run this SQL in the SQL Editor:
create table submissions (
id uuid primary key default gen_random_uuid(),
created_at timestamptz default now(),
name text not null,
email text not null,
location text not null,
color_prefs text,
avoid_plants text,
comments text,
photo_url text not null,
stripe_session_id text,
payment_status text default 'pending'
);- Create a Storage bucket named
garden-photoswith Public access. - Copy your project URL and keys into
.env.local.
- Create an account at https://stripe.com
- Copy your Secret key and Publishable key into
.env.local. - Seed the three tier Products + Prices in Stripe:
The script is idempotent — re-run it anytime tier prices or names change in
npm run stripe:sync
lib/tiers.ts. It prints an env block; paste the sixSTRIPE_PRODUCT_*_ID/STRIPE_PRICE_*_IDvalues into.env.local. Run it again with ask_live_...key to seed production, then paste those IDs into your Vercel env. - For local webhook testing:
Copy the
stripe listen --forward-to localhost:3000/api/webhook
whsec_...signing secret it shows you intoSTRIPE_WEBHOOK_SECRET. - For production, register a webhook in the Stripe dashboard pointing to
https://your-domain.com/api/webhook, listening forcheckout.session.completed.
- Set
ADMIN_PASSWORDto a strong password in.env.local. - Set
ADMIN_COOKIE_SECRETto any random 32-character string. - Visit
/admin/loginand sign in with your password.
npm i -g vercel
vercelAdd all .env.local variables to your Vercel project's environment variables, then redeploy.
Placeholder cards are defined in data/portfolio.ts. Replace the imageUrl values with your own work and drop the photos in public/examples/.
app/
page.tsx Landing page
submit/page.tsx Customer request form
success/page.tsx Post-payment confirmation
admin/page.tsx Submissions dashboard
admin/login/ Admin login
api/submit/ Upload photo + create Stripe session
api/webhook/ Stripe webhook (marks submissions paid)
api/admin/login/ Admin password → session cookie
components/
SubmitForm.tsx Drag-drop form (client)
AdminTable.tsx Submissions table + detail modal (client)
lib/
supabase.ts Supabase server client
stripe.ts Stripe instance (lazy)
data/portfolio.ts Static example work
proxy.ts Admin route guard (JWT cookie check)