{"path":"SetUp.md","size":8800,"locked":false,"binary":false,"truncated":false,"content":"# Setup Guide\n\nFollow these steps to connect your database and unlock the admin dashboard.\n\n---\n\n## 1. Create a Supabase project\n\n- Go to **[supabase.com/dashboard/new](https://supabase.com/dashboard/new)**  \n- Sign up with GitHub or email  \n- Create a new project (any name, pick a strong database password)\n\n---\n\n## 2. Get your API keys\n\n- In your Supabase dashboard, go to **Settings** (gear icon) → **API**  \n- Copy these three values:\n\n| Variable name | What it looks like |\n|---------------|-------------------|\n| `NEXT_PUBLIC_SUPABASE_URL` | `https://xxxxx.supabase.co` |\n| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | long string starting with `eyJ...` |\n| `SUPABASE_SERVICE_ROLE_KEY` | long string starting with `eyJ...` |\n\n---\n\n## 3. Add the keys to your hosting platform\n\n**Vercel** – Project Settings → Environment Variables → Add the three keys → Save → Redeploy  \n**Netlify** – Site settings → Environment variables → Add the three keys → Save → Redeploy\n\nUse exactly the names shown above.\n\n---\n\n## 4. Run the database migration\n\n- In your Supabase dashboard, open the **SQL Editor** (terminal icon)  \n- Copy the entire SQL block below (click the **copy button** when you hover over the block on GitHub)  \n- Paste it into the SQL Editor and click **Run**\n\n```sql\ncreate extension if not exists \"uuid-ossp\";\n\ndrop trigger if exists on_auth_user_created on auth.users;\ndrop function if exists public.handle_new_user;\n\ncreate table if not exists public.profiles (\n  id uuid primary key references auth.users(id) on delete cascade,\n  full_name text,\n  avatar_url text,\n  created_at timestamptz not null default now(),\n  updated_at timestamptz not null default now()\n);\n\nalter table public.profiles enable row level security;\n\ndrop policy if exists \"Users can view own profile\" on public.profiles;\ncreate policy \"Users can view own profile\" on public.profiles for select using (auth.uid() = id);\n\ndrop policy if exists \"Users can update own profile\" on public.profiles;\ncreate policy \"Users can update own profile\" on public.profiles for update using (auth.uid() = id) with check (auth.uid() = id);\n\ncreate or replace function public.handle_new_user()\nreturns trigger language plpgsql security definer set search_path = ''\nas $$\nbegin\n  insert into public.profiles (id, full_name, avatar_url)\n  values (new.id, new.raw_user_meta_data ->> 'full_name', new.raw_user_meta_data ->> 'avatar_url');\n  return new;\nend;\n$$;\n\ncreate trigger on_auth_user_created\n  after insert on auth.users\n  for each row execute function public.handle_new_user();\n\ncreate table if not exists public.admin_users (\n  user_id uuid primary key references auth.users(id) on delete cascade,\n  created_at timestamptz default now()\n);\nalter table public.admin_users disable row level security;\n\ncreate table if not exists public.site_settings (\n  id int primary key default 1,\n  name text not null default 'Your Business Name',\n  tagline text default '',\n  logo text default '',\n  favicon text default '/favicon.ico',\n  colors jsonb default '{\"primary\":\"#111\",\"primaryLight\":\"#333\",\"secondary\":\"#fafafa\",\"dark\":\"#111\",\"light\":\"#fafafa\"}',\n  fonts jsonb default '{\"heading\":\"Cinzel, serif\",\"body\":\"Inter, sans-serif\"}',\n  hero jsonb default '{}',\n  about jsonb default '{}',\n  contact jsonb default '{}',\n  footer jsonb default '{\"copyright\":\"\",\"socials\":{\"instagram\":\"\",\"facebook\":\"\",\"x\":\"\",\"youtube\":\"\",\"whatsapp_channel\":\"\",\"telegram_channel\":\"\",\"discord_channel\":\"\",\"viber_channel\":\"\"}}',\n  nav jsonb default '[]',\n  chat jsonb default '{\"whatsapp\":\"\",\"telegram\":\"\",\"discord\":\"\",\"viber\":\"\"}',\n  preferred_chat text default 'whatsapp',\n  created_at timestamptz default now(),\n  updated_at timestamptz default now()\n);\n\nalter table public.site_settings enable row level security;\n\ndrop policy if exists \"Public read\" on public.site_settings;\ncreate policy \"Public read\" on public.site_settings for select using (true);\n\ndrop policy if exists \"Admin insert\" on public.site_settings;\ncreate policy \"Admin insert\" on public.site_settings for insert with check (auth.uid() in (select user_id from public.admin_users));\n\ndrop policy if exists \"Admin update\" on public.site_settings;\ncreate policy \"Admin update\" on public.site_settings for update using (auth.uid() in (select user_id from public.admin_users)) with check (auth.uid() in (select user_id from public.admin_users));\n\ncreate table if not exists public.products (\n  id uuid primary key default uuid_generate_v4(),\n  name text not null,\n  description text,\n  price numeric(10,2) not null,\n  image text default '',\n  images jsonb default '[]',\n  badge text,\n  icon text default 'sparkles',\n  is_active boolean default true,\n  created_at timestamptz default now(),\n  updated_at timestamptz default now()\n);\n\nalter table public.products enable row level security;\n\ndrop policy if exists \"Public read\" on public.products;\ncreate policy \"Public read\" on public.products for select using (is_active = true);\n\ndrop policy if exists \"Admin manage\" on public.products;\ncreate policy \"Admin manage\" on public.products for all using (auth.uid() in (select user_id from public.admin_users));\n\ncreate table if not exists public.orders (\n  id uuid primary key default uuid_generate_v4(),\n  customer_name text not null,\n  customer_email text not null,\n  customer_phone text,\n  product_id uuid references public.products(id),\n  quantity int default 1,\n  total_price numeric(10,2) not null,\n  status text default 'pending',\n  created_at timestamptz default now()\n);\n\nalter table public.orders enable row level security;\n\ndrop policy if exists \"Admin read\" on public.orders;\ncreate policy \"Admin read\" on public.orders for select using (auth.uid() in (select user_id from public.admin_users));\n\ndrop policy if exists \"Public insert\" on public.orders;\ncreate policy \"Public insert\" on public.orders for insert with check (true);\n\ncreate table if not exists public.reviews (\n  id uuid primary key default uuid_generate_v4(),\n  order_id uuid references public.orders(id) on delete cascade,\n  product_id uuid references public.products(id),\n  user_id uuid references auth.users(id),\n  author text,\n  rating int check (rating >= 1 and rating <= 5),\n  comment text,\n  approved boolean default false,\n  created_at timestamptz default now()\n);\n\nalter table public.reviews enable row level security;\n\ndrop policy if exists \"Public read approved\" on public.reviews;\ncreate policy \"Public read approved\" on public.reviews for select using (approved = true);\n\ndrop policy if exists \"Public insert\" on public.reviews;\ncreate policy \"Public insert\" on public.reviews for insert with check (true);\n\ndrop policy if exists \"Admin delete\" on public.reviews;\ncreate policy \"Admin delete\" on public.reviews for delete using (auth.uid() in (select user_id from public.admin_users));\n\ndrop policy if exists \"Admin update\" on public.reviews;\ncreate policy \"Admin update\" on public.reviews for update using (auth.uid() in (select user_id from public.admin_users));\n\n-- Allow admins to see all reviews (including unapproved)\ndrop policy if exists \"Admin read all\" on public.reviews;\ncreate policy \"Admin read all\" on public.reviews\n  for select\n  using (auth.uid() in (select user_id from public.admin_users));\n\n-- Seed default site settings\ninsert into public.site_settings (id, name) values (1, 'Your Business Name') on conflict (id) do nothing;\n\n-- First user becomes admin\ncreate or replace function public.make_first_user_admin()\nreturns trigger language plpgsql security definer set search_path = ''\nas $$\nbegin\n  if not exists (select 1 from public.admin_users) then\n    insert into public.admin_users (user_id, created_at)\n    values (new.id, now());\n  end if;\n  return new;\nend;\n$$;\n\ndrop trigger if exists on_first_user_admin on auth.users;\ncreate trigger on_first_user_admin\n  after insert on auth.users\n  for each row execute function public.make_first_user_admin();\n`; \n```\n\n---\n\n## 5. Turn off email confirmation (recommended)\n\n- In your Supabase dashboard, click the **☰** icon (top‑left) to open the sidebar  \n- Go to **Authentication** → **Configuration** → **Sign in/Providers**  \n- Check the **User Signups** options \n- Turn off **“Confirm email”**  \n- Click **Save**\n\nThis lets customers sign up and log in instantly without checking their inbox.\n---\n\n## 6. Deploy and sign up\n\n- After adding the keys and running the SQL, redeploy your site  \n- Visit your live URL and **sign up**  \n- The first user who signs up automatically becomes the admin  \n- Go to `/admin` to access your dashboard\n---\n\n## 7. Customize the booking button (optional)\n\nThe product cards show **\"Order via Chat\"** by default.  \nTo change it, open `src/components/ProductCard.tsx` and edit the button text on line 182.\n\nCommon alternatives:\n- `Book Now`\n- `Inquire`\n- `Chat with Us`\n- `Buy Now`\n"}