-- Leave of Absence table DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'leave_type') THEN CREATE TYPE leave_type AS ENUM ( 'emergency_leave', 'parental_leave', 'sick_leave', 'vacation_leave' ); END IF; END $$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'leave_status') THEN CREATE TYPE leave_status AS ENUM ( 'pending', 'approved', 'rejected', 'cancelled' ); END IF; END $$; CREATE TABLE IF NOT EXISTS leave_of_absence ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, leave_type leave_type NOT NULL, justification TEXT NOT NULL, start_time TIMESTAMPTZ NOT NULL, end_time TIMESTAMPTZ NOT NULL, status leave_status NOT NULL DEFAULT 'pending', filed_by UUID NOT NULL REFERENCES auth.users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT valid_time_range CHECK (end_time > start_time) ); -- Indexes CREATE INDEX IF NOT EXISTS idx_leave_user_id ON leave_of_absence(user_id); CREATE INDEX IF NOT EXISTS idx_leave_start_time ON leave_of_absence(start_time); CREATE INDEX IF NOT EXISTS idx_leave_status ON leave_of_absence(status); -- RLS ALTER TABLE leave_of_absence ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Privileged users can view all leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Users can view own leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Privileged users can file own leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Users can file own leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Admins can approve or reject leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Users can cancel own future approved leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Admins can cancel future approved leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Privileged users can update leaves" ON leave_of_absence; DROP POLICY IF EXISTS "Users can cancel own pending leaves" ON leave_of_absence; -- Admin/dispatcher/it_staff can see all leaves CREATE POLICY "Privileged users can view all leaves" ON leave_of_absence FOR SELECT USING ( EXISTS ( SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role IN ('admin', 'dispatcher', 'it_staff') ) ); -- All users can see their own leaves CREATE POLICY "Users can view own leaves" ON leave_of_absence FOR SELECT USING (user_id = auth.uid()); -- Only admin/dispatcher/it_staff can file leaves (for themselves) CREATE POLICY "Privileged users can file own leaves" ON leave_of_absence FOR INSERT WITH CHECK ( user_id = auth.uid() AND filed_by = auth.uid() AND EXISTS ( SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role IN ('admin', 'dispatcher', 'it_staff') ) ); -- Only admins can approve/reject pending leaves CREATE POLICY "Admins can approve or reject leaves" ON leave_of_absence FOR UPDATE USING ( status = 'pending' AND EXISTS ( SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin' ) ) WITH CHECK ( status IN ('approved', 'rejected') ); -- Users can cancel their own future approved leaves CREATE POLICY "Users can cancel own future approved leaves" ON leave_of_absence FOR UPDATE USING ( user_id = auth.uid() AND status = 'approved' AND start_time > now() ) WITH CHECK ( status = 'cancelled' ); -- Admins can cancel future approved leaves CREATE POLICY "Admins can cancel future approved leaves" ON leave_of_absence FOR UPDATE USING ( status = 'approved' AND start_time > now() AND EXISTS ( SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin' ) ) WITH CHECK ( status = 'cancelled' ); -- Drop legacy reason column if it exists from an earlier migration ALTER TABLE leave_of_absence DROP COLUMN IF EXISTS reason; -- Updated_at trigger CREATE OR REPLACE FUNCTION update_leave_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trigger_leave_updated_at ON leave_of_absence; CREATE TRIGGER trigger_leave_updated_at BEFORE UPDATE ON leave_of_absence FOR EACH ROW EXECUTE FUNCTION update_leave_updated_at();