Ayurvedic clinic · online dispensary

Sri Ayurvedic Vaidya Mitra

An Ayurvedic clinic and online dispensary platform — catalogue, consultation booking, manual UPI payments and an admin back office — built as a single Next.js application.

Client
Sri Ayurvedic Vaidya Mitra, Srirangam, Trichy
Status
Deployed
sriayurvedicvaidyamitra.com
Sri Ayurvedic Vaidya Mitra storefront homepage with product carousel and category navigation
369
Products live
Migrated from WordPress
573
Product images
Moved into storage
22,497
Lines of code
Single application
47
API endpoints
Route handlers
9
SQL migrations
19 tables, RLS on all
36
Automated checks
Against real Postgres
Built withNext.js 16React 19TypeScriptTailwind CSS 4Supabase PostgresZodResendReact Hook FormZustandsharp
01

Overview

Two jobs, one system.

Sri Ayurvedic Vaidya Mitra is a registered Ayurvedic clinic and dispensary in Srirangam, Trichy, founded by Mrs. T. Mari Sri, with consultations given by Dr. R. Balamurgan, BAMS. The practice needed one system to do two jobs that are usually separate: sell classical Ayurvedic medicines online, and take bookings for clinical consultation.

The result is a single Next.js application backed by Supabase for database, file storage and authentication, with Resend for transactional email. It carries a 369-product catalogue imported from the practice's previous WordPress site, four consultation offerings, a manual UPI payment flow with admin verification, a gated content library, and an admin back office covering every one of those.

Payments without a gateway

The customer pays by UPI to the shop's own VPA and submits the transaction reference; an administrator verifies it against the bank statement, which is the moment stock is reserved. This was a deliberate constraint of the brief — no gateway fees, no settlement delay — and it shapes the order flow throughout.
sriayurvedicvaidyamitra.com/about
About page introducing the practice, its founder and consulting physician
02

Scope

What was delivered.

Storefront

Delivered

Catalogue with search, faceted filtering, category, brand and concern browsing; product pages; cart and wishlist that survive sign-in; three-layer header with mega menus.

Accounts

Delivered

Sign-up with name, mobile, email and password, verified by emailed one-time code. Sign-in by password or code, password recovery, and a customer account area.

Orders & payment

Delivered

Server-priced checkout, UPI QR generation, transaction-reference submission with optional screenshot, admin verification, stock reservation and status tracking.

Consultations

Delivered

Four offerings, booking with a seven-day lead time, payment before confirmation, and admin verification that confirms the slot in one transaction.

Free library

Delivered

Articles, books and videos with type filtering. Metadata is public; the files themselves open only for signed-in readers.

Admin back office

Delivered

Products, taxonomy, orders, bookings, articles, users and settings, plus a bulk CSV/XLSX importer that migrates external product images into storage.

Product page
Product detail page with pricing, description and add to cart
Catalogue — 369 products across categories, brands and concerns
Customer account
Customer account area showing orders and profile details
Accounts — sign-up verified by emailed one-time code
03

Orders & payment

Pay by UPI, verified by hand.

Checkout prices every line on the server, generates a UPI QR for the shop's own VPA, and takes the transaction reference back from the customer with an optional screenshot. Nothing is reserved until an administrator matches that reference against the bank statement.

Checkout — payment step
Checkout payment step with UPI QR code, order summary and transaction reference form

The live UPI ID and QR are masked in this screenshot.

04

Consultations

Booking, with the clinic's rules enforced.

Four consultation offerings, a seven-day booking lead time, and payment before confirmation. The lead time lives in the database rather than the form, because the booking endpoint is reachable without the interface.

Consultations
Consultation offerings page listing the four available consultation types
Four offerings, each priced and bookable
Booking form
Consultation booking form with preferred date, time slot and concern description
Seven-day lead time surfaced in the form, enforced in SQL
05

Admin back office

Where the shop is actually run.

One dashboard covers everything that needs attention: products, taxonomy, orders, bookings, articles, users and settings. Stock alerts, pending UPI verifications and unconfirmed consultations surface on the landing screen rather than waiting to be found.

Admin — dashboard
Admin dashboard showing product, order, revenue and booking counters with recent orders and stock alerts
Counters for pending payments, low stock and unconfirmed bookings — the queues an administrator works through
Admin — products
Admin product management table with search, stock filter, SKU, category, price and featured flags
All 369 products, searchable by name, SKU or brand — with the bulk CSV/XLSX importer that migrated them in
06

Technology

Every choice, and why.

LayerChoiceVersionWhy
FrameworkNext.js (App Router)16.3.2Server components keep database access off the client entirely
UIReact19.2.8Server and client components in one tree
LanguageTypeScript5Row types flow from database to component unchanged
StylingTailwind CSS4Design tokens declared in CSS, no config file to drift
DatabaseSupabase Postgres2.112.3Row Level Security enforced at the database, not the app
ValidationZod4.4.3One schema shared by the browser form and the server handler
EmailResend6.22.0Verified sending domain; Supabase's own mailer is testing-grade
FormsReact Hook Form7.86.0Uncontrolled inputs, validated by the shared Zod schema
Client stateZustand5.0.15Guest cart and wishlist persisted before sign-in
Imagessharp0.35.3Icon, badge and social-card generation at build time
07

Architecture

No component touches the database.

Pages and route handlers call a service, and services map snake_case database rows onto the domain shapes components render. That boundary is what allowed the entire backend to be moved from MongoDB to Supabase mid-project without editing a single component.

Three database clients

