Skip to content

Repository files navigation

Cellar — Wine Bar Manager

A Next.js app for managing a restaurant wine bar's back of house: inventory, vendors, purchase orders, and vendor invoices (logged as expenses), all surfaced on a dashboard.

Stack

  • Next.js 16 (App Router, React Server Components, Server Actions) + TypeScript
  • Prisma 7 ORM with PostgreSQL (Supabase) via the @prisma/adapter-pg driver adapter
  • Tailwind CSS 4 + shadcn/ui (base-ui primitives, lucide icons)
  • Clerk for authentication (email + Google) and Organizations for multi-tenancy
  • Zod for server-side validation

Getting started

pnpm install            # installs deps; postinstall runs `prisma generate`
# set DATABASE_URL (+ Clerk keys) in .env — see Database and .env.example
pnpm db:migrate         # apply migrations to your Postgres database
pnpm dev                # http://localhost:3000 — sign up, create your venue
# optional sample data, scoped to an org you created (see Authentication):
SEED_ORG_ID=org_xxx pnpm db:seed

If a pnpm script is blocked by a pre-run check on your machine, run the underlying binary directly, e.g. node_modules/.bin/tsx prisma/seed.ts.

Database

Uses PostgreSQL via Prisma's @prisma/adapter-pg adapter. Any Postgres works; the project is set up against Supabase.

  • Set DATABASE_URL in .env to your connection string. For Supabase, use the Session pooler string (IPv4-compatible) from the dashboard's Connect modal — the direct db.<ref>.supabase.co host is IPv6-only and may fail on IPv4 networks. Example:
    DATABASE_URL="postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres"
    
  • pnpm db:migrate applies migrations; pnpm db:studio opens Prisma Studio.
  • The Prisma client is generated to lib/generated/prisma and instantiated with the pg adapter in lib/prisma.ts.

Authentication & multi-tenancy

Auth is handled by Clerk; each organization is one venue and all data is scoped to the active organization.

  1. Create an app at dashboard.clerk.com.
  2. Enable Email and Google sign-in, and turn on Organizations (Configure → Organizations).
  3. Copy your keys into .env (see .env.example):
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
    CLERK_SECRET_KEY="sk_test_..."
    
  4. pnpm dev, sign up, then create your venue when prompted at /select-org.
  5. To load sample data into that venue, copy its org id (org_…) from the Clerk dashboard and run SEED_ORG_ID=org_xxx pnpm db:seed.

Keyless dev mode: without keys, Clerk v7 auto-provisions a temporary dev instance so you can click around immediately. It's ephemeral (resets on server restart) — set real keys for stable local development.

Auth is enforced in two layers: proxy.ts (Clerk middleware) protects every route except /sign-in and /sign-up, and requireOrg() (lib/auth.ts) redirects users with no active organization to /select-org and returns the orgId/userId that every page and server action uses to scope data.

Vendor order emails

When a purchase order enters the Sent state — created as Sent, or moved Draft→Sent via "Mark as sent" — the vendor is emailed a full order summary (line items, totals, dates, notes) via Resend.

  • Set RESEND_API_KEY and ORDER_EMAIL_FROM in .env (see .env.example).
  • Dev: the default sender onboarding@resend.dev works without a domain but can only deliver to your own Resend account's email. Production: verify a domain in Resend and set ORDER_EMAIL_FROM to an address on it.
  • Sending is best-effort: if the key is unset or the vendor has no email on file, the order still saves and the "Mark as sent" toast says what happened. It never blocks or fails order creation.

Logic lives in lib/email.ts (send + HTML template) and notifyVendorOrderSent() in lib/actions/orders.ts.

Scripts

Script What it does
pnpm dev Start the dev server
pnpm build Production build
pnpm db:migrate Create/apply a migration (prisma migrate dev)
pnpm db:reset Drop and recreate the DB, then re-run seed
pnpm db:seed Seed sample data
pnpm db:studio Open Prisma Studio to browse the data

Features

  • Dashboard — inventory value, wines tracked, low-stock count, month-to-date spend, reorder suggestions (below par), recent orders and invoices.
  • Wines — bottle inventory with type, vintage, vendor, quantity, par level, cost/sale price and a low-stock badge.
  • Vendors — distributors with contact details; detail view rolls up their wines, orders and total invoiced.
  • Purchase Orders — multi-line orders with a status lifecycle (Draft → Sent → Received / Cancelled). Marking an order received increments wine inventory for each line item, in a transaction.
  • Invoices — vendor invoices logged as expenses with manual line items and an auto-computed total.

Data model

Vendor, Wine, PurchaseOrder + PurchaseOrderItem, Invoice + InvoiceItem. See prisma/schema.prisma. The Prisma client is generated to lib/generated/prisma and instantiated in lib/prisma.ts.

Project layout

app/          # routes: dashboard (/), /wines, /vendors, /orders, /invoices
components/   # app-sidebar, page-header, delete-button, forms/, ui/ (shadcn)
lib/
  actions/    # server actions (create/update/delete + order status transitions)
  prisma.ts   # PrismaClient singleton (pg adapter)
  constants.ts# wine types + order statuses
  utils.ts    # cn(), currency/date formatters
prisma/       # schema, migrations, seed.ts

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages