Announcements and IT Job Checklist
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
-- ============================================================
|
||||
-- Announcements + Comments tables
|
||||
-- ============================================================
|
||||
|
||||
-- 1. announcements table
|
||||
CREATE TABLE IF NOT EXISTS public.announcements (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
author_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
title text NOT NULL,
|
||||
body text NOT NULL,
|
||||
visible_roles text[] NOT NULL DEFAULT ARRAY['admin','dispatcher','programmer','it_staff']::text[],
|
||||
is_template boolean NOT NULL DEFAULT false,
|
||||
template_id uuid REFERENCES public.announcements(id) ON DELETE SET NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
ALTER TABLE public.announcements ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.announcements REPLICA IDENTITY FULL;
|
||||
|
||||
-- RLS: SELECT — user can see if their role is in visible_roles OR they are the author
|
||||
CREATE POLICY "Announcements: select" ON public.announcements
|
||||
FOR SELECT
|
||||
USING (
|
||||
author_id = auth.uid()
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM public.profiles p
|
||||
WHERE p.id = auth.uid()
|
||||
AND p.role::text = ANY(visible_roles)
|
||||
)
|
||||
);
|
||||
|
||||
-- RLS: INSERT — admin, dispatcher, programmer, it_staff only
|
||||
CREATE POLICY "Announcements: insert" ON public.announcements
|
||||
FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.profiles p
|
||||
WHERE p.id = auth.uid()
|
||||
AND p.role IN ('admin', 'dispatcher', 'programmer', 'it_staff')
|
||||
)
|
||||
);
|
||||
|
||||
-- RLS: UPDATE — only the author or admin
|
||||
CREATE POLICY "Announcements: update" ON public.announcements
|
||||
FOR UPDATE
|
||||
USING (
|
||||
author_id = auth.uid()
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM public.profiles p
|
||||
WHERE p.id = auth.uid() AND p.role = 'admin'
|
||||
)
|
||||
);
|
||||
|
||||
-- RLS: DELETE — only the author or admin
|
||||
CREATE POLICY "Announcements: delete" ON public.announcements
|
||||
FOR DELETE
|
||||
USING (
|
||||
author_id = auth.uid()
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM public.profiles p
|
||||
WHERE p.id = auth.uid() AND p.role = 'admin'
|
||||
)
|
||||
);
|
||||
|
||||
-- Index for efficient queries
|
||||
CREATE INDEX IF NOT EXISTS idx_announcements_created_at ON public.announcements (created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_announcements_visible_roles ON public.announcements USING GIN (visible_roles);
|
||||
|
||||
-- 2. announcement_comments table
|
||||
CREATE TABLE IF NOT EXISTS public.announcement_comments (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
announcement_id uuid NOT NULL REFERENCES public.announcements(id) ON DELETE CASCADE,
|
||||
author_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
body text NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
ALTER TABLE public.announcement_comments ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.announcement_comments REPLICA IDENTITY FULL;
|
||||
|
||||
-- RLS: SELECT — can read comments if user can see the parent announcement
|
||||
CREATE POLICY "Announcement comments: select" ON public.announcement_comments
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.announcements a
|
||||
WHERE a.id = announcement_id
|
||||
AND (
|
||||
a.author_id = auth.uid()
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM public.profiles p
|
||||
WHERE p.id = auth.uid()
|
||||
AND p.role::text = ANY(a.visible_roles)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
-- RLS: INSERT — any authenticated user who can see the announcement
|
||||
CREATE POLICY "Announcement comments: insert" ON public.announcement_comments
|
||||
FOR INSERT
|
||||
WITH CHECK (
|
||||
author_id = auth.uid()
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.announcements a
|
||||
WHERE a.id = announcement_id
|
||||
AND (
|
||||
a.author_id = auth.uid()
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM public.profiles p
|
||||
WHERE p.id = auth.uid()
|
||||
AND p.role::text = ANY(a.visible_roles)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
-- RLS: DELETE — only the comment author or admin
|
||||
CREATE POLICY "Announcement comments: delete" ON public.announcement_comments
|
||||
FOR DELETE
|
||||
USING (
|
||||
author_id = auth.uid()
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM public.profiles p
|
||||
WHERE p.id = auth.uid() AND p.role = 'admin'
|
||||
)
|
||||
);
|
||||
|
||||
-- Index for efficient comment lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_announcement_comments_announcement_id
|
||||
ON public.announcement_comments (announcement_id, created_at);
|
||||
|
||||
-- 3. Extend notifications table with announcement_id FK
|
||||
ALTER TABLE public.notifications
|
||||
ADD COLUMN IF NOT EXISTS announcement_id uuid REFERENCES public.announcements(id) ON DELETE CASCADE;
|
||||
|
||||
-- 4. Enable realtime for both tables
|
||||
ALTER PUBLICATION supabase_realtime ADD TABLE public.announcements;
|
||||
ALTER PUBLICATION supabase_realtime ADD TABLE public.announcement_comments;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- ============================================================
|
||||
-- Add IT Job printed tracking columns to tasks table
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE public.tasks
|
||||
ADD COLUMN IF NOT EXISTS it_job_printed boolean NOT NULL DEFAULT false;
|
||||
|
||||
ALTER TABLE public.tasks
|
||||
ADD COLUMN IF NOT EXISTS it_job_printed_at timestamptz;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- ============================================================
|
||||
-- Track who (dispatcher/admin) marked the IT Job as received
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE public.tasks
|
||||
ADD COLUMN IF NOT EXISTS it_job_received_by_id uuid
|
||||
REFERENCES auth.users(id) ON DELETE SET NULL;
|
||||
@@ -0,0 +1,58 @@
|
||||
-- ============================================================
|
||||
-- SECURITY DEFINER RPCs for toggling IT Job printed status.
|
||||
-- The tasks table RLS UPDATE policies restrict who can update
|
||||
-- rows, but dispatchers/admins need to set it_job_printed
|
||||
-- regardless of task ownership. SECURITY DEFINER bypasses RLS
|
||||
-- while still validating the caller's role.
|
||||
-- ============================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.mark_it_job_printed(
|
||||
p_task_id uuid,
|
||||
p_receiver_id uuid
|
||||
)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM profiles
|
||||
WHERE id = auth.uid()
|
||||
AND role IN ('admin', 'dispatcher', 'programmer')
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Only admin or dispatcher can mark IT Job as received';
|
||||
END IF;
|
||||
|
||||
UPDATE tasks
|
||||
SET it_job_printed = true,
|
||||
it_job_printed_at = now(),
|
||||
it_job_received_by_id = p_receiver_id
|
||||
WHERE id = p_task_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.unmark_it_job_printed(
|
||||
p_task_id uuid
|
||||
)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM profiles
|
||||
WHERE id = auth.uid()
|
||||
AND role IN ('admin', 'dispatcher', 'programmer')
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Only admin or dispatcher can unmark IT Job as received';
|
||||
END IF;
|
||||
|
||||
UPDATE tasks
|
||||
SET it_job_printed = false,
|
||||
it_job_printed_at = null,
|
||||
it_job_received_by_id = null
|
||||
WHERE id = p_task_id;
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- ============================================================
|
||||
-- Grant EXECUTE on IT Job RPCs to the authenticated role and
|
||||
-- enable REPLICA IDENTITY FULL on tasks for realtime updates.
|
||||
-- ============================================================
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.mark_it_job_printed(uuid, uuid) TO authenticated;
|
||||
GRANT EXECUTE ON FUNCTION public.unmark_it_job_printed(uuid) TO authenticated;
|
||||
|
||||
-- Enable REPLICA IDENTITY FULL on the tasks table so that
|
||||
-- Supabase Realtime UPDATE events carry the full new row data,
|
||||
-- including the newly added it_job_printed / it_job_printed_at /
|
||||
-- it_job_received_by_id columns.
|
||||
ALTER TABLE public.tasks REPLICA IDENTITY FULL;
|
||||
Reference in New Issue
Block a user