Supabase is reached through three separate clients, and choosing the wrong one fails quietly rather than loudly — which is precisely why they are kept distinct.

ClientCredentialsUsed forSecurity
Publicanon key, no cookiesCatalogue reads on cached pagesEnforced as anonymous
Requestanon key + sessionCart, orders, bookings, admin readsEnforced as that user
Serviceservice roleStorage writes, imports, auth adminBypasses RLS — server only

Why the public client exists

A call that reads cookies inside a cached page throws at request time, taking the page down. Catalogue reads therefore use a client that cannot read cookies at all, which makes the mistake impossible rather than merely discouraged.
08

Data model

Nineteen tables, all with RLS.

Nineteen tables across nine migrations, every one with Row Level Security enabled. Access rules live in the database, so a query issued from anywhere — the application, a script, a leaked key — is subject to the same policy.

DomainTablesLive rows
Catalogueproducts, product_images, categories, brands, tags, product_tags1,232
Commerceorders, order_items, order_status_history, order_notes, cart_items, wishlist_items12
Clinicconsultations, consultation_bookings, booking_notes7
Contentarticles, settings1
Identityprofiles4

Business logic in SQL

Sixteen database functions hold the rules that must not be influenced by a browser. The client sends product identifiers and quantities and nothing else — no prices, no totals.

  • create_order

    Prices every line from the products table, applies the shipping rule from settings, and writes the order in one transaction.

  • verify_payment

    Reserves stock with a guarded update per line; any shortfall rolls the whole verification back. It is idempotent, so a double click cannot double-decrement.

  • create_booking

    Enforces the seven-day consultation lead time server-side, because the booking endpoint is reachable without the interface.

  • verify_booking_payment

    Confirms payment and the appointment slot together, and refuses any caller who is not an administrator.

  • product_facets

    Returns all filter counts in one round trip, since PostgREST cannot express GROUP BY.

09

Security

Hiding an interface is never the control.

Administrator status is read from the database on every request, never from a token claim, and is checked in three independent places: the admin layout guard, each admin route handler, and the RLS policies beneath both.
  • Private files are genuinely private. Payment screenshots and library PDFs live in buckets with no public read policy; access is a short-lived signed URL minted only after the session has been checked.
  • Uploads are identified by their bytes, not their declared type. A file renamed to .jpg is rejected because the signature is read directly.
  • Rate limiting on sign-in, sign-up, password recovery and the contact form, per address and per source, so the shared email quota cannot be drained.
  • The service-role key is server-only and the module carrying it is marked so it cannot be imported into browser code even by accident.
10

Verification

Types and lint meant very little here.

Type-checking and linting pass, but on this project they proved to mean very little on their own: every defect that mattered — storage permissions, a missing join modifier, cookies in a cached page, an unconfigured image host — passed both cleanly and appeared only against the real service.

Two habits followed from that. Database logic is exercised by npm run db:verify, which applies all nine migrations to an in-process Postgres and runs 36 checks over pricing, stock reservation, idempotency, authorisation and booking rules — it has caught real defects before deployment. Everything else is verified by exercising the actual endpoint against the live project and confirming the row or object exists, including a full customer journey from sign-up through payment.

11

Discoverability

Structured for local search.

The highest-intent queries for this practice are geographic. Pages carry MedicalClinic and Physician structured data with address, opening hours and credentials, which is what makes a clinic eligible for a local map result rather than appearing as an anonymous shop.

The sitemap covers 492 URLs and regenerates daily; robots rules exclude private and duplicate paths; product pages carry Product markup with images, price and availability. Analytics is wired through the Google tag, loaded after hydration so it never delays first paint.

A note on ratings

The catalogue shipped with 42 seeded star ratings left over from sample data. These were published to Google as review markup, which breaches the review-snippet policy and risks a manual penalty. They were cleared, and the seed source was changed so they cannot return. Ratings will appear when real customers leave them.
12

Responsive

Holds together at every width.

The three-layer header collapses to a sheet, the product grid reflows to a single column, and checkout stays usable on a phone — where most of this catalogue's traffic lands.

Storefront on a tablet viewport
Tablet
Storefront on a mobile viewport
Mobile
13

Operations

Running it, and what it costs.

Deployed on Vercel against the production Supabase project, with a verified sending domain for transactional mail. Eleven maintenance scripts cover the work that recurs: migration verification, seed regeneration, WooCommerce conversion, taxonomy seeding, icon and social-card generation, image optimisation and a live email delivery test.

ResourceIn useFree-tier limit
Database~1,250 rows500 MB
File storage70.4 MB1 GB
EmailLow volume3,000 / month

The one real exposure

The Supabase free tier includes no backups. The catalogue, taxonomy, orders and customer accounts have no recovery point if the project is lost. A scheduled export, or the paid tier's daily backups, is the recommended next step before the store is promoted.
14

Outstanding

Deferred, with the reasoning recorded.

Product reviews

Deferred

Verified-purchase reviews need order volume to be worth building; empty stars on 369 products read worse than none. Google Business Profile reviews are the higher-value target first.

WhatsApp notifications

Costed

Roughly ₹30 per month at current volume, with no DLT registration required, unlike SMS. Needs Meta business verification and a dedicated number.

Three dosage categories

Blocked

Granule, Kwatham and a Tablets category separate from Gulika do not exist yet. Splitting the latter means reassigning 116 products.

Google Business Profile

Client action

The single largest lever for local ranking, and outside the codebase. The map coordinates in the site should be replaced with the exact pin once claimed.

Have a business that needs more than a brochure?

Catalogues, bookings, payments and the back office to run them — designed, built and deployed as one system.