React + Tailwind + Vinext frontend, Cloudflare Worker API and a persistent Cloudflare D1 SQL database. This hosted project does not run Go or MySQL; those require a separately hosted service.
Open /login, choose Doctor or Wholesaler, then sign in with ChatGPT and complete clinic/company details. Account roles are persisted and enforced server-side; they cannot be changed using browser parameters. Email is display-only; ownership uses the stable platform user ID. Only account owners can read their carts, restock plans and buyer orders; sellers access only their listings and associated quotes/orders.
The deployed site is public; purchasing and seller operations require sign-in. A doctor and wholesaler use different accounts; entering the other portal does not grant that role. The login page offers sign-out. Medical council registration, drug license and GSTIN fields are collected; the current system does not independently verify licenses.
/wholesaler: overview, stock alerts, pending requests and recent activity./wholesaler/products: search/filter, add/edit/delete listings, SKU, minimum order quantity and volume pricing./wholesaler/inventory: download a CSV template, preview and validate rows, then confirm import. Headers:sku,batch,quantity,expiry. Quantities replace the matching batch total./wholesaler/quotes: review requests; respond with price, availability and delivery estimate, or decline./wholesaler/orders: confirm approved purchases, mark shipped and delivered. Doctor approval is required first.
/login?role=Doctor and /login?role=Wholesaler select onboarding roles. Existing accounts redirect according to their saved role. Server-side guards block cross-access. /seller and /creator redirect to the registered portal.
Three sample listings, a quote request and an order are seeded once per wholesaler. Sample records are labeled and persist in the database. Seller listings appear in the Doctor catalog; quote responses are shared with the requesting doctor.
- Sign in as Doctor and complete clinic details.
- Search supplier listings by product, generic name or brand. Supplier sample listings are labeled in this prototype.
- Add quantities to the cart; volume prices are calculated on the server.
- Submit the cart. It is split into one purchase request per supplier.
- Approve or reject requests. Approval validates current prices and unexpired inventory, allocates earliest-expiry batches and persists the order and stock in one transaction.
- Equipment purchases use RFQs. Accepting a quote creates a purchase request at the quoted price; approval allocates its stock.
- Create monthly restock plans with a first-request date. Use Create due requests when due or Request now for an early request. Early requests preserve the ordinary cart and do not duplicate an existing pending request for that plan.
D1 tables: accounts, products, inventory_batches, pricing_tiers, cart_items, orders, order_items, order_allocations, rfq_requests, subscriptions, audit_events, and marketplace_meta. The legacy workspaces table is retained without deletion.
Schema changes use append-only Drizzle migrations. On the first application read, an idempotent transactional migration copies the legacy marketplace aggregate into relational records. Old demo products remain in storage but are excluded from the purchasing catalog. Wholesaler onboarding seeds clearly labeled sample records once.
Each mutation reads a consistent snapshot, computes validated changes, then executes guarded row upserts/deletions, an audit event and the revision increment as one D1 batch transaction. A stale revision changes no rows and returns a conflict. Orders retain batch allocations and status history. Monetary values are integer paise, with safe total bounds.
This is designed for a small marketplace. Reads currently assemble a marketplace snapshot before applying response ownership filters. Large catalogs should replace this with indexed per-account queries, pagination and finer-grained optimistic concurrency before scaling. No client-side storage is used as the source of truth.
Use Node 22.13+ and the committed pnpm lockfile. Install with pnpm install --frozen-lockfile. The logical database binding is DB in .openai/hosting.json.
pnpm db:generate: append SQL migrations after schema edits.node node_modules/typescript/bin/tsc --noEmit: type check.node tests/workflows.mjs: workflow, permissions, pricing, expiry and API error-response regressions.node tests/database.mjs: real isolated D1 integration tests through Miniflare, including SQL migration, persistence, stock allocation, quotation-to-order conversion, due restocks and concurrent-write rollback.pnpm build: build the Worker and frontend.
Sites applies database schema migrations when publishing. Local development must apply the SQL migrations to a local D1 database before previewing. Authentication headers are trusted only behind the platform dispatcher; do not expose a standalone server that accepts caller-supplied identity headers.
Payments are arranged directly with suppliers; no online charge is made and the UI never marks an order paid. Tax, shipping-carrier integration, independent license verification and automatic background restocking are not configured. MedAgent uses catalog retrieval with explicit cart confirmation and is labeled guided mode; an LLM API and document-ingestion pipeline are not connected. Nurse invitations/delegation are not enabled. These limitations mean the project is not yet a regulated commercial launch.
This ZIP contains the deployed React/TypeScript + Cloudflare D1 project, not the original proposed Go/MySQL stack. It includes source, migrations, dependency lockfile and tests, but no user database records, credentials, node_modules or compiled output.
The full sign-in experience depends on the Sites platform. A local build alone does not reproduce that authentication service. For independent hosting, integrate a verified authentication provider before exposing the app; never trust identity headers sent directly by clients.