aethex-forge/client/lib/arm-affiliation-service.ts
2025-11-15 16:38:40 +00:00

232 lines
6.5 KiB
TypeScript

import { supabase } from "./supabase";
export interface ArmAffiliation {
arm: "foundation" | "gameforge" | "labs" | "corp" | "devlink";
detected: boolean;
reason: string;
activityCount: number;
}
/**
* Detects which arms a user is affiliated with based on their activities
*/
export const armAffiliationService = {
/**
* Check if user has enrolled in any Foundation courses
*/
async hasFoundationActivity(
userId: string,
): Promise<{ detected: boolean; count: number }> {
try {
const { data: enrollments, error } = await supabase
.from("course_enrollments")
.select("id", { count: "exact" })
.eq("user_id", userId)
.limit(1);
if (error) throw error;
return {
detected: (enrollments?.length ?? 0) > 0,
count: enrollments?.length ?? 0,
};
} catch (error) {
console.error("Error checking Foundation activity:", error);
return { detected: false, count: 0 };
}
},
/**
* Check if user has GameForge projects/teams
*/
async hasGameForgeActivity(
userId: string,
): Promise<{ detected: boolean; count: number }> {
try {
const { data: projects, error: projectError } = await supabase
.from("gameforge_projects")
.select("id", { count: "exact" })
.eq("user_id", userId)
.limit(1);
if (projectError) throw projectError;
const { data: teams, error: teamError } = await supabase
.from("gameforge_team_members")
.select("id", { count: "exact" })
.eq("user_id", userId)
.limit(1);
if (teamError) throw teamError;
const totalCount = (projects?.length ?? 0) + (teams?.length ?? 0);
return { detected: totalCount > 0, count: totalCount };
} catch (error) {
console.error("Error checking GameForge activity:", error);
return { detected: false, count: 0 };
}
},
/**
* Check if user has Labs research activities
*/
async hasLabsActivity(
userId: string,
): Promise<{ detected: boolean; count: number }> {
try {
const { data: research, error } = await supabase
.from("labs_research_tracks")
.select("id", { count: "exact" })
.eq("user_id", userId)
.limit(1);
if (error) throw error;
return {
detected: (research?.length ?? 0) > 0,
count: research?.length ?? 0,
};
} catch (error) {
console.error("Error checking Labs activity:", error);
return { detected: false, count: 0 };
}
},
/**
* Check if user has Corp-related activities
*/
async hasCorpActivity(
userId: string,
): Promise<{ detected: boolean; count: number }> {
try {
// Corp activities could include partnerships, investments, or business accounts
const { data: accounts, error } = await supabase
.from("corp_accounts")
.select("id", { count: "exact" })
.eq("user_id", userId)
.limit(1);
if (error) throw error;
return {
detected: (accounts?.length ?? 0) > 0,
count: accounts?.length ?? 0,
};
} catch (error) {
// Corp table may not exist yet, return false
return { detected: false, count: 0 };
}
},
/**
* Check if user has DevLink/Roblox development activity
*/
async hasDevLinkActivity(
userId: string,
): Promise<{ detected: boolean; count: number }> {
try {
const { data: devLinkProfiles, error } = await supabase
.from("devlink_profiles")
.select("id", { count: "exact" })
.eq("user_id", userId)
.limit(1);
if (error && error.code !== "42P01") throw error; // Ignore table not found errors
return {
detected: (devLinkProfiles?.length ?? 0) > 0,
count: devLinkProfiles?.length ?? 0,
};
} catch (error) {
// Table may not exist yet
return { detected: false, count: 0 };
}
},
/**
* Detect all arm affiliations for a user
*/
async detectAffiliations(userId: string): Promise<ArmAffiliation[]> {
const [foundation, gameforge, labs, corp, devlink] = await Promise.all([
this.hasFoundationActivity(userId),
this.hasGameForgeActivity(userId),
this.hasLabsActivity(userId),
this.hasCorpActivity(userId),
this.hasDevLinkActivity(userId),
]);
return [
{
arm: "foundation",
detected: foundation.detected,
reason: foundation.detected
? `${foundation.count} course(s) enrolled`
: "No courses enrolled",
activityCount: foundation.count,
},
{
arm: "gameforge",
detected: gameforge.detected,
reason: gameforge.detected
? `${gameforge.count} project(s) and team(s)`
: "No projects or teams",
activityCount: gameforge.count,
},
{
arm: "labs",
detected: labs.detected,
reason: labs.detected
? `${labs.count} research track(s)`
: "No research tracks",
activityCount: labs.count,
},
{
arm: "corp",
detected: corp.detected,
reason: corp.detected
? `${corp.count} corp account(s)`
: "No corp accounts",
activityCount: corp.count,
},
{
arm: "devlink",
detected: devlink.detected,
reason: devlink.detected
? "DevLink profile created"
: "No DevLink profile",
activityCount: devlink.count,
},
];
},
/**
* Sync detected affiliations to user_arm_affiliations table
*/
async syncDetectedAffiliations(userId: string, token: string): Promise<void> {
try {
const detectedArms = await this.detectAffiliations(userId);
for (const affiliation of detectedArms) {
if (affiliation.detected) {
await fetch(
`${import.meta.env.VITE_API_BASE}/api/user/arm-affiliations`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
arm: affiliation.arm,
affiliation_type: "courses", // Generic type for auto-detected
affiliation_data: {
detected: true,
reason: affiliation.reason,
},
confirmed: false,
}),
},
);
}
}
} catch (error) {
console.error("Error syncing arm affiliations:", error);
}
},
};