The production stack

This page is the handoff, not a mockup: the Supabase schema and the n8n workflow below are written, downloadable, and ready to run against a fresh project.

01
Manager uploads
PDF or photo hits the dropzone
02
n8n webhook
receives the file, builds the vision payload
03
GPT-4o Vision
line items + pitch content, strict JSON schema
04
Supabase
invoices + wines rows land behind RLS
05
Server's phone
realtime push to the floor list

Supabase schema + RLS

venues · venue_members · invoices · wines · storage bucket · realtime

Download schema.sql
create table public.wines (
  id           uuid primary key default gen_random_uuid(),
  venue_id     uuid not null references public.venues (id),
  invoice_id   uuid references public.invoices (id),
  name         text not null,
  vintage      text not null default 'NV',
  style        text check (style in
               ('red','white','sparkling','rose','dessert')),
  qty          integer not null default 0,
  unit_cost    numeric(10,2) not null,  -- manager-only
  bottle_price numeric(10,2) not null,
  glass_price  numeric(10,2),
  taste        text, palate jsonb, pairings text[],
  pitch        text not null default '',
  published    boolean not null default false
);

-- venue isolation
create policy "members read wines" on public.wines
  for select using (is_member(venue_id));

-- servers never see cost: column-level grant
revoke select on public.wines from authenticated;
grant select (id, name, vintage, style, bottle_price,
              glass_price, taste, palate, pairings, pitch, ...)
  on public.wines to authenticated;

Row-level security isolates venues; a column-level grant plus a floor_list view means a server's session cannot read unit_cost even with the API open in a browser tab. Realtime is enabled on wines, so a publish lands on every phone.

n8n workflow

importable JSON · webhook to Supabase in 8 nodes

Download workflow
  1. 1
    Invoice upload webhook
    POST /wine-invoice, multipart file + venue_id
  2. 2
    Build data URL
    binary to base64 for the vision payload
  3. 3
    GPT-4o Vision parse
    strict json_schema response: line items, taste, palate, pairings, one-sentence pitch
  4. 4
    Price and shape rows
    house pricing rule applied in code, not by the model
  5. 5
    Create invoice row
    Supabase insert, service role
  6. 6
    Split wines + create wine rows
    one row per parsed line item
  7. 7
    Respond to upload
    returns count + review status to the app

Import into any n8n instance, attach OpenAI + Supabase credentials, point the app's dropzone at the webhook URL. The vision call uses a strict JSON schema, so malformed output cannot reach the database.

What this demo runs vs. production

This demo, live right now

The same React + Tailwind front end you are using, with the vision parse running as a real AI call inside a Next.js route and state in your browser, so anyone can try it with zero accounts. The parse you watched on the manager view is not canned.

Production, per the spec

Identical pipeline on your infrastructure: Supabase for Postgres, Auth, storage and realtime; the n8n webhook workflow calling GPT-4o Vision; this front end pointed at Supabase with manager and server roles enforced by RLS.