Skip to content

Repository files navigation

Hygenc Covers Logo

Hygenc Covers โ€” Intimate Hygiene E-Commerce Platform

Premium toilet seat cover products for Sri Lanka โ€” built for retail, hospitality & B2B


React Vite TailwindCSS React Router Supabase Resend Vercel

License: MIT Node WhatsApp


Table of Contents


Overview

Hygenc Covers is a high-conversion, mobile-first e-commerce platform for intimate hygiene toilet seat covers targeting the Sri Lankan market. It began as a client-only WhatsApp storefront and has grown into a full-stack application with a Supabase backend, a serverless order-email API, customer accounts, and a complete admin console.

The platform serves three customer segments:

Segment Product Channel
Retail / Personal Single Use Pack Cart ยท COD checkout ยท WhatsApp
Travellers Travel Pack (Waterproof) Cart ยท COD checkout ยท WhatsApp
Hotels / Corporates Enterprise Bulk Pack B2B Enquiry Form

The storefront is fully internationalised across English, Sinhala (เทƒเท’เถ‚เท„เถฝ), and Tamil (เฎคเฎฎเฎฟเฎดเฏ). Customers can check out three ways: a cash-on-delivery (COD) order persisted to Supabase with an emailed confirmation, a WhatsApp deep-link order, or a B2B enquiry for bulk buyers.


What's New

The platform has expanded well beyond the original WhatsApp-only storefront. Recent additions:

  • ๐Ÿ›’ COD checkout + order persistence โ€” orders saved to Supabase via a create_checkout_order RPC with an idempotent, offline-tolerant fallback path.
  • ๐Ÿ“ง Transactional order emails โ€” a serverless function renders a branded HTML receipt and sends it through Resend (with a verified-sender fallback).
  • ๐Ÿ” Customer accounts โ€” sign-up / sign-in, profile, and order history backed by Supabase Auth.
  • ๐Ÿ›ก๏ธ Admin console โ€” dashboard, orders, products, customers, inquiries, newsletter, analytics, quiz, and settings, behind an admin auth guard.
  • ๐Ÿ“Š Analytics & tracking โ€” page views, conversions, and custom events stored in Supabase and surfaced in the admin analytics page.
  • ๐Ÿงช Product finder quiz โ€” guided recommendation flow with responses stored for insight.
  • ๐Ÿ—‚๏ธ Product catalog & bundles โ€” a data-driven catalog (6 products) with bundle rules (add-ons require a base bundle).
  • ๐Ÿ“„ Policy pages โ€” privacy, terms, refund, and shipping policy pages wired into the footer.
  • ๐Ÿ“ค Admin export โ€” orders/customers exportable to CSV/HTML.
  • ๐Ÿ”Ž SEO โ€” per-page metadata via a reusable SEO component.

Tech Stack

Layer Technology
UI Framework React 19
Build Tool Vite 8
Styling Tailwind CSS 4 (@tailwindcss/vite JIT)
Routing React Router DOM 7
Icons Font Awesome 7 (SVG Core) + Lucide React
Animation Framer Motion + CSS keyframes / IntersectionObserver
Charts Recharts (admin analytics)
Dates date-fns
Client State React Context API (Cart ยท Lang ยท CustomerAuth ยท AdminAuth)
Backend (BaaS) Supabase โ€” Postgres, Auth, Row-Level Security, RPC functions
Serverless API Vercel Functions (/api/*, Node handler)
Transactional Email Resend (production) ยท Nodemailer (local tooling)
Hosting / CI-CD Vercel (static SPA + serverless functions)
Checkout Channels COD form ยท WhatsApp deep link (wa.me) ยท B2B enquiry
Browser Persistence localStorage / sessionStorage
Linting ESLint + react-hooks + react-refresh

System Architecture

graph TB
    subgraph Browser["๐ŸŒ Client (Browser SPA)"]
        direction TB
        subgraph Providers["Context Providers"]
            LC[LangContext<br/>EN ยท SI ยท TA]
            CC[CartContext<br/>localStorage]
            CAC[CustomerAuthContext]
            AAC[AdminAuthContext]
        end
        subgraph Surfaces["UI Surfaces"]
            Store[Storefront Pages]
            Acct[Customer Account]
            Admin[Admin Console]
            Quiz[Product Quiz]
        end
        Lib[lib/ ยท database.js ยท supabase.js ยท orderEmail.js ยท adminExport.js]
        AT[AnalyticsTracker]
        Providers --> Surfaces
        Surfaces --> Lib
        Surfaces --> AT
    end

    subgraph Vercel["โ–ฒ Vercel Edge / Functions"]
        SPA[Static SPA<br/>intimate-react/dist]
        FN["/api/send-order-email<br/>(serverless handler)"]
    end

    subgraph Supabase["๐ŸŸข Supabase"]
        DB[(Postgres<br/>+ RLS)]
        AUTH[Supabase Auth]
        RPC[RPC: create_checkout_order<br/>save_customer_profile ยท delete_order_admin]
    end

    subgraph Ext["External Services"]
        RESEND[Resend Email API]
        WA[WhatsApp<br/>wa.me]
        GF[Google Fonts]
    end

    Browser -->|served by| SPA
    Lib -->|"REST ยท Auth ยท RPC"| DB
    Lib --> AUTH
    Lib --> RPC
    AT -->|events| DB
    Lib -->|"POST order"| FN
    FN -->|"RPC create_checkout_order"| RPC
    FN -->|"send receipt"| RESEND
    Surfaces -->|"deep link"| WA
    Browser --> GF
Loading

Module Map

graph TD
    main[main.jsx] --> App

    App --> Ctx[Context Providers]
    App --> GlobalUI[Global UI Layer]
    App --> Router[React Router]
    App --> Track[AnalyticsTracker]

    Ctx --> Lang[LangContext]
    Ctx --> Cart[CartContext]
    Ctx --> CustAuth[CustomerAuthContext]
    Ctx --> AdminAuth[AdminAuthContext]

    GlobalUI --> AnnouncementBar
    GlobalUI --> WhatsAppButton
    GlobalUI --> DiscountPopup
    GlobalUI --> LivePurchaseNotification
    GlobalUI --> CartDrawer
    GlobalUI --> StickyMobileCTA
    GlobalUI --> SEO
    GlobalUI --> ScrollToTop

    Router --> Storefront
    Router --> Customer
    Router --> AdminRouter

    Storefront --> Home & Products & ProductDetail[Product1..6] & Compare
    Storefront --> About & Benefits & Blog & FAQ & B2B & Referral
    Storefront --> Delivery & Contact & Quiz & PolicyPages & OrderTemplate

    Customer --> CustomerAuth & CustomerAccount

    AdminRouter --> AdminLogin
    AdminRouter --> AdminLayout
    AdminLayout --> AdminDashboard & AdminOrders & AdminProducts & AdminCustomers
    AdminLayout --> AdminInquiries & AdminNewsletter & AdminAnalytics & AdminQuiz & AdminSettings

    ProductDetail --> Catalog[data/catalog.js]
    ProductDetail --> ProductContent[data/productContent.js]
    AdminOrders --> adminExport[lib/adminExport.js]
    CartDrawer --> orderEmail[lib/orderEmail.js]
    orderEmail --> API["/api/send-order-email"]
    AdminDashboard --> database[lib/database.js]
    database --> supabase[lib/supabase.js]
Loading

Routing

The app splits at the top level into an admin tree (/admin/*, fully isolated โ€” no storefront overlays) and the customer tree (/*). Source of truth: App.jsx.

flowchart LR
    root["/"] -->|redirect| splash["/splash<br/>brand intro"]
    splash --> home["/home"]

    subgraph Store["๐Ÿ›๏ธ Storefront (/*)"]
        home --> products["/products"]
        products --> pd["/products/1 โ€ฆ /products/6"]
        products --> compare["/compare"]
        home --> quiz["/quiz"]
        home --> about["/about"] & benefits["/benefits"] & blog["/blog"] & faq["/faq"]
        home --> b2b["/b2b"] & referral["/referral"] & delivery["/delivery"] & contact["/contact"]
        home --> policies["/privacy-policy ยท /refund-policy ยท /terms-and-conditions"]
        home --> order["/order"]
    end

    subgraph Cust["๐Ÿ‘ค Customer (/*)"]
        login["/login<br/>sign in / up"]
        account["/account<br/>profile + orders"]
    end

    subgraph Adm["๐Ÿ›ก๏ธ Admin (/admin/*)"]
        adminidx["/admin<br/>(AdminLogin ยท index)"]
        adminidx --> dash["/admin/dashboard"]
        dash --> aorders["/admin/orders"]
        dash --> aproducts["/admin/products"]
        dash --> acustomers["/admin/customers"]
        dash --> ainquiries["/admin/inquiries"]
        dash --> anewsletter["/admin/newsletter"]
        dash --> aanalytics["/admin/analytics"]
        dash --> aquiz["/admin/quiz"]
        dash --> asettings["/admin/settings"]
    end

    home -.-> login
    login -.-> account
    wildcard["unknown path"] -->|redirect| splash
Loading

Data Model

Eight tables back the platform. customer_profiles and orders.customer_id reference Supabase's auth.users. The authoritative schema (columns, RLS policies, RPCs, seed data) lives in supabase-schema.sql.

erDiagram
    AUTH_USERS ||--o| CUSTOMER_PROFILES : "has profile"
    AUTH_USERS ||--o{ ORDERS : "places (optional)"
    ORDERS ||--|{ ORDER_ITEMS : contains
    PRODUCTS ||--o{ ORDER_ITEMS : "referenced by"

    AUTH_USERS {
        uuid id PK
        text email
        jsonb app_metadata "role=admin gate"
    }
    CUSTOMER_PROFILES {
        uuid id PK "= auth.users.id"
        text email
        text name
        text phone
        text address
        text city
        text preferred_payment_method
    }
    ORDERS {
        uuid id PK
        uuid customer_id FK "nullable"
        text order_ref UK
        text customer_name
        text customer_email
        text customer_phone
        text address
        text city
        numeric total
        text status
        text payment_method
        text discount_code
        text note
        timestamptz created_at
    }
    ORDER_ITEMS {
        uuid id PK
        uuid order_id FK
        uuid product_id FK "nullable"
        text product_name
        int quantity
        numeric price
    }
    PRODUCTS {
        uuid id PK
        text slug UK
        text name
        numeric price
        numeric cost
        int stock
        int sold
        numeric rating
        text category
        bool active
    }
    INQUIRIES {
        uuid id PK
        text company
        text name
        text email
        text message
        text status
    }
    NEWSLETTER_SUBSCRIBERS {
        uuid id PK
        text email UK
        bool active
    }
    QUIZ_RESPONSES {
        uuid id PK
        text name
        text phone
        jsonb answers
        text result
        int score
    }
    SITE_EVENTS {
        uuid id PK
        text event_type
        text path
        text label
        jsonb metadata
        timestamptz created_at
    }
Loading

RLS, RPC & Realtime:

  • All eight tables enable Row-Level Security. Public can insert orders, inquiries, newsletter, quiz, and site events; reads of sensitive data require the admin role (checked via auth.jwt() -> 'app_metadata' ->> 'role'). Customers can read only their own profile and orders.
  • SECURITY DEFINER RPCs: create_checkout_order (atomic order + items insert, idempotent on order_ref), save_customer_profile (upsert), and delete_order_admin (admin-gated cascade delete).
  • Tables are added to the supabase_realtime publication, so the admin dashboard and product feed update live via postgres_changes subscriptions.

Checkout & Order Lifecycle

sequenceDiagram
    actor User
    participant Cart as CartDrawer
    participant OE as lib/orderEmail.js
    participant FN as /api/send-order-email
    participant SB as Supabase RPC
    participant RS as Resend
    participant WA as WhatsApp

    User->>Cart: Review cart (free-delivery progress)

    alt COD checkout
        User->>Cart: Submit name / phone / address / city
        Note over Cart: lib/database.js captureLocalOrder()<br/>โ†’ localStorage pending queue (offline-safe)
        Cart->>SB: rpc create_checkout_order (client, idempotent)
        Cart->>OE: build order + items (order_ref)
        OE->>FN: POST { order, items } (+ X-Customer-Access-Token)
        FN->>SB: rpc create_checkout_order (server, idempotent)
        alt RPC ok / duplicate (23505)
            SB-->>FN: saved (no-op on duplicate order_ref)
        else RPC missing
            FN->>SB: direct insert orders + order_items
        end
        FN->>RS: send branded HTML receipt
        RS-->>FN: message id
        FN-->>OE: { ok, id, order_saved, order_save_via }
        OE-->>User: confirmation + /order template
    else WhatsApp order
        User->>Cart: "Order via WhatsApp"
        Cart->>WA: wa.me?text=<encoded order>
        WA-->>User: pre-filled chat
    end

    Note over Cart,FN: Both client and server save via the same idempotent RPC<br/>(keyed on order_ref) โ€” a failed leg never double-charges or duplicates
Loading

Order Email Pipeline

flowchart TD
    A[POST /api/send-order-email] --> B{Method = POST?}
    B -->|No| Z1[405 Method Not Allowed]
    B -->|Yes| C{RESEND_API_KEY set?}
    C -->|No| Z2[500 Email not configured]
    C -->|Yes| D{order_ref + email + items?}
    D -->|No| Z3[400 Missing fields]
    D -->|Yes| E[Start Supabase save - async]

    E --> F[Build branded HTML receipt]
    F --> G[Resend: send from ORDER_EMAIL_FROM]
    G --> H{Sender/domain error?}
    H -->|Yes| I[Retry from onboarding@resend.dev]
    H -->|No| J[Use response]
    I --> J
    J --> K{Email sent?}
    K -->|No| Z4[Return Resend error status]
    K -->|Yes| L[await Supabase save result]
    L --> M[200 ok ยท id ยท order_saved ยท order_save_via]
Loading

Server-side environment knobs: ORDER_EMAIL_FROM, ORDER_EMAIL_REPLY_TO, ORDER_EMAIL_BCC, plus SITE_URL / VERCEL_URL for the logo base URL.


Authentication

Two independent auth contexts, both backed by Supabase Auth, guard their respective areas.

flowchart TB
    subgraph Customer["๐Ÿ‘ค Customer Auth ยท CustomerAuthContext"]
        c1["/login โ€” sign up or sign in"] --> c2["Supabase Auth session"]
        c2 --> c3["/account โ€” profile + order history"]
        c2 -->|access token| c4["X-Customer-Access-Token<br/>sent to order API"]
    end

    subgraph Admin["๐Ÿ›ก๏ธ Admin Auth ยท AdminAuthContext"]
        a1["/admin โ€” AdminLogin"] --> a2["Supabase session +<br/>app_metadata.role = admin"]
        a2 -->|authorised| a3["AdminLayout + pages"]
        a2 -->|denied| a1
        a3 --> a4["admin-gated RPC / RLS<br/>e.g. delete_order_admin"]
    end
Loading

Admin Panel

A protected console (/admin/*) for operating the store. Wrapped by AdminLayout and guarded by AdminAuthContext.

graph LR
    Login[AdminLogin] --> Layout[AdminLayout]
    Layout --> D[Dashboard<br/>today's views + conversions]
    Layout --> O[Orders<br/>view ยท delete ยท export]
    Layout --> P[Products<br/>catalog management]
    Layout --> C[Customers]
    Layout --> I[Inquiries<br/>B2B + contact]
    Layout --> N[Newsletter<br/>subscribers]
    Layout --> A[Analytics<br/>events + funnels]
    Layout --> Q[Quiz<br/>responses]
    Layout --> S[Settings]
Loading
Page Purpose
Dashboard KPIs โ€” today's page views, conversions, recent orders
Orders List, inspect, delete (admin-checked RPC), CSV/HTML export
Products Manage the product catalog
Customers Registered customer accounts
Inquiries B2B + contact-form submissions
Newsletter Email subscribers
Analytics Event stream, page views, conversion insight
Quiz Product-finder quiz responses
Settings Store configuration

Analytics & Tracking

flowchart LR
    subgraph Client
        Route[Route change] --> AT[AnalyticsTracker]
        UI[CTA clicks ยท add-to-cart ยท checkout] --> AT
    end
    AT -->|"trackSiteEvent()"| DB[(site_events)]
    DB -->|realtime + queries| Dash[Admin ยท Analytics + Dashboard]
    Dash --> Insight[Views ยท conversions ยท funnels]
Loading

AnalyticsTracker records page views on navigation and custom conversion events via trackSiteEvent() (lib/database.js), writing to the site_events table (event_type, path, label, metadata). The admin dashboard aggregates today's views and conversions, updating live through realtime subscriptions.


Internationalisation (i18n)

graph LR
    LangContext -->|"t = translations[lang]"| EN[English ๐Ÿ‡ฌ๐Ÿ‡ง]
    LangContext -->|"t = translations[lang]"| SI[เทƒเท’เถ‚เท„เถฝ ๐Ÿ‡ฑ๐Ÿ‡ฐ]
    LangContext -->|"t = translations[lang]"| TA[เฎคเฎฎเฎฟเฎดเฏ ๐Ÿ‡ฎ๐Ÿ‡ณ]

    subgraph Keys["Translation Key Groups"]
        direction TB
        NAV[Navigation]
        HERO[Hero]
        PRODUCTS[Products]
        CART[Cart & checkout]
        TRUST[Trust badges]
        FAQ[FAQ Q&A]
        NEWSLETTER[Newsletter]
        ANNOUNCE[Announcement bar]
        NOTIFY[Live notifications]
    end

    LangContext --> Keys
Loading

Language is toggled via LangToggle in the Navbar. English is the default audience language.


Cart State Management

stateDiagram-v2
    [*] --> Empty : App load / localStorage empty

    Empty --> HasItems : add(product)
    HasItems --> HasItems : updateQty(id, delta)
    HasItems --> HasItems : remove(id)
    HasItems --> Empty : clear()

    HasItems --> DrawerOpen : open()
    DrawerOpen --> Checkout : COD checkout
    DrawerOpen --> WhatsApp : Order via WhatsApp
    DrawerOpen --> HasItems : close()

    Checkout --> Saved : order persisted + emailed
    Saved --> Empty : clear() after success
    WhatsApp --> [*] : deep link opens

    note right of HasItems
        Persisted to localStorage
        Subtotal computed on render
        Free-delivery progress at LKR 3,000
        Bundle rule: add-ons require a base bundle
    end note
Loading

Features

Customer-Facing

Feature Description
Splash & Premium Hero Animated brand intro, gradient/blob hero, trust chips, hero stats
Product Catalog (ร—6) Data-driven catalog with bundles; add-ons require a base bundle
COD Checkout Order form โ†’ Supabase persistence โ†’ emailed receipt
WhatsApp Checkout Pre-filled order deep link, zero payment infra
Product Quiz Guided product-finder with stored responses
Customer Accounts Sign-up/in, profile, order history (Supabase Auth)
Live Purchase Notifications Cycling social-proof toasts
Announcement Bar 24h countdown, dismissible, gradient shimmer
Before/After Slider Drag/touch hygiene-contrast reveal
Scroll Reveals & CountUp IntersectionObserver animations + animated stats
Cart Drawer Qty controls, free-delivery progress, dual checkout
Sticky Mobile CTA Shop + WhatsApp bar after scroll
Discount Popup & Newsletter Timed offer + email capture
FAQ Accordion Pure-CSS grid-rows animation
B2B & Referral Bulk enquiry + referral entry points
Policy Pages Privacy / terms / refund / shipping
SEO Per-page metadata via SEO component

Backend & Operations

Feature Description
Order Persistence create_checkout_order RPC with idempotent + direct-insert fallback
Transactional Email Branded HTML receipts via Resend with sender fallback
Admin Console Full CRUD/insight surface behind admin auth
Realtime Admin dashboard + product feed update live via postgres_changes
Offline-safe orders Local pending-order queue with idempotent cloud sync
Analytics Page views, conversions, custom events in site_events
Export Orders/customers โ†’ CSV/HTML
RLS & RPC Least-privilege client; admin-checked destructive ops

Developer Experience

Feature Description
Vite HMR Sub-100ms hot module replacement
Tailwind v4 Zero-config JIT via @tailwindcss/vite
Custom animations 12+ keyframes; prefers-reduced-motion honoured
Graceful degradation App runs even when Supabase/Resend env vars are absent
ESLint react-hooks + react-refresh rules

Project Structure

INTIMATE_HYGIENE/
โ”œโ”€โ”€ api/
โ”‚   โ””โ”€โ”€ send-order-email.js        # Vercel serverless: save order (RPC) + Resend receipt
โ”œโ”€โ”€ supabase/
โ”‚   โ””โ”€โ”€ email-templates/           # Supabase auth email templates
โ”œโ”€โ”€ supabase-schema.sql            # Authoritative DB schema (tables, RLS, RPC)
โ”œโ”€โ”€ vercel.json                    # Build + SPA rewrite + serverless config
โ”œโ”€โ”€ package.json                   # Root scripts & backend deps (supabase-js, resend, nodemailer)
โ”‚
โ””โ”€โ”€ intimate-react/                # Frontend SPA
    โ”œโ”€โ”€ public/                    # Logos, favicons, product images
    โ”œโ”€โ”€ index.html
    โ”œโ”€โ”€ vite.config.js             # Vite + Tailwind + dev API middleware
    โ”œโ”€โ”€ eslint.config.js
    โ””โ”€โ”€ src/
        โ”œโ”€โ”€ main.jsx               # Entry โ€” mounts <App/>
        โ”œโ”€โ”€ App.jsx                # Providers + global UI + router
        โ”œโ”€โ”€ index.css              # Global styles & keyframes
        โ”‚
        โ”œโ”€โ”€ context/
        โ”‚   โ”œโ”€โ”€ LangContext.jsx           # EN/SI/TA translations
        โ”‚   โ”œโ”€โ”€ CartContext.jsx           # Cart state + bundle rules
        โ”‚   โ”œโ”€โ”€ CustomerAuthContext.jsx   # Customer Supabase Auth
        โ”‚   โ””โ”€โ”€ AdminAuthContext.jsx      # Admin auth + guard
        โ”‚
        โ”œโ”€โ”€ lib/
        โ”‚   โ”œโ”€โ”€ supabase.js               # Supabase client (env-guarded)
        โ”‚   โ”œโ”€โ”€ database.js               # Data access (orders, analytics, etc.)
        โ”‚   โ”œโ”€โ”€ orderEmail.js             # Client โ†’ /api/send-order-email
        โ”‚   โ””โ”€โ”€ adminExport.js            # CSV/HTML export helpers
        โ”‚
        โ”œโ”€โ”€ data/
        โ”‚   โ”œโ”€โ”€ catalog.js                # Product catalog + bundle definitions
        โ”‚   โ””โ”€โ”€ productContent.js         # Per-product marketing content
        โ”‚
        โ”œโ”€โ”€ hooks/
        โ”‚   โ””โ”€โ”€ useInView.js              # IntersectionObserver hook
        โ”‚
        โ”œโ”€โ”€ components/                   # Navbar, Footer, CartDrawer, SEO,
        โ”‚   โ”‚                             # AnalyticsTracker, TrustBadges, Reveal,
        โ”‚   โ”‚                             # BeforeAfterSlider, CountUp, LangToggle, โ€ฆ
        โ”‚   โ””โ”€โ”€ ...
        โ”‚
        โ”œโ”€โ”€ pages/
        โ”‚   โ”œโ”€โ”€ Splash ยท Home ยท Products ยท Product1..6 ยท Compare
        โ”‚   โ”œโ”€โ”€ About ยท Benefits ยท Blog ยท FAQ ยท B2B ยท Referral ยท Delivery ยท Contact
        โ”‚   โ”œโ”€โ”€ Quiz ยท PolicyPages ยท OrderTemplate
        โ”‚   โ””โ”€โ”€ CustomerAuth ยท CustomerAccount
        โ”‚
        โ””โ”€โ”€ admin/
            โ”œโ”€โ”€ AdminLayout.jsx ยท AdminLogin.jsx
            โ””โ”€โ”€ pages/
                โ”œโ”€โ”€ AdminDashboard ยท AdminOrders ยท AdminProducts ยท AdminCustomers
                โ”œโ”€โ”€ AdminInquiries ยท AdminNewsletter ยท AdminAnalytics
                โ””โ”€โ”€ AdminQuiz ยท AdminSettings

Getting Started

Prerequisites

  • Node.js โ‰ฅ 18
  • npm โ‰ฅ 9
  • (Optional for full backend) A Supabase project + a Resend API key

Installation

git clone https://github.com/your-org/INTIMATE_HYGIENE.git
cd INTIMATE_HYGIENE/intimate-react
npm install

Development

# From intimate-react/
npm run dev
# โ†’ http://localhost:5173/

Vite mounts the order-email API in dev via a custom middleware plugin (orderEmailDevApi in vite.config.js) that runs the very same api/send-order-email.js handler used in production โ€” so POST /api/send-order-email works locally with no separate server process.

The frontend runs even without Supabase/Resend configured โ€” lib/supabase.js is env-guarded (isSupabaseConfigured) and the app degrades gracefully: orders fall back to a local pending queue + WhatsApp, and analytics/admin features stay dormant. Configure the env vars below to enable the full stack.

Production Build

# From intimate-react/
npm run build        # โ†’ intimate-react/dist
npm run preview      # serve the production build locally

Environment & Configuration

Create a .env.local at the repo root (server/serverless) and the matching VITE_-prefixed values for the client.

Client (intimate-react, exposed to the browser)

Variable Purpose
VITE_SUPABASE_URL Supabase project URL
VITE_SUPABASE_PUBLISHABLE_KEY Supabase anon/publishable key
VITE_ORDER_EMAIL_API_URL (optional) base URL for the order-email API (defaults to same origin)

Serverless / API (/api/send-order-email)

Variable Purpose
RESEND_API_KEY Required โ€” Resend API key for sending receipts
SUPABASE_URL (or VITE_SUPABASE_URL) Supabase URL for server-side order save
SUPABASE_SERVICE_ROLE_KEY (or anon/publishable) Key used by the function for RPC/insert
ORDER_EMAIL_FROM Verified sender (falls back to onboarding@resend.dev)
ORDER_EMAIL_REPLY_TO Reply-to address
ORDER_EMAIL_BCC BCC for internal order copies
SITE_URL / VERCEL_URL Base URL used for the email logo

Serverless / API (/api/send-order-status-email)

Sends the customer a branded status update whenever an admin changes an order's status in the dashboard (pending โ†’ processing โ†’ shipped โ†’ delivered, plus cancelled). Reuses RESEND_API_KEY, ORDER_EMAIL_FROM, ORDER_EMAIL_REPLY_TO and SITE_URL.

Variable Purpose
ORDER_STATUS_EMAIL_BCC (optional) BCC for internal copies of status updates โ€” unset means the customer alone is emailed

Storefront constants (in code)

Setting Location
WhatsApp number whatsappMsg strings + CartDrawer.jsx / WhatsAppButton.jsx / StickyMobileCTA.jsx
Free-delivery threshold (LKR 3,000) DeliveryProgressBar.jsx / CartDrawer.jsx
Product catalog & bundles src/data/catalog.js

Deployment

flowchart LR
    Dev[git push main] --> Vercel
    subgraph Vercel["โ–ฒ Vercel"]
        Build["buildCommand:<br/>cd intimate-react && npm install && npm run build"]
        Build --> Static[Static SPA<br/>intimate-react/dist]
        Func[Serverless<br/>/api/send-order-email]
    end
    Static -->|"rewrite all routes โ†’ /index.html"| Client[Browser]
    Client --> Func
    Func --> SB[(Supabase)]
    Func --> RS[Resend]
Loading

Deployment is driven by vercel.json:

{
  "buildCommand": "cd intimate-react && npm install && npm run build",
  "outputDirectory": "intimate-react/dist",
  "rewrites": [
    { "source": "/(.*)", "destination": "/index.html" }
  ]
}
  • Files in api/ are deployed as Vercel serverless functions (/api/send-order-email, /api/send-order-status-email).
  • The SPA rewrite ensures client-side routes (e.g. /admin/orders, /products/3) resolve on refresh/direct visit.
  • Set all environment variables in the Vercel project settings.
  • Run supabase-schema.sql against your Supabase project to provision tables, RLS, and RPCs.

Scripts

All scripts run from intimate-react/:

Script Description
npm run dev Vite dev server + dev order-email API, HMR (:5173)
npm run build Production bundle โ†’ dist/
npm run preview Serve the production build locally
npm run lint Run ESLint

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/your-feature
  3. Commit using conventional commits: git commit -m "feat: add X"
  4. Push: git push origin feat/your-feature
  5. Open a Pull Request against main

Hygenc Covers ยท Made with โค๏ธ for Sri Lanka

WhatsApp ยท Intimate Hygiene Enterprises

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages