-- User Badges Migration for BroadcastBeat -- Adds earned badges (Contributor, Mentor, Expert, Moderator) based on reputation and activity -- ============================================================ -- 1. USER BADGES TABLE -- ============================================================ CREATE TABLE IF NOT EXISTS public.user_badges ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES public.user_profiles(id) ON DELETE CASCADE, badge_type TEXT NOT NULL CHECK (badge_type IN ('contributor', 'mentor', 'expert', 'moderator')), awarded_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, awarded_reason TEXT ); CREATE UNIQUE INDEX IF NOT EXISTS idx_user_badges_unique ON public.user_badges (user_id, badge_type); CREATE INDEX IF NOT EXISTS idx_user_badges_user_id ON public.user_badges (user_id); -- ============================================================ -- 2. FUNCTION: EVALUATE AND AWARD BADGES -- ============================================================ CREATE OR REPLACE FUNCTION public.evaluate_user_badges(p_user_id UUID) RETURNS VOID LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_reputation INTEGER; v_thread_count INTEGER; v_reply_count INTEGER; v_upvotes_received INTEGER; v_is_moderator BOOLEAN := false; BEGIN -- Get reputation SELECT COALESCE(reputation, 0) INTO v_reputation FROM public.user_profiles WHERE id = p_user_id; -- Get thread count SELECT COUNT(*) INTO v_thread_count FROM public.forum_threads WHERE author_id = p_user_id; -- Get reply count SELECT COUNT(*) INTO v_reply_count FROM public.forum_replies WHERE author_id = p_user_id; -- Get total upvotes received SELECT COALESCE(SUM(upvotes), 0) INTO v_upvotes_received FROM ( SELECT upvotes FROM public.forum_threads WHERE author_id = p_user_id UNION ALL SELECT upvotes FROM public.forum_replies WHERE author_id = p_user_id ) AS all_posts; -- Check moderator role (if user_roles table exists) IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user_roles' ) THEN SELECT EXISTS( SELECT 1 FROM public.user_roles WHERE user_id = p_user_id AND role IN ('moderator', 'admin') ) INTO v_is_moderator; END IF; -- Award CONTRIBUTOR badge: reputation >= 100 OR (threads >= 5 AND replies >= 10) IF v_reputation >= 100 OR (v_thread_count >= 5 AND v_reply_count >= 10) THEN INSERT INTO public.user_badges (user_id, badge_type, awarded_reason) VALUES (p_user_id, 'contributor', 'Reached 100 reputation or posted 5+ threads and 10+ replies') ON CONFLICT (user_id, badge_type) DO NOTHING; END IF; -- Award MENTOR badge: reputation >= 300 OR upvotes_received >= 50 IF v_reputation >= 300 OR v_upvotes_received >= 50 THEN INSERT INTO public.user_badges (user_id, badge_type, awarded_reason) VALUES (p_user_id, 'mentor', 'Reached 300 reputation or received 50+ upvotes') ON CONFLICT (user_id, badge_type) DO NOTHING; END IF; -- Award EXPERT badge: reputation >= 1000 OR (upvotes_received >= 200 AND thread_count >= 20) IF v_reputation >= 1000 OR (v_upvotes_received >= 200 AND v_thread_count >= 20) THEN INSERT INTO public.user_badges (user_id, badge_type, awarded_reason) VALUES (p_user_id, 'expert', 'Reached 1000 reputation or received 200+ upvotes with 20+ threads') ON CONFLICT (user_id, badge_type) DO NOTHING; END IF; -- Award MODERATOR badge: has moderator/admin role IF v_is_moderator THEN INSERT INTO public.user_badges (user_id, badge_type, awarded_reason) VALUES (p_user_id, 'moderator', 'Assigned moderator or admin role') ON CONFLICT (user_id, badge_type) DO NOTHING; END IF; END; $$; -- ============================================================ -- 3. TRIGGER FUNCTION: AUTO-EVALUATE BADGES ON REPUTATION CHANGE -- ============================================================ CREATE OR REPLACE FUNCTION public.trigger_evaluate_badges() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN PERFORM public.evaluate_user_badges(NEW.id); RETURN NEW; END; $$; -- ============================================================ -- 4. ENABLE RLS -- ============================================================ ALTER TABLE public.user_badges ENABLE ROW LEVEL SECURITY; -- ============================================================ -- 5. RLS POLICIES -- ============================================================ DROP POLICY IF EXISTS "public_read_user_badges" ON public.user_badges; CREATE POLICY "public_read_user_badges" ON public.user_badges FOR SELECT TO public USING (true); DROP POLICY IF EXISTS "service_role_manage_user_badges" ON public.user_badges; CREATE POLICY "service_role_manage_user_badges" ON public.user_badges FOR ALL TO service_role USING (true) WITH CHECK (true); -- ============================================================ -- 6. TRIGGER: EVALUATE BADGES WHEN REPUTATION CHANGES -- ============================================================ DROP TRIGGER IF EXISTS on_reputation_change_evaluate_badges ON public.user_profiles; CREATE TRIGGER on_reputation_change_evaluate_badges AFTER UPDATE OF reputation ON public.user_profiles FOR EACH ROW WHEN (OLD.reputation IS DISTINCT FROM NEW.reputation) EXECUTE FUNCTION public.trigger_evaluate_badges(); -- ============================================================ -- 7. BACKFILL BADGES FOR EXISTING USERS -- ============================================================ DO $$ DECLARE v_user_id UUID; BEGIN IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'user_profiles' ) THEN FOR v_user_id IN SELECT id FROM public.user_profiles LOOP PERFORM public.evaluate_user_badges(v_user_id); END LOOP; RAISE NOTICE 'Badge backfill completed for all existing users.'; END IF; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'Badge backfill failed: %', SQLERRM; END $$;