Skip to main content
RapidDev - Software Development Agency
Lovable PromptsInternal ToolsIntermediate

Build an Admin Panel in Lovable

A role-gated admin dashboard with sidebar navigation, three CRUD modules (users, products, orders), search, filters, pagination, and CSV export — backed by Supabase Cloud with admin-only RLS policies.

Time to MVP

~2 hours

Credits

~500-800 credits for full build

Difficulty

Intermediate

Cloud features

3

TL;DR

The one-paragraph version before you dive in.

Paste the starter prompt below into Lovable Agent Mode and you get a working admin panel scaffold (sidebar layout, users/products/orders CRUD, search, pagination, role-gated routes) on Supabase Cloud. Total build time: ~2 hours with five follow-up prompts. Expected credit burn: 500-800 on a Pro $25/mo plan.

Setup checklist

Complete these steps in Lovable before pasting the starter prompt — takes about 5 minutes.

Cloud tab settings

Database

Stores users, products, orders, and an audit_log for admin actions.

  1. 1Click the + button next to Preview in the top toolbar to open the Cloud tab
  2. 2Click Database — you'll see an empty Table Editor
  3. 3Leave it empty for now — the starter prompt will create the schema and migrations for you

Auth

Email+password sign-in for admins. Role is stored in app_metadata so it's signed into the JWT and cannot be tampered with from the client.

  1. 1Cloud tab → Users & Auth
  2. 2Under Sign-in methods, enable Email (the default — already on)
  3. 3Disable any other provider you don't need (Google, GitHub) so the surface area is small
  4. 4Confirm 'Allow new users to sign up' is OFF — you'll invite admins manually from the Users page

Storage

Holds product images uploaded from the Products module. One private bucket with admin-only read/write.

  1. 1Cloud tab → Storage
  2. 2Click Create bucket, name it 'product-images', leave Public OFF
  3. 3Note the bucket name — the starter prompt will reference it

Preflight checklist

  • You're in a fresh Lovable project (Vite + React + TypeScript + Tailwind + shadcn/ui already scaffolded — that's the default)
  • You're on Pro $25/mo plan or willing to upgrade mid-build — full build runs ~500-800 credits and Free plan caps at 30/mo
  • You've created the first admin user manually via Cloud tab → Users & Auth → Add user, and set their app_metadata to {"role": "admin"} from the user's row

The starter prompt — paste this first

Copy this. Paste it into Lovable Agent Mode (the default chat at the bottom-left of the editor). Hit send.

lovable-agent-mode.txt
~120-180 credits
Build an admin panel for an internal tool. Stack assumptions: React + Vite + TypeScript + Tailwind + shadcn/ui (already scaffolded by Lovable), React Router for routes, Supabase JS client for data access against Lovable Cloud.

## Database schema (create a migration)
Create four tables in the public schema:

1. `profiles` — one row per auth.users row, linked by id (uuid, FK to auth.users.id, cascade delete). Columns: full_name (text), email (text not null), role (text not null default 'member', check role in ('admin','member')), created_at (timestamptz default now()).

2. `products` — id (uuid pk default gen_random_uuid()), name (text not null), sku (text unique not null), price_cents (int not null), stock (int not null default 0), image_path (text — Storage path), archived (bool default false), created_at (timestamptz default now()).

3. `orders` — id (uuid pk default gen_random_uuid()), order_number (text unique not null default 'ORD-' || nextval('orders_seq')), customer_email (text not null), status (text not null default 'pending', check status in ('pending','paid','shipped','refunded','cancelled')), total_cents (int not null), placed_at (timestamptz default now()), notes (text). Also create sequence orders_seq.

4. `audit_log` — id (uuid pk default gen_random_uuid()), actor_id (uuid references auth.users.id), action (text not null), entity_type (text not null), entity_id (text), payload (jsonb), created_at (timestamptz default now()).

Enable RLS on all four. Add policies: only users where `(auth.jwt() ->> 'app_metadata')::jsonb ->> 'role' = 'admin'` can select/insert/update/delete profiles, products, orders, audit_log. Add a `before insert` trigger on profiles that copies auth.users.email into profiles.email.

## Layout
Create `src/layouts/AdminLayout.tsx` — a two-column shell with a fixed left sidebar (240px) and a scrollable main area. Sidebar contains: logo at top, nav links (Dashboard, Users, Products, Orders, Settings), bottom-pinned user menu (avatar initials + signed-in email + Sign out button). Use shadcn/ui components throughout (Button, Avatar, DropdownMenu, ScrollArea). Top of main area: breadcrumbs (Home / [Section] / [Detail]).

