create table if not exists public.pdf_quotes ( id uuid primary key default gen_random_uuid(), workspace_id uuid not null references public.workspaces(id) on delete cascade, document_id uuid not null references public.documents(id) on delete cascade, asset_id uuid not null references public.media_assets(id) on delete cascade, highlight_id text not null, target_url text not null, quote_text text not null, page integer not null check (page > 0), rects jsonb not null default '[]'::jsonb, file_url text, source_title text, created_by uuid references public.profiles(id) on delete set null default auth.uid(), created_at timestamptz not null default timezone('utc', now()), updated_at timestamptz not null default timezone('utc', now()) ); create unique index if not exists pdf_quotes_asset_highlight_idx on public.pdf_quotes (asset_id, highlight_id); create index if not exists pdf_quotes_workspace_idx on public.pdf_quotes (workspace_id, created_at desc); create index if not exists pdf_quotes_asset_idx on public.pdf_quotes (asset_id, created_at desc); create or replace function public.set_pdf_quotes_updated_at() returns trigger language plpgsql security invoker as $$ begin new.updated_at := timezone('utc', now()); return new; end; $$; drop trigger if exists set_pdf_quotes_updated_at on public.pdf_quotes; create trigger set_pdf_quotes_updated_at before update on public.pdf_quotes for each row execute procedure public.set_pdf_quotes_updated_at(); create or replace function public.sync_pdf_quote_meta() returns trigger language plpgsql security invoker as $$ declare v_asset record; begin select workspace_id, document_id, file_url into v_asset from public.media_assets where id = new.asset_id; if not found then raise exception 'Media asset % not found when creating PDF quote', new.asset_id; end if; new.workspace_id := v_asset.workspace_id; new.document_id := v_asset.document_id; if new.file_url is null then new.file_url := v_asset.file_url; end if; return new; end; $$; drop trigger if exists sync_pdf_quote_meta on public.pdf_quotes; create trigger sync_pdf_quote_meta before insert or update on public.pdf_quotes for each row execute procedure public.sync_pdf_quote_meta(); alter table public.pdf_quotes enable row level security; drop policy if exists "Select pdf quotes within workspace" on public.pdf_quotes; create policy "Select pdf quotes within workspace" on public.pdf_quotes for select using ( exists ( select 1 from public.workspace_members wm where wm.workspace_id = pdf_quotes.workspace_id and wm.user_id = auth.uid() ) ); drop policy if exists "Insert pdf quotes within workspace" on public.pdf_quotes; create policy "Insert pdf quotes within workspace" on public.pdf_quotes for insert with check ( exists ( select 1 from public.workspace_members wm where wm.workspace_id = pdf_quotes.workspace_id and wm.user_id = auth.uid() ) ); drop policy if exists "Update pdf quotes within workspace" on public.pdf_quotes; create policy "Update pdf quotes within workspace" on public.pdf_quotes for update using ( exists ( select 1 from public.workspace_members wm where wm.workspace_id = pdf_quotes.workspace_id and wm.user_id = auth.uid() ) ) with check ( exists ( select 1 from public.workspace_members wm where wm.workspace_id = pdf_quotes.workspace_id and wm.user_id = auth.uid() ) ); drop policy if exists "Delete pdf quotes within workspace" on public.pdf_quotes; create policy "Delete pdf quotes within workspace" on public.pdf_quotes for delete using ( exists ( select 1 from public.workspace_members wm where wm.workspace_id = pdf_quotes.workspace_id and wm.user_id = auth.uid() ) );