-- Düğün Salonu Yönetim Sistemi - Full Database Schema & Data -- Generated by Antigravity -- ========================================== -- 1. BASE SCHEMA (Core Tables) -- ========================================== -- Enable UUID extension create extension if not exists "uuid-ossp"; -- Create Profiles Table (Extends Auth) create table if not exists profiles ( id uuid references auth.users on delete cascade not null primary key, role text check (role in ('admin', 'staff')) default 'staff', full_name text, created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- Create Customers Table (CRM) create table if not exists customers ( id uuid default uuid_generate_v4() primary key, full_name text not null, phone text, email text, city text, district text, address text, notes text, created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- Create Halls Table (Salons) create table if not exists halls ( id uuid default uuid_generate_v4() primary key, name text not null, capacity int, description text, features text[], created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- Create Packages Table (Pricing) create table if not exists packages ( id uuid default uuid_generate_v4() primary key, name text not null, description text, price decimal(10,2) not null, is_active boolean default true, created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- Create Reservations Table create table if not exists reservations ( id uuid default uuid_generate_v4() primary key, hall_id uuid references halls(id) on delete set null, customer_id uuid references customers(id) on delete set null, package_id uuid references packages(id) on delete set null, start_time timestamp with time zone not null, end_time timestamp with time zone not null, status text check (status in ('pending', 'confirmed', 'cancelled', 'completed')) default 'pending', notes text, created_by uuid references auth.users(id), created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- Create Payments Table create table if not exists payments ( id uuid default uuid_generate_v4() primary key, reservation_id uuid references reservations(id) on delete cascade, amount decimal(10,2) not null, payment_type text check (payment_type in ('deposit', 'full', 'remaining')), payment_method text check (payment_method in ('cash', 'credit_card', 'transfer')), status text check (status in ('pending', 'paid', 'refunded')) default 'pending', paid_at timestamp with time zone, created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- RLS Policies for Base Tables alter table profiles enable row level security; alter table customers enable row level security; alter table halls enable row level security; alter table packages enable row level security; alter table reservations enable row level security; alter table payments enable row level security; -- Drop existing policies to avoid conflicts if re-running drop policy if exists "Enable all access for authenticated users" on profiles; drop policy if exists "Enable all access for authenticated users" on customers; drop policy if exists "Enable all access for authenticated users" on halls; drop policy if exists "Enable all access for authenticated users" on packages; drop policy if exists "Enable all access for authenticated users" on reservations; drop policy if exists "Enable all access for authenticated users" on payments; create policy "Enable all access for authenticated users" on profiles for all using (auth.role() = 'authenticated'); create policy "Enable all access for authenticated users" on customers for all using (auth.role() = 'authenticated'); create policy "Enable all access for authenticated users" on halls for all using (auth.role() = 'authenticated'); create policy "Enable all access for authenticated users" on packages for all using (auth.role() = 'authenticated'); create policy "Enable all access for authenticated users" on reservations for all using (auth.role() = 'authenticated'); create policy "Enable all access for authenticated users" on payments for all using (auth.role() = 'authenticated'); -- Auth Trigger create or replace function public.handle_new_user() returns trigger as $$ begin insert into public.profiles (id, full_name, role) values (new.id, new.raw_user_meta_data->>'full_name', 'staff') on conflict (id) do nothing; return new; end; $$ language plpgsql security definer; -- Drop trigger if exists drop trigger if exists on_auth_user_created on auth.users; create trigger on_auth_user_created after insert on auth.users for each row execute procedure public.handle_new_user(); -- Create Audit Logs Table create table if not exists audit_logs ( id uuid default uuid_generate_v4() primary key, user_id uuid references auth.users(id), action text not null, entity_type text not null, entity_id uuid, details jsonb, created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- RLS for Audit Logs alter table audit_logs enable row level security; drop policy if exists "Admins can read all logs" on audit_logs; create policy "Admins can read all logs" on audit_logs for select using ( exists ( select 1 from profiles where profiles.id = auth.uid() and profiles.role = 'admin' ) ); drop policy if exists "Users can insert logs" on audit_logs; create policy "Users can insert logs" on audit_logs for insert with check (auth.uid() = user_id); -- ========================================== -- 2. CMS Tables & Seeds -- ========================================== -- 1. Site Contents Table (Genel İçerikler) CREATE TABLE IF NOT EXISTS public.site_contents ( key TEXT PRIMARY KEY, value TEXT, type TEXT DEFAULT 'text', -- 'text', 'image_url', 'html', 'json' section TEXT, -- 'home', 'about', 'contact', 'footer' created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- RLS: Public read, Admin write ALTER TABLE public.site_contents ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Allow public read access on site_contents" ON public.site_contents; CREATE POLICY "Allow public read access on site_contents" ON public.site_contents FOR SELECT TO anon, authenticated USING (true); DROP POLICY IF EXISTS "Allow authenticated update on site_contents" ON public.site_contents; CREATE POLICY "Allow authenticated update on site_contents" ON public.site_contents FOR UPDATE TO authenticated USING (true) WITH CHECK (true); DROP POLICY IF EXISTS "Allow authenticated insert on site_contents" ON public.site_contents; CREATE POLICY "Allow authenticated insert on site_contents" ON public.site_contents FOR INSERT TO authenticated WITH CHECK (true); -- 2. Services Table (Hizmetler) CREATE TABLE IF NOT EXISTS public.services ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, title TEXT NOT NULL, description TEXT, image_url TEXT, "order" INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Allow public read on services" ON public.services; CREATE POLICY "Allow public read on services" ON public.services FOR SELECT TO anon, authenticated USING (is_active = true OR auth.role() = 'authenticated'); DROP POLICY IF EXISTS "Allow admin all on services" ON public.services; CREATE POLICY "Allow admin all on services" ON public.services FOR ALL TO authenticated USING (true) WITH CHECK (true); -- 3. Gallery Table (Galeri) CREATE TABLE IF NOT EXISTS public.gallery ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, image_url TEXT NOT NULL, caption TEXT, category TEXT DEFAULT 'Genel', "order" INTEGER DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); ALTER TABLE public.gallery ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Allow public read on gallery" ON public.gallery; CREATE POLICY "Allow public read on gallery" ON public.gallery FOR SELECT TO anon, authenticated USING (true); DROP POLICY IF EXISTS "Allow admin all on gallery" ON public.gallery; CREATE POLICY "Allow admin all on gallery" ON public.gallery FOR ALL TO authenticated USING (true) WITH CHECK (true); -- Seed Data (Initial Content) INSERT INTO public.site_contents (key, value, type, section) VALUES ('site_title', 'Rüya Düğün Salonu', 'text', 'general'), ('hero_title', 'Hayallerinizdeki Düğün İçin', 'text', 'home'), ('hero_subtitle', 'Unutulmaz anlar, profesyonel hizmet ve şık atmosfer.', 'text', 'home'), ('contact_phone', '+90 555 123 45 67', 'text', 'contact'), ('contact_address', 'Atatürk Mah. Karanfil Sok. No:5, İstanbul', 'text', 'contact'), ('site_logo', '', 'image_url', 'general') ON CONFLICT (key) DO NOTHING; -- ========================================== -- 3. Expenses Module -- ========================================== -- Create Expense Categories Table create table if not exists expense_categories ( id uuid default uuid_generate_v4() primary key, name text not null, description text, created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- Create Expenses Table create table if not exists expenses ( id uuid default uuid_generate_v4() primary key, category_id uuid references expense_categories(id) on delete set null, amount decimal(10,2) not null, description text, date timestamp with time zone default timezone('utc'::text, now()) not null, created_by uuid references auth.users(id), created_at timestamp with time zone default timezone('utc'::text, now()) not null ); -- RLS alter table expense_categories enable row level security; alter table expenses enable row level security; drop policy if exists "Enable all access for authenticated users" on expense_categories; drop policy if exists "Enable all access for authenticated users" on expenses; create policy "Enable all access for authenticated users" on expense_categories for all using (auth.role() = 'authenticated'); create policy "Enable all access for authenticated users" on expenses for all using (auth.role() = 'authenticated'); -- ========================================== -- 4. Storage Setup -- ========================================== -- 1. Create the storage bucket insert into storage.buckets (id, name, public) values ('hall-logos', 'hall-logos', true) on conflict (id) do nothing; insert into storage.buckets (id, name, public) values ('public-site', 'public-site', true) on conflict (id) do nothing; -- 2. Enable RLS -- Hall Logos Policies drop policy if exists "Public Access" on storage.objects; create policy "Public Access" on storage.objects for select using ( bucket_id = 'hall-logos' ); drop policy if exists "Authenticated Uploads" on storage.objects; create policy "Authenticated Uploads" on storage.objects for insert with check ( bucket_id = 'hall-logos' and auth.role() = 'authenticated' ); drop policy if exists "Authenticated Updates" on storage.objects; create policy "Authenticated Updates" on storage.objects for update with check ( bucket_id = 'hall-logos' and auth.role() = 'authenticated' ); drop policy if exists "Authenticated Deletes" on storage.objects; create policy "Authenticated Deletes" on storage.objects for delete using ( bucket_id = 'hall-logos' and auth.role() = 'authenticated' ); -- Public Site Bucket Policies -- Allow public read SPECIFIC to this bucket DROP POLICY IF EXISTS "Public Access public-site" ON storage.objects; CREATE POLICY "Public Access public-site" ON storage.objects FOR SELECT TO public USING ( bucket_id = 'public-site' ); -- Allow authenticated upload/delete SPECIFIC to this bucket DROP POLICY IF EXISTS "Authenticated Insert public-site" ON storage.objects; CREATE POLICY "Authenticated Insert public-site" ON storage.objects FOR INSERT TO authenticated WITH CHECK ( bucket_id = 'public-site' ); DROP POLICY IF EXISTS "Authenticated Update public-site" ON storage.objects; CREATE POLICY "Authenticated Update public-site" ON storage.objects FOR UPDATE TO authenticated USING ( bucket_id = 'public-site' ); DROP POLICY IF EXISTS "Authenticated Delete public-site" ON storage.objects; CREATE POLICY "Authenticated Delete public-site" ON storage.objects FOR DELETE TO authenticated USING ( bucket_id = 'public-site' ); -- ========================================== -- 5. Schema Updates (Halls) -- ========================================== -- Add logo_url column to halls table alter table halls add column if not exists logo_url text; -- ========================================== -- 6. Schema Updates (Reservations) -- ========================================== -- Add price column to reservations table alter table reservations add column if not exists price decimal(10,2); -- Update existing reservations to set price from their package (snapshotting the price) update reservations set price = packages.price from packages where reservations.package_id = packages.id and reservations.price is null; -- Add groom_region and bride_region to reservations table ALTER TABLE reservations ADD COLUMN IF NOT EXISTS groom_region text, ADD COLUMN IF NOT EXISTS bride_region text; -- ========================================== -- 7. Schema Updates (Gallery) -- ========================================== ALTER TABLE public.gallery ADD COLUMN IF NOT EXISTS video_url TEXT; ALTER TABLE public.gallery ADD COLUMN IF NOT EXISTS is_hero BOOLEAN DEFAULT false; -- ========================================== -- 8. Additional Data (Site Contents) -- ========================================== INSERT INTO public.site_contents (key, value, type, section) VALUES ('contact_email', 'info@ruyadugun.com', 'text', 'contact') ON CONFLICT (key) DO NOTHING; INSERT INTO public.site_contents (key, value, type, section) VALUES ('social_instagram', 'https://instagram.com/', 'text', 'contact'), ('social_facebook', 'https://facebook.com/', 'text', 'contact'), ('social_twitter', 'https://twitter.com/', 'text', 'contact') ON CONFLICT (key) DO NOTHING; INSERT INTO public.site_contents (key, value, type, section) VALUES ('contact_map_embed', '', 'html', 'contact') ON CONFLICT (key) DO NOTHING;