Wrap the layout in an AdminGuard that calls `supabase.auth.getUser()` and reads app_metadata.role. If not admin → redirect to /unauthorized (create that page too, with a one-line 'You don't have access' message and Sign out button).

## Pages
Create these routes in src/App.tsx and corresponding page components in src/pages/admin/:
- /admin (Dashboard.tsx) — 4 metric cards (Users count, Products count, Orders this month, Revenue this month) + recent orders table (last 10).
- /admin/users (Users.tsx) — table of profiles, search by email/name, role filter, paginated 25/page. Row click opens a Sheet (right drawer) with edit form (full_name, role) and Save.
- /admin/products (Products.tsx) — same pattern. Edit Sheet has name, sku, price (dollars input → cents conversion), stock, image upload (Storage bucket 'product-images'), archived toggle.
- /admin/orders (Orders.tsx) — table with status filter chips (pending/paid/shipped/refunded/cancelled), date range filter, search by order_number or customer_email. Row click opens a Dialog with order detail + status change dropdown.
- /admin/settings (Settings.tsx) — placeholder card with 'Account' section showing current admin profile + 'Sign out' button.

All three list pages share an `<AdminTable>` wrapper component that handles search debounce (300ms), pagination, empty state, and loading skeletons.

Every mutation (update profile, update product, update order status) writes one row to audit_log via a `useAuditLog()` hook.

## Styling cues
Light mode only for v1. Use the default shadcn theme but set the primary color to slate-900 in tailwind.config.ts. Tables use shadcn Table with zebra striping on hover. Use lucide-react icons in sidebar nav.

Generate the schema migration first, then the layout, then the four pages in the order above. After each major piece, stop and let me confirm before continuing.

What this prompt generates

  • Creates a SQL migration with 4 tables (profiles, products, orders, audit_log), RLS policies, and one trigger
  • Scaffolds AdminLayout.tsx with sidebar nav, user menu, breadcrumbs, and admin-only AdminGuard wrapper
  • Generates 5 page components (Dashboard, Users, Products, Orders, Settings) wired to React Router
  • Builds a reusable <AdminTable> wrapper with search debounce, pagination, empty/loading states
  • Adds useAuditLog() hook that records every mutation to the audit_log table

Paste into: Lovable Agent Mode (the default chat at the bottom-left of the editor)

Expected output

What Lovable will generate after the starter prompt runs successfully.

Files
supabase/migrations/0001_admin_panel_schema.sql

All four tables + RLS policies + profiles trigger

src/layouts/AdminLayout.tsx

Sidebar + breadcrumbs + user menu shell, wrapped in AdminGuard

src/components/AdminGuard.tsx

Reads JWT app_metadata.role, redirects to /unauthorized if not admin

src/components/AdminTable.tsx

Reusable list-view wrapper with search, pagination, states

src/hooks/useAuditLog.ts

writeAudit({action, entity_type, entity_id, payload}) helper

src/pages/admin/Dashboard.tsx

Metric cards + recent orders table

src/pages/admin/Users.tsx

Profiles list + edit Sheet

src/pages/admin/Products.tsx

Products list + edit Sheet with image upload

src/pages/admin/Orders.tsx

Orders list + status change Dialog

src/pages/admin/Settings.tsx

Current admin profile + sign out

src/pages/Unauthorized.tsx

Fallback for non-admin users

Routes
/admin

Dashboard with KPI cards and recent orders

/admin/users

Profiles list, role filter, edit drawer

/admin/products

Products list with image upload

/admin/orders

Orders list with status filter and detail dialog

/admin/settings

Account settings (placeholder for v1)

/unauthorized

Shown when a non-admin lands on /admin/*

DB Tables
profiles
ColumnType
iduuid primary key
full_nametext
emailtext not null
roletext not null default 'member'
created_attimestamptz default now()

RLS: Admin-only via JWT app_metadata.role check

products
ColumnType
iduuid primary key default gen_random_uuid()
nametext not null
skutext unique not null
price_centsint not null
stockint not null default 0
image_pathtext
archivedbool default false
created_attimestamptz default now()

RLS: Admin-only read+write; consider a 'storefront' policy later if you expose products publicly

orders
ColumnType
iduuid primary key default gen_random_uuid()
order_numbertext unique not null
customer_emailtext not null
statustext not null default 'pending'
total_centsint not null
placed_attimestamptz default now()
notestext

RLS: Admin-only for v1; if you connect a customer-facing storefront later, add a per-customer select policy

audit_log
ColumnType
iduuid primary key default gen_random_uuid()
actor_iduuid references auth.users.id
actiontext not null
entity_typetext not null
entity_idtext
payloadjsonb
created_attimestamptz default now()

RLS: Admin-only; you'll read this from /admin/settings later

Components
AdminLayout

Two-column shell with sidebar + main scroll area

AdminGuard

Role check wrapper, redirects unauthorized users

AdminTable

Generic list view with search, pagination, empty/loading states

EntityDrawer (Sheet)

Right-side drawer for edit forms

StatusFilterChips

Order status quick filter row

Follow-up prompts

Paste these into Agent Mode one by one, in order, after the starter prompt finishes.

1

Lock down RLS for production

Defense-in-depth on RLS so a leaked anon key cannot read or escalate admin data.

~30 credits
prompt
Audit the RLS policies you just created. For each table (profiles, products, orders, audit_log), confirm there is no policy that allows anon role access. Make sure every policy uses `(auth.jwt() ->> 'app_metadata')::jsonb ->> 'role' = 'admin'` and not `auth.uid()` alone. Also add a `WITH CHECK` clause to every INSERT/UPDATE policy so admins can't escalate someone else's role. Run a test query as the anon role at the end and confirm it returns 0 rows for every table.

When to use: Right after the starter prompt — before you put real data in

2

Add CSV export to every list view

One-click data export from any filtered view, with audit trail.

~40 credits
prompt
Add an Export CSV button to the top-right of the Users, Products, and Orders pages. Clicking it should download the currently filtered+sorted view as a CSV (respecting search query, filters, and date range — not the whole table). Use the 'papaparse' library if not already installed. File names: 'users-YYYY-MM-DD.csv', 'products-YYYY-MM-DD.csv', 'orders-YYYY-MM-DD.csv'. Log every export to audit_log with action 'export.csv' and payload {row_count, filters}.

When to use: Once the three list pages render correctly

3

Add an admin invite flow

Self-serve admin onboarding — no more manual app_metadata edits in the Cloud tab.

~80 credits
prompt
On /admin/users add an 'Invite admin' button (top-right). It opens a Dialog with an email field. On Submit, call a Supabase Edge Function `invite-admin` that does: (1) creates a user via supabase.auth.admin.inviteUserByEmail(email), (2) sets app_metadata.role='admin' on the new user, (3) writes an audit_log row with action='admin.invite'. Show toast success/error. The Edge Function should reject the call if the caller is not already an admin (check JWT inside the function). Use the Cloud tab → Edge Functions to deploy.

When to use: When you've onboarded yourself and want to add a second admin

4

Add a Stripe billing module

Live Stripe customer/subscription view inside the admin panel.

~100 credits
prompt
Add a new page /admin/billing that lists Stripe subscriptions for all profiles. Pre-reqs: I'll add STRIPE_SECRET_KEY to Cloud tab → Secrets. Create an Edge Function `stripe-subscriptions-list` that calls Stripe's customer.list with limit=100 + auto-pagination and returns name, email, status, current_period_end, amount. Render the result in an AdminTable. Add a row action 'Open in Stripe' that links to dashboard.stripe.com/customers/{id}. Status filter chips: active, past_due, canceled. Don't store anything in our DB — Stripe is the source of truth.

When to use: Once Stripe is your billing system and you want a single pane of glass

5

Wire up the dashboard metric cards with real data

The Dashboard becomes a real at-a-glance health view instead of a placeholder.

~50 credits
prompt
Replace the placeholder numbers on /admin Dashboard with live data. Create a Supabase RPC function `admin_metrics()` that returns: total_users (count of profiles), total_products (count where archived=false), orders_this_month (count where placed_at >= date_trunc('month', now())), revenue_this_month_cents (sum of total_cents where status in ('paid','shipped') and placed_at >= date_trunc('month', now())). The hook useAdminMetrics() should call it on mount and refresh every 60 seconds. Format revenue in dollars with thousands separators in the card.

When to use: After you have at least 5-10 real orders in the database

6

Add an audit log viewer

Full visibility into what every admin did and when.

~40 credits
prompt
On /admin/settings add a 'Recent admin activity' section below the account card. Show the last 50 audit_log entries, newest first, in a compact list: <actor_email> · <action> · <entity_type>:<entity_id> · <relative time>. Each row is clickable to expand the payload JSON in a Dialog. Add a 'Load more' button at the bottom that paginates 50 at a time.

When to use: Once you have 2+ admins

Common errors

Real error strings you'll see. Find yours, paste the fix prompt.

new row violates row-level security policy for table "profiles"

You're signed in but your JWT doesn't have app_metadata.role='admin'. Lovable's Cloud tab created your user without it.

Fix — paste into Lovable Agent Mode
Open Cloud tab → Users & Auth, click my user, find the 'app_metadata' field (it'll be empty {}), set it to {"role": "admin"} and save. Then sign out and sign back in to refresh the JWT.

Manual fix

Cloud tab → Users & Auth → click your user row → edit app_metadata to {"role": "admin"} → save → sign out + sign back in

Uncaught TypeError: Cannot read properties of undefined (reading 'role')

AdminGuard tried to read role from JWT before the auth session loaded. The Lovable starter likely didn't add a loading state.

Fix — paste into Lovable Agent Mode
In AdminGuard, show a centered loading spinner while supabase.auth.getUser() is in flight. Only check role and redirect after the user object is defined. Use a useEffect with isLoading state, default true.
Failed to fetch: 401 Unauthorized (Storage upload)

The product-images bucket has no policy for authenticated users. Buckets default to no access.

Fix — paste into Lovable Agent Mode
Add a Storage policy on the product-images bucket: allow SELECT, INSERT, UPDATE, DELETE for any role where (auth.jwt() ->> 'app_metadata')::jsonb ->> 'role' = 'admin'. Run the policy SQL in Cloud tab → Database → SQL Editor.
duplicate key value violates unique constraint "orders_order_number_key"

Lovable scaffolded order_number as plain text instead of pulling from the sequence. The default expression is missing.

Fix — paste into Lovable Agent Mode
Alter the orders table: set the default for order_number to ('ORD-' || nextval('orders_seq')::text). Make sure the sequence orders_seq exists; if not, create it first with CREATE SEQUENCE orders_seq START 1000.

Manual fix

Cloud tab → Database → SQL Editor: CREATE SEQUENCE IF NOT EXISTS orders_seq START 1000; ALTER TABLE orders ALTER COLUMN order_number SET DEFAULT ('ORD-' || nextval('orders_seq')::text);

Module not found: Can't resolve 'papaparse' (after the CSV export prompt)

Lovable added the import but didn't install the dependency.

Fix — paste into Lovable Agent Mode
Add 'papaparse' and '@types/papaparse' to package.json dependencies and run a fresh install. If you're in Lovable preview only, just tell Agent Mode 'install papaparse and @types/papaparse' and it will update package.json and trigger a rebuild.
Hydration failed because the initial UI does not match what was rendered on the server

You're not actually using SSR (Vite is SPA) — this error is usually from a date being formatted with toLocaleString() inconsistently, or a Math.random() in render.

Fix — paste into Lovable Agent Mode
Audit the codebase for any toLocaleString() or new Date().toLocaleDateString() calls in render. Replace with a stable formatter like date-fns format(date, 'MMM d, yyyy'). If you need any randomness in render, move it to a useEffect with useState.

Cost reality

What this build actually costs — no surprises on your card.

Recommended Lovable plan

Pro $25/mo — you'll burn through Free's 30 monthly credits before the starter prompt finishes. Pro gives you 100 credits + 5 daily plus unlocks Dev Mode (you'll want it the first time you need to hand-edit a generated file).

Monthly run cost breakdown

~500-800 credits including the starter prompt, all six follow-up prompts, 2-3 rounds of iteration and bug-fix prompts. total
ItemCost
Lovable Pro

Required for sustained iteration; Free will rate-limit you mid-build

$25/mo
Supabase (Lovable Cloud)

Free tier: 500MB DB, 50K monthly active users, 1GB Storage. Plenty for an internal admin panel.

$0/mo at MVP scale
Custom domain (optional)

Cloudflare or Porkbun if you want admin.yourcompany.com

$10-15/yr

Scaling notes: Costs only jump if (1) you onboard >50K MAU which bumps Supabase to Pro $25/mo, or (2) you start running heavy AI features inside the admin panel (live transcription, image generation) which add per-token model costs. The admin panel itself never gets expensive — it's an internal tool with low traffic.

Production checklist

Steps to take before you share the URL with real users.

Domain & SSL

  • Set up a custom subdomain

    Lovable: top-right Publish icon → Custom domains → add admin.yourcompany.com. Add the CNAME to your DNS provider (Cloudflare/Porkbun). SSL is automatic.

  • Block search engines from indexing the admin panel

    Tell Agent Mode: 'Add a /robots.txt to the public folder with User-agent: * / Disallow: / so the admin panel never gets indexed by Google.'

Email

  • Customize the Supabase invite email

    Cloud tab → Users & Auth → Email templates → Invite user. Replace the default with your brand and a clear 'You've been invited as an admin to [Product]' message.

  • Set a custom From address

    Cloud tab → Users & Auth → SMTP — connect Resend or Postmark (both free up to 100/day). Otherwise emails go from supabase.io and look like spam.

Backups

  • Enable daily Postgres backups

    Free tier includes daily backups with 7-day retention. No setup needed. Verify in Cloud tab → Database → Backups.

  • Export audit_log monthly to S3 (optional)

    Schedule an Edge Function (cron via pg_cron extension) that dumps audit_log rows older than 90 days to an S3 bucket, then deletes them locally. Keeps the table fast.

Monitoring

  • Wire Sentry for client errors

    Tell Agent Mode: 'Add @sentry/react. I'll provide VITE_SENTRY_DSN as a Secret. Initialize it in src/main.tsx with environment based on import.meta.env.MODE.'

  • Watch Supabase logs after each release

    Cloud tab → Logs → API + Database tabs. Filter by 4xx/5xx for the first 24 hours after any schema change.

Frequently asked questions

Does this work on the Free Lovable plan?

You can start the starter prompt on Free, but you'll hit the 30 monthly credit cap before the schema migration even finishes. Realistically you need Pro at $25/mo. The good news: once the admin panel is built, you don't need to keep paying Lovable — you can Publish to a custom domain and the panel runs on Supabase Cloud for free.

Why store role in app_metadata and not in the profiles table?

Because app_metadata is signed into the JWT by Supabase Auth and cannot be modified from the client. If you stored role in profiles, a clever user could update their own profile row and escalate to admin. JWT-signed claims are tamper-proof — that's the whole point of putting role there.

Can I extend this to support multiple admin roles (super-admin, support, etc.)?

Yes. Change app_metadata to {"role": "admin", "permissions": ["users.write", "orders.refund"]} and have AdminGuard accept a required permission. Update RLS policies to check (auth.jwt() ->> 'app_metadata')::jsonb -> 'permissions' ? 'users.write'. Lovable can generate this refactor for ~60-80 credits.

What if I'd rather use Clerk for auth?

You can — but you lose Lovable Cloud's first-class Supabase Auth integration and have to set up Clerk JWT templates that match Supabase's JWT signing key. For an admin panel, the built-in Supabase Auth is usually the right call. Switch to Clerk only if you already have a Clerk-based product and want shared sessions.

How do I move the database off Lovable Cloud later?

Lovable Cloud is just managed Supabase under the hood. You can export the schema and data (Cloud tab → Database → Export, or via Supabase Studio if you provision your own Supabase project) and point your Vite frontend to a self-hosted or Supabase.com-hosted instance by swapping VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY. No app code changes.

Can RapidDev build this admin panel for me end-to-end?

Yes — if your build outgrows this prompt kit and you need custom architecture, hardened RLS, SSO, audit-grade logging, or a multi-tenant variant, RapidDev builds production-grade Lovable apps at $13K-$25K. Book a free 30-minute consultation at rapidevelopers.com — we'll tell you honestly whether the prompt kit gets you 90% of the way or whether your scope justifies the build.

How long does the full chain (starter + 6 follow-ups) actually take?

About 2 hours of active editing if Lovable cooperates on the first try, 3-4 hours if you need to iterate. The bottleneck is reading Lovable's output between prompts — not the LLM speed. Block off an afternoon, not 30 minutes.

Need a production-grade version?

RapidDev builds production-grade Lovable apps at $13K–$25K. Book a free 30-minute consultation.

Book a free consultation

30-min call. No commitment.

Want this built for you?

We ship production apps at a fixed price — $13K–$25K, 6–10 weeks, source code yours. You've seen what it takes; we do it every week.

Get a fixed-price quote

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.