mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
183fe0a93c
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
107 lines
4.9 KiB
TypeScript
107 lines
4.9 KiB
TypeScript
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.99.1";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("authorization") || "";
|
|
if (!authHeader.startsWith("Bearer ")) {
|
|
return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
|
}
|
|
|
|
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
|
|
const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;
|
|
const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
|
|
const callerClient = createClient(supabaseUrl, anonKey, { global: { headers: { Authorization: authHeader } } });
|
|
const adminClient = createClient(supabaseUrl, serviceRoleKey);
|
|
|
|
const { data: { user }, error: userError } = await callerClient.auth.getUser();
|
|
if (userError || !user?.id || !user.email) {
|
|
return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
|
}
|
|
|
|
const normalizedEmail = user.email.trim().toLowerCase();
|
|
const displayName = (user.user_metadata?.full_name as string | undefined) || normalizedEmail;
|
|
|
|
const { data: committees, error: committeeError } = await adminClient
|
|
.from("arc_committee_members")
|
|
.select("id, association_id, is_active")
|
|
.or(`user_id.eq.${user.id},email.ilike.${normalizedEmail}`);
|
|
if (committeeError) throw committeeError;
|
|
|
|
// Ensure caller has arc_member role if they're on any committee roster
|
|
if ((committees ?? []).some((c) => c.is_active)) {
|
|
const { data: existingRole } = await adminClient
|
|
.from("user_roles")
|
|
.select("role")
|
|
.eq("user_id", user.id)
|
|
.eq("role", "arc_member")
|
|
.maybeSingle();
|
|
if (!existingRole) {
|
|
await adminClient.from("user_roles").insert({ user_id: user.id, role: "arc_member" });
|
|
}
|
|
}
|
|
|
|
const { data: owners, error: ownersError } = await adminClient
|
|
.from("owners")
|
|
.select("association_id, first_name, last_name")
|
|
.eq("user_id", user.id)
|
|
.neq("status", "archived");
|
|
if (ownersError) throw ownersError;
|
|
|
|
const committeeAssociationIds = (committees ?? []).filter((c) => c.is_active).map((c) => c.association_id).filter(Boolean);
|
|
const allCommitteeAssociationIds = (committees ?? []).map((c) => c.association_id).filter(Boolean);
|
|
const ownerAssociationIds = (owners ?? []).map((o) => o.association_id).filter(Boolean);
|
|
const inactiveRosterIds = (committees ?? [])
|
|
.filter((c) => !c.is_active && ownerAssociationIds.includes(c.association_id))
|
|
.map((c) => c.id);
|
|
const missingRosterAssociationIds = ownerAssociationIds.filter((id) => !allCommitteeAssociationIds.includes(id));
|
|
|
|
if (inactiveRosterIds.length > 0) {
|
|
const { error: activateError } = await adminClient
|
|
.from("arc_committee_members")
|
|
.update({ is_active: true })
|
|
.in("id", inactiveRosterIds);
|
|
if (activateError) throw activateError;
|
|
}
|
|
|
|
if (missingRosterAssociationIds.length > 0) {
|
|
const ownerName = owners?.find((o) => o.first_name || o.last_name);
|
|
const name = ownerName ? `${ownerName.first_name || ""} ${ownerName.last_name || ""}`.trim() : displayName;
|
|
const rows = [...new Set(missingRosterAssociationIds)].map((association_id) => ({
|
|
association_id,
|
|
name,
|
|
email: normalizedEmail,
|
|
is_active: true,
|
|
role: "Member",
|
|
}));
|
|
const { error: insertError } = await adminClient.from("arc_committee_members").insert(rows);
|
|
if (insertError) throw insertError;
|
|
}
|
|
|
|
const associationIds = [...new Set([...committeeAssociationIds, ...ownerAssociationIds])];
|
|
if (!associationIds.length) {
|
|
return new Response(JSON.stringify({ applications: [], associationIds, noCommittee: true }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
|
}
|
|
|
|
const { data: applications, error: appError } = await adminClient
|
|
.from("arc_applications")
|
|
.select("*, associations(name), units(unit_number, address), owners(first_name, last_name, property_address)")
|
|
.in("association_id", associationIds)
|
|
.order("created_at", { ascending: false });
|
|
if (appError) throw appError;
|
|
|
|
return new Response(JSON.stringify({ applications: applications ?? [], associationIds, noCommittee: false }), {
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
});
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : "Failed to load ARC reviews";
|
|
return new Response(JSON.stringify({ error: message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });
|
|
}
|
|
}); |