Implementation Plan
Stack: Next.js 14 (App Router) + Tailwind CSS
Hosting: Vercel (free tier)
Budget: $0–30/month total (no Shopify)
Design source: /Design prototype development/design_handoff_alnuzha_website/
Stack Decision
| Layer | Tool | Cost | Why |
|---|---|---|---|
| Framework | Next.js 14 App Router | Free | SSR + SSG + API routes in one; design README recommends it explicitly |
| Styling | Tailwind CSS | Free | Maps directly to design tokens; no extra CSS framework needed |
| Fonts | Google Fonts | Free | Fraunces, Source Sans 3, IBM Plex Sans Arabic, IBM Plex Mono — all available |
| Cart state | React Context + localStorage | Free | Already specified in design README (nuzha_cart key, merge logic documented) |
| Forms | React Hook Form + Zod | Free | Validation without a library bloat |
| Email (forms) | Resend | Free (100/day) | Wholesale inquiry + catalogue gate emails; simple API route |
| Hosting | Vercel | Free tier | Next.js native, global CDN, automatic deploys from GitHub |
| Analytics | Google Analytics 4 | Free | Already called out in business strategy |
| Product data | Static JSON files | Free | No DB needed for MVP; easy to migrate later |
| CMS (Phase 2) | Supabase free tier | Free | If dynamic content needed; 500 MB free |
| Payments (Phase 3) | Stripe | 2.9% + 30¢/txn | No monthly fee; only pay on sales |
Total monthly cost: $0 to start. Domain ($12/year) is the only required spend.
Project File Structure
alnuzhadates/
│
├── app/ # Next.js App Router
│ ├── layout.tsx # Root layout (fonts, cart provider, WhatsApp btn)
│ ├── page.tsx # Homepage
│ ├── shop/
│ │ └── page.tsx # Shop / product listing
│ ├── products/
│ │ └── [slug]/
│ │ └── page.tsx # Product detail (dynamic route)
│ ├── cart/
│ │ └── page.tsx # Cart + checkout
│ ├── wholesale/
│ │ ├── page.tsx # Wholesale hub landing
│ │ ├── request-quote/
│ │ │ └── page.tsx # Inquiry form page
│ │ ├── private-label/
│ │ │ └── page.tsx
│ │ └── catalogue/
│ │ └── page.tsx # Gated catalogue download
│ ├── our-story/
│ │ └── page.tsx
│ ├── certifications/
│ │ └── page.tsx
│ ├── markets/
│ │ └── page.tsx
│ ├── blog/
│ │ ├── page.tsx # Blog index
│ │ └── [slug]/
│ │ └── page.tsx # Blog post
│ ├── contact/
│ │ └── page.tsx
│ └── api/
│ ├── inquiry/
│ │ └── route.ts # Wholesale inquiry → email
│ └── catalogue/
│ └── route.ts # Catalogue gate → email + download link
│
├── components/
│ ├── layout/
│ │ ├── AnnouncementBar.tsx
│ │ ├── Header.tsx # Sticky, scroll-aware, cart count badge
│ │ ├── Footer.tsx
│ │ └── WhatsAppButton.tsx # Fixed floating green button
│ ├── home/
│ │ ├── Hero.tsx
│ │ ├── TrustStrip.tsx
│ │ ├── VarietiesGrid.tsx
│ │ ├── DualPath.tsx
│ │ ├── FromMadinah.tsx
│ │ ├── WhySourceFromUs.tsx
│ │ ├── ValueAddedRange.tsx
│ │ ├── MarketsProof.tsx
│ │ └── LeadMagnet.tsx
│ ├── shop/
│ │ ├── FilterSidebar.tsx
│ │ ├── ProductGrid.tsx
│ │ ├── ProductCard.tsx
│ │ └── Toast.tsx
│ ├── product/
│ │ ├── Gallery.tsx
│ │ ├── ProductInfo.tsx # Grade selector, qty stepper, add-to-cart
│ │ ├── CertStrip.tsx
│ │ └── RelatedProducts.tsx
│ ├── cart/
│ │ ├── CartEmpty.tsx
│ │ ├── CartLineItem.tsx
│ │ └── OrderSummary.tsx
│ ├── wholesale/
│ │ ├── InquiryForm.tsx # The primary lead-capture form
│ │ └── CatalogueGate.tsx
│ └── ui/ # Reusable design system components
│ ├── Button.tsx # Primary / secondary / green variants
│ ├── Badge.tsx # In stock / Best seller / New harvest / Wholesale only
│ ├── Input.tsx # Shared form input (focus → amber border)
│ ├── Card.tsx # Variety card / value-prop card
│ ├── Eyebrow.tsx # Uppercase label component
│ └── SectionWrapper.tsx # Max-width 1240px + horizontal padding
│
├── context/
│ └── CartContext.tsx # Cart state, localStorage sync, tab sync
│
├── data/
│ ├── products.ts # All product data (varieties, value-added, gift boxes)
│ └── content.ts # Static text strings (markets, testimonials, certs)
│
├── hooks/
│ ├── useCart.ts
│ └── useScrolled.ts # Header scroll-aware hook
│
├── lib/
│ ├── email.ts # Resend email sender utility
│ └── i18n.ts # EN/AR translation map
│
├── public/
│ ├── fonts/ # Self-hosted fallback if needed
│ ├── images/ # Product photos, cert logos (when available)
│ └── catalogue.pdf # The gated wholesale catalogue
│
├── styles/
│ └── globals.css # Tailwind base + custom CSS variables
│
├── tailwind.config.ts # All design tokens mapped here
└── next.config.ts
Design Token → Tailwind Mapping
The tailwind.config.ts will define all tokens from the design system so class names remain semantic throughout the codebase.
colors:
brown: #4A2E1E → text-brown, bg-brown
gold: #B8772E → text-gold, bg-gold, border-gold
gold-hover: #A3681F
green: #3B5A40 → text-green, bg-green
green-hover: #456B4B
cream: #FAF5EC → bg-cream (page background)
sand: #F0E7D6 → bg-sand (cards, alternating sections)
charcoal: #2B2420 → text-charcoal (body)
line: #E3D8C5 → border-line
muted: #6B6058
faint: #9C8A6E
sub-brown: #D8CAB2
fontFamily:
serif: ['Fraunces', ...]
sans: ['Source Sans 3', ...]
arabic: ['IBM Plex Sans Arabic', ...]
mono: ['IBM Plex Mono', ...]
maxWidth:
site: 1240px
borderRadius:
btn: 7px
card: 14px
pill: 100px
Phase 1 — MVP
Goal: Live site that captures B2B leads and sells DTC. Every page from the design prototype is implemented faithfully. Timeline: 3–4 weeks Cost: $0 (Vercel free + Resend free)
Task 1.1 — Project Setup
-
npx create-next-app@latest alnuzhadates --typescript --tailwind --app --src-dir no - Install dependencies:
react-hook-form,zod,@hookform/resolvers,resend,clsx - Configure
tailwind.config.tswith all design tokens (colors, fonts, spacing, radii, shadows) - Set up Google Fonts in
app/layout.tsx: Fraunces (400, 500, 600), Source Sans 3 (400, 500, 600, 700), IBM Plex Sans Arabic (400, 500), IBM Plex Mono (400) - Add CSS custom properties in
globals.cssfor any values Tailwind can't express (e.g.nuzhaPopandnuzhaFadekeyframe animations) - Create
CartContext.tsxwithlocalStoragekeynuzha_cart, item shape{ id, name, grade, price, qty }, merge keyid+"|"+grade, tab-sync viawindow.storageevent - Create
data/products.tswith all 9 products (varieties + value-added + gift boxes) including SAR prices - Create
.env.localwithRESEND_API_KEY,INQUIRY_EMAIL_TO,CATALOGUE_PDF_URL - Set up GitHub repo + connect to Vercel for automatic deploys
Task 1.2 — UI Component Library (components/ui/)
Implement all base components from Design System.dc.html before building pages.
Button.tsx
-
variant: 'primary' | 'secondary' | 'green' | 'text' -
size: 'sm' | 'default' | 'lg' - Primary:
bg-gold text-white hover:bg-gold-hover shadow-[0_2px_4px_rgba(74,46,30,.18)] - Secondary:
border-[1.5px] border-brown text-brown hover:bg-brown hover:text-cream - Green:
bg-green text-cream hover:bg-green-hover - Text link:
text-brown border-b-[1.5px] border-gold hover:text-gold - Disabled:
bg-[#D8C4A3] cursor-not-allowed
Badge.tsx
-
variant: 'instock' | 'bestseller' | 'newharvest' | 'wholesale' - All use
border-radius: 100px, 12–13px, weight 600 - In stock: sand pill + green dot
- Best seller: solid green / cream text
- New harvest: solid gold / white text
- Wholesale only: white,
1px solid brown, brown text
Input.tsx
- Border
1.5px solid #E3D8C5, bg cream, radius 8px, padding 12–13px × 15px - Focus:
border-gold bg-white - Label: 13.5px, weight 600, brown, 7–8px gap below
Card.tsx
- Rest:
1px solid #E3D8C5, no shadow - Hover:
shadow-[0_16px_32px_rgba(74,46,30,.10)] -translate-y-1 - Transition:
transition-[transform,box-shadow] duration-200 ease - Radius 12–16px
SectionWrapper.tsx
-
max-w-[1240px] mx-auto px-8(32px horizontal padding) - Accepts
classNamefor section-level overrides
Eyebrow.tsx
-
text-[12-13px] font-semibold uppercase tracking-[.14em] text-gold
Toast.tsx
- Fixed bottom-center, green bg, white text
- Animate in:
nuzhaFade(translateY + opacity), 0.3s ease - Auto-dismiss after 1.8s
- Used on "Add to cart" in Shop and Product pages
Task 1.3 — Layout Components
AnnouncementBar.tsx
- Date Brown background (
bg-brown) - Left: "Certified & Documented · HACCP · Halal · ISO 22000 · Ships to 20+ countries" — dot-separated, Fraunces 14px,
text-sub-brown - Right:
[EN | AR]toggle button — outline pill, click callstoggleLang()from context
Header.tsx
- Logo: brown circle "N" + "Al Nuzha / MADINAH DATES" wordmark (swap for real logo later)
- Nav links: Products, Wholesale, Our Story, Quality, Markets, Contact
- CTAs:
[Request a Quote](primary) +[Shop](secondary) - Cart icon with live count badge (reads from CartContext)
- Sticky:
position: sticky; top: 0; z-index: 50 - Scroll behavior:
useScrolledhook — whenscrollY > 16, reduce vertical padding + addshadow-[0_6px_22px_rgba(74,46,30,.08)] - Mobile: hamburger menu, nav collapses to drawer
Footer.tsx
- Date Brown background, 4-column layout:
- Col 1: Brand (circle "N" logo + tagline) + WhatsApp button (green,
💬 WhatsApp us) - Col 2: Products (links to each variety + value-added)
- Col 3: Company (Our Story, Wholesale, Certifications, Markets, Blog)
- Col 4: Contact (Madinah address, email, phone)
- Col 1: Brand (circle "N" logo + tagline) + WhatsApp button (green,
- Bottom row: certification logos + copyright + EN/AR toggle
WhatsAppButton.tsx
- Fixed,
bottom-6 right-6(orleft-6for RTL) - Green circle,
💬or WhatsApp icon, 56px -
href="https://wa.me/[NUMBER]"— placeholder until number confirmed -
target="_blank" rel="noopener noreferrer"
Task 1.4 — Homepage (app/page.tsx)
Implement all 13 sections from Al Nuzha Homepage.dc.html in order:
Section 1: Announcement Bar — (AnnouncementBar component, already in layout)
Section 2: Header — (Header component, already in layout)
Section 3: Hero
- 2-column grid
grid-cols-[1.05fr_1fr]on desktop, stacked on mobile - Left: green eyebrow pill "Single-origin · Madinah Al-Munawwarah", H1 "Premium dates from Madinah — the home of Ajwa.", sub-headline, two CTAs
[Wholesale Inquiry](primary) +[Shop Dates](secondary) - Stats row: "20+ countries · 15+ varieties · 4 certifications" with top border divider above
- Right: rounded image placeholder (striped
#E7DCC7+#DCCDB0) for hero photo - Full-bleed cream background
Section 4: Trust Strip
- Date Brown band, centered
- "Certified & documented · HACCP · Halal · ISO 22000 · Certificate of Origin · Phytosanitary" — dot-separated, Fraunces 18px,
text-sub-brown
Section 5: Our Varieties
- Section header: eyebrow + H2 "Our varieties" + "View all varieties →" link to
/shop - 3-column grid of 6 cards: Ajwa, Safawi, Sukkari, Mabroom, Ambari, Khudri
- Each card: sand bg, image placeholder, Fraunces variety name, short note
- Card hover lift (Card component)
- Mobile: 2-up grid or horizontal scroll
Section 6: Dual Path
- Sand background band
- 2 equal panels side-by-side (stacked on mobile):
- Left: Date Brown panel, "For businesses", headline, "Wholesale, private label & bulk export",
[Request a Quote](primary) - Right: White panel, "For you", headline, "Gift boxes & premium packs, delivered",
[Shop Now](primary)
- Left: Date Brown panel, "For businesses", headline, "Wholesale, private label & bulk export",
Section 7: From Madinah
- 2-column: image placeholder left, text right (reverse on mobile)
- Eyebrow + H2 "From the palms of Madinah", 2–3 sentences origin story, "Read our story →" link to
/our-story
Section 8: Why Source From Us
- Date Brown background
- Centered H2
- 4-column grid of value-prop cards: amber top border, square numbered icon, title, short description
- Single-origin Madinah
- Certified & documented
- Reliable supply & lead times
- Private-label ready
Section 9: Value-Added Range
- Cream background, centered header
- 4-column grid: Stuffed dates · Chocolate-coated · Date paste · Date syrup
- Each: image placeholder, eyebrow category, name, link to product page
Section 10: Markets / Social Proof
- Sand background band, 2-column
- Left: H3 "Trusted by buyers in", country chips (UAE, Kuwait, Qatar, UK, Germany, France, Malaysia, Indonesia, USA, Canada), testimonial blockquote
- Right: square map placeholder (or real map embed)
Section 11: Lead Magnet
- Palm Green rounded panel (
rounded-2xl) - 2-column: copy left ("Download our 2026 wholesale catalogue & price list" + description), email capture form right
- On submit → calls
/api/catalogueroute → shows inline success "Check your inbox ✓" state - Form: email input +
[Get the catalogue]button
Section 12: Footer — (Footer component, already in layout)
Section 13: WhatsApp button — (WhatsAppButton component, already in layout)
EN/AR toggle behavior
-
LanguageContextwithlang: 'en' | 'ar'state - On toggle: flip
<html dir="rtl/ltr">, swap font to IBM Plex Sans Arabic, replacedata-i18ntext fromlib/i18n.tsmap - Mirror WhatsApp button position on RTL
Task 1.5 — Shop Page (app/shop/page.tsx)
From Shop.dc.html:
Page header block
- Title "Madinah's finest, delivered" (Fraunces H1)
- Subtitle + link "Need bulk quantities? Request a wholesale quote →" →
/wholesale/request-quote
FilterSidebar.tsx (sticky)
- Category buttons: All products / Whole dates / Gift boxes / Value-added
- Active state: sand background, weight 600
- Variety chips below (display, no filter logic needed in MVP)
- "All certified & documented" note at bottom
ProductGrid.tsx (3-column)
- Maps
data/products.tsfiltered by active category - Each ProductCard:
- White card, image placeholder, optional corner Badge (Best seller / New harvest)
- Eyebrow category label, Fraunces product name (links to
/products/[slug]), short note - Price (Fraunces,
SAR [price]) [Add to cart]primary button- Hover lift
- On add: pushes to CartContext, shows Toast "Ajwa Dates added to cart ✓"
Cart icon in Header
- Live count badge — reads
cartItems.reduce((sum, i) => sum + i.qty, 0)from CartContext
Task 1.6 — Product Detail Page (app/products/[slug]/page.tsx)
From Product.dc.html:
-
generateStaticParams()fromdata/products.tsslugs — pre-rendered at build - Breadcrumb: Home / Shop / [Product name]
- 2-column layout: Gallery | Info
Gallery.tsx
- Square main image placeholder
- 4 thumbnail placeholders; clicking sets active; active thumbnail has amber border
ProductInfo.tsx
- Badges (Best seller / In stock)
- Eyebrow (variety type), H1 product name
- Description paragraph
- Live price display (changes with selected grade)
- Grade/size selector buttons: 500g / 1kg / 2kg — selected = Date Brown fill, white text
- Quantity stepper:
−button, number input,+button; min 1 -
[Add to cart · SAR {qty × price}]— shows live total; on click → CartContext + Toast -
[Request a wholesale quote]secondary button →/wholesale/request-quote?product=[name] - Detail rows: Origin & grading / Certification / Shipping info
- Cert strip (HACCP · Halal · ISO 22000)
RelatedProducts.tsx
- "You may also like" — 3-up grid from other products
- Same card style as shop
Task 1.7 — Cart Page (app/cart/page.tsx)
From Cart.dc.html:
Empty state
- Centered card, 🛒 emoji, "Your cart is empty",
[Browse the shop]button →/shop
Populated state (reads from CartContext)
CartLineItem.tsx (per item)
- Image placeholder, product name, grade label
- Remove link
- Qty stepper (decrement below 1 → removes item)
- Line total:
SAR {price × qty}
OrderSummary.tsx (sticky)
- Subtotal:
Σ price × qty - Shipping: free if subtotal ≥ 250 SAR, else 25 SAR, 0 when empty
- Total = subtotal + shipping
- Format:
SAR {n.toLocaleString()} -
[Checkout securely]primary button → triggers "placed" success state, clears cart to[] - Success state: Palm Green card "Order placed ✓" with contact details note
- "Need an invoice / bulk order? Request a quote →" link
- Trust row: 🔒 Secure checkout · HACCP · Halal · Worldwide shipping
Note: Checkout in MVP shows a success state and clears cart. Real payment (Stripe) is Phase 3. Add a note on the cart page: "Complete your order via WhatsApp or email" as interim.
Task 1.8 — Wholesale Hub (app/wholesale/)
Wholesale landing page (/wholesale)
- Hero: H1 "Source from Madinah — the home of Ajwa", sub-headline,
[Request a Quote]CTA - 4 value props (same as homepage Section 8 but expanded)
- Product catalogue preview (4–6 variety cards) with
[Download Catalogue]→/wholesale/catalogue - Private label callout →
/wholesale/private-label - Certifications summary →
/certifications
Inquiry Form page (/wholesale/request-quote)
- Full InquiryForm component
- Fields (all from README spec):
- Company name (required)
- Contact name (required)
- Country (required, select dropdown — list of target markets first, then all countries)
- Email (required, validated)
- Phone / WhatsApp (required)
- Varieties of interest (multi-select checkboxes: Ajwa, Safawi, Sukkari, Mabroom, Ambari, Khudri, Value-added)
- Estimated volume (select: <1 MT / 1–5 MT / 5–10 MT / 10–20 MT / 1 container+)
- Pack format (checkboxes: Bulk, Retail-ready 500g, Private label, Gift boxes)
- Additional notes (textarea)
- Validation via Zod schema
- On submit: POST to
/api/inquiry→ sends formatted email toINQUIRY_EMAIL_TOvia Resend → show inline success
API route (/api/inquiry/route.ts)
- Parse and validate body
- Send email to business inbox with all form fields formatted
- Send auto-reply to submitter with catalogue PDF link + "we'll be in touch within 48h" message
- Return
{ success: true }or{ error: string }
Catalogue gate page (/wholesale/catalogue)
- Teaser content (variety thumbnails, "what's inside" bullets)
- CatalogueGate form: email + company name
- On submit: POST to
/api/catalogue→ sends email with download link + adds to list → show download button
API route (/api/catalogue/route.ts)
- Validate email
- Send email with catalogue PDF download link
- Return success
Private Label page (/wholesale/private-label)
- What we offer: pack formats, labelling, MOQs for private label
- 3-step process: Inquiry → Sample → Production
- CTA →
/wholesale/request-quote?type=private-label
Task 1.9 — Supporting Pages (MVP)
Our Story (/our-story)
- Hero section with image placeholder (Madinah / farms)
- Origin story: Madinah Al-Munawwarah, the Ajwa significance, climate
- Factory & process section: image placeholders for facility, packing, cold chain
- Values: transparency, provenance, the anti-fraud stance
- CTA back to Wholesale inquiry
Certifications (/certifications)
- Intro: "We believe buyers deserve proof, not promises"
- Certification cards: HACCP, Halal, ISO 22000 — each with logo placeholder, explanation, download link for PDF
- Placeholder cards for BRCGS / Organic / Fairtrade with "In progress" badge
- Per-shipment docs section: COO, Phytosanitary — what they are, how to request
- Anti-fraud statement block
Contact (/contact)
- Contact form (name, email, subject, message)
- WhatsApp number (click-to-chat)
- Email address
- Factory address: Madinah Al-Munawwarah, Saudi Arabia
- Social links
- Form submits to
/api/inquirywithtype: 'contact'
Task 1.10 — Performance, SEO & Deployment
SEO (built-in from day 1)
-
metadataexport on every page (title, description, OG tags) - Page titles follow pattern:
[Page] | Al Nuzha — Premium Dates from Madinah - Each product page gets
type: 'Product'JSON-LD schema - Root layout:
OrganizationJSON-LD schema -
robots.txtandsitemap.xmlviaapp/robots.tsandapp/sitemap.ts -
<html lang="en">(switches toaron toggle)
Performance
- All images:
next/imagewith properwidth,height,priorityon hero - Fonts: loaded via
next/font/google(no layout shift) - Code splitting is automatic with App Router
Deployment
- Push to GitHub → Vercel auto-deploys on every push to
main - Add
RESEND_API_KEYandINQUIRY_EMAIL_TOto Vercel environment variables - Connect
alnuzhadates.comdomain in Vercel dashboard - Verify SSL (automatic via Vercel)
- Add site to Google Search Console
- Add GA4 script via
app/layout.tsxornext/script
MVP Deliverables Checklist
- Homepage — all 13 sections pixel-faithful to design
- Shop page — product grid + filters + add-to-cart + toast
- Product detail — grade selector + qty stepper + add-to-cart + B2B path
- Cart — line items, order summary, shipping logic, checkout success state
- Wholesale hub — landing + inquiry form + catalogue gate + private-label page
- Our Story page
- Certifications page
- Contact page
- Working email delivery (inquiry + catalogue gate)
- Cart persists across pages and tabs (localStorage)
- EN/AR toggle functional on homepage
- Mobile responsive (all breakpoints)
- Floating WhatsApp button
- GA4 connected
- Deployed on Vercel at alnuzhadates.com
Phase 2 — Content & SEO Engine
Goal: Add the content pages that drive organic traffic and deepen credibility. Timeline: Weeks 5–10 after MVP launch Trigger: Start Phase 2 once B2B inquiries are coming in from Phase 1.
Task 2.1 — Individual Variety Pages
One dedicated SEO page per variety under /products/[slug] (already handled by the dynamic route from Phase 1, but now with full content):
| Page | Primary SEO target |
|---|---|
/products/ajwa-dates | "ajwa dates wholesale supplier", "buy ajwa dates online" |
/products/safawi-dates | "safawi dates wholesale", "buy safawi dates" |
/products/sukkari-dates | "sukkari dates wholesale", "sukkari dates supplier" |
/products/mabroom-dates | "mabroom dates wholesale" |
/products/ambari-dates | "ambari dates wholesale", "anbara dates" |
/products/khudri-dates | "khudri dates wholesale" |
Each page expands to include: full origin story for that variety, grade table (count/kg, moisture %, pack formats), spec sheet download, dual CTA.
Task 2.2 — Blog / Resources
- Blog index page
/blog— card grid of articles - MDX setup for blog posts (or simple
.tsdata + page template) - Write and publish 4 priority cornerstone articles:
- "Complete Guide to Ajwa Dates" — targets "ajwa dates" high volume
- "Madinah Date Varieties Explained" — internal link to all variety pages
- "How to Import Dates into the EU and UK" — B2B outreach asset
- "Date Paste as a Sugar Replacement" — food manufacturer audience
- Each article: internal links to relevant product page +
/wholesale/request-quoteCTA at end
Task 2.3 — Markets Pages
-
/markets— hub page with GCC, UK, Europe panels -
/markets/gcc— pack format norms for GCC, varieties popular, contact CTA -
/markets/uk— Fairtrade note, ethnic distributor info, Halal certs -
/markets/europe— ISO 22000, pitted preference in Germany, organic interest
Task 2.4 — Full Arabic Localisation
- Switch from simple
data-i18nmap tonext-intllibrary - Create
/messages/en.jsonand/messages/ar.json - All page text localised
- RTL layout:
dir="rtl"on<html>, WhatsApp button mirrors, nav reorders - Arabic product names and descriptions
- Test on a real Arabic-language device
Task 2.5 — Email Newsletter System
- Resend Audiences or integrate with Mailchimp/Brevo free tier
- Segment: B2B vs B2C (set on catalogue download vs DTC purchase)
- Welcome sequence: 2-email series after catalogue download
- Monthly newsletter template
Phase 3 — Payments & Growth
Goal: Real DTC checkout + advanced B2B features. Timeline: Months 3–6 after launch Trigger: Start when DTC orders are coming in consistently.
Task 3.1 — Stripe Payment Integration
- Install
stripeand@stripe/stripe-js -
/api/checkout/route.ts— creates Stripe Checkout Session from cart items - Cart
[Checkout securely]redirects to Stripe-hosted checkout - Stripe webhook
/api/webhooks/stripe/route.ts— listens forcheckout.session.completed, sends order confirmation email - Order confirmation page
/order-confirmation - Product prices stored with Stripe Price IDs in
data/products.ts
Task 3.2 — B2B Customer Portal
- Authentication: NextAuth.js with email magic-link (no password)
- B2B account application form → manual approval flow → email notification
- Approved accounts see wholesale pricing tier in product pages and cart
- Wholesale pricing stored as a separate price field in product data
- Account dashboard: past orders, repeat-order button, invoice download (PDF)
Task 3.3 — Database (if needed)
- Supabase free tier (PostgreSQL, 500 MB)
- Tables:
orders,inquiry_leads,catalogue_downloads,b2b_accounts - Migrate from static
data/products.tsto Supabaseproductstable (only if dynamic updates are needed — otherwise keep static)
Task 3.4 — Subscriptions (Date Box)
- Stripe Billing for recurring subscriptions
- "Monthly Ajwa box" product with subscription option
- Customer manages subscription from account portal
Task 3.5 — SEO & Performance Audit
- Lighthouse audit — target 90+ on Performance, Accessibility, SEO
- Core Web Vitals: LCP < 2.5s, CLS < 0.1, FID/INP < 200ms
- Image optimisation: real photography replaces all placeholders with WebP +
srcset - Add
FAQPageschema to certifications and variety pages - Google Shopping feed from product data
Cost Summary
| Item | Monthly | Notes |
|---|---|---|
| Vercel (hosting) | $0 | Free tier: 100 GB bandwidth, plenty for MVP |
| Resend (email) | $0 | Free: 100 emails/day, 3,000/month |
| Domain | ~$1 | ~$12/year for .com |
| Supabase (DB, Phase 3) | $0 | Free: 500 MB, 50,000 MAU |
| Stripe (payments, Phase 3) | 2.9% + 30¢ per txn | No monthly fee |
| Total (Phases 1–2) | ~$1/month | Just the domain prorated |
| Total (Phase 3) | ~$1/month + txn fees | Scales only with revenue |
Key Technical Decisions
Why Next.js App Router (not Pages Router)?
Static generation (generateStaticParams) for product pages = fast SEO-optimised pages served from Vercel CDN. API routes for form handling without a separate backend.
Why static data/products.ts for MVP?
Zero database setup time, zero cost, instant. Products don't change daily. Migrating to a database later is straightforward when dynamic updates are needed.
Why Resend over SendGrid or Mailgun? 100 emails/day free, simple API, excellent Next.js integration, reliable deliverability. Sufficient for MVP lead volume.
Why localStorage for cart (not server-side)? Exact specification from the design README. No auth required = faster to build and use. Correct for a guest checkout MVP. Replace with server-side sessions in Phase 3 when auth is added.
Why not a headless CMS for Phase 1? Unnecessary complexity for MVP. Static data is faster to ship, easier to maintain, and free. Add a CMS (e.g., Sanity free tier) in Phase 2 if the client needs to self-manage content.