AeThex-OS/server/dashboard.ts
MrPiglr ad5f15271e fix: resolve 55+ TypeScript errors and cleanup codebase
- Create server/auth.ts with requireAuth, optionalAuth, requireAdmin middleware
- Fix os.tsx: add Target/Check imports, fix useLayout->usePlatformLayout, fix achievements types
- Fix game-routes.ts: add all Request/Response types, fix session access
- Fix revenue.ts: org_id -> organization_id
- Fix votes.ts: currentSplit scope, created_by type
- Fix dashboard.ts: remove unsupported .distinct() method
- Fix game-dev-apis.ts: header/body type assertions
- Upgrade api/execute.ts: add Python simulation, JSON validation, HTML/CSS passthrough
- Upgrade app-registry.ts: full implementation with 15 apps, RBAC, categories
- Clean up Java heap error logs
2026-02-03 02:31:34 -07:00

322 lines
8.8 KiB
TypeScript

import { supabase } from "./supabase";
interface EarningsResult {
success: boolean;
data?: any;
error?: string;
}
/**
* Get all earnings for a user across all projects
*/
export async function getUserEarnings(userId: string): Promise<EarningsResult> {
try {
// Get all split allocations for this user
const { data: allocations, error: allocError } = await supabase
.from("split_allocations")
.select("*")
.eq("user_id", userId)
.order("created_at", { ascending: false });
if (allocError) {
return {
success: false,
error: `Failed to fetch allocations: ${allocError.message}`,
};
}
// Group by project and sum amounts
const earningsByProject: Record<string, any> = {};
let totalEarned = 0;
(allocations || []).forEach((alloc: any) => {
const projectId = alloc.project_id;
const amount = parseFloat(alloc.allocated_amount);
totalEarned += amount;
if (!earningsByProject[projectId]) {
earningsByProject[projectId] = {
project_id: projectId,
total_earned: 0,
allocation_count: 0,
recent_allocations: [],
};
}
earningsByProject[projectId].total_earned += amount;
earningsByProject[projectId].allocation_count += 1;
// Keep last 5 allocations per project
if (
earningsByProject[projectId].recent_allocations.length < 5
) {
earningsByProject[projectId].recent_allocations.push({
amount: alloc.allocated_amount,
percentage: alloc.allocated_percentage,
revenue_event_id: alloc.revenue_event_id,
created_at: alloc.created_at,
});
}
});
// Get escrow balances for each project
const projectIds = Object.keys(earningsByProject);
const { data: escrowAccounts, error: escrowError } = await supabase
.from("escrow_accounts")
.select("*")
.eq("user_id", userId);
if (escrowError) {
console.error("Escrow fetch error:", escrowError);
}
// Attach escrow data to projects
(escrowAccounts || []).forEach((escrow: any) => {
if (earningsByProject[escrow.project_id]) {
earningsByProject[escrow.project_id].escrow_balance =
escrow.balance;
earningsByProject[escrow.project_id].escrow_held = escrow.held_amount;
earningsByProject[escrow.project_id].escrow_released =
escrow.released_amount;
}
});
const projects = Object.values(earningsByProject).sort(
(a: any, b: any) => b.total_earned - a.total_earned
);
return {
success: true,
data: {
user_id: userId,
total_earned_all_projects: totalEarned.toFixed(2),
projects_count: projects.length,
projects,
},
};
} catch (err: any) {
return {
success: false,
error: `Unexpected error: ${err.message}`,
};
}
}
/**
* Get earnings for a user on a specific project
*/
export async function getProjectEarnings(
userId: string,
projectId: string
): Promise<EarningsResult> {
try {
// Get all allocations for this user on this project
const { data: allocations, error: allocError } = await supabase
.from("split_allocations")
.select("*")
.eq("user_id", userId)
.eq("project_id", projectId)
.order("created_at", { ascending: false });
if (allocError) {
return {
success: false,
error: `Failed to fetch allocations: ${allocError.message}`,
};
}
let totalEarned = 0;
const allocations_list = (allocations || []).map((alloc: any) => {
const amount = parseFloat(alloc.allocated_amount);
totalEarned += amount;
return {
amount: alloc.allocated_amount,
percentage: alloc.allocated_percentage,
revenue_event_id: alloc.revenue_event_id,
split_version: alloc.split_version,
created_at: alloc.created_at,
};
});
// Get escrow balance for this project
const { data: escrow, error: escrowError } = await supabase
.from("escrow_accounts")
.select("*")
.eq("user_id", userId)
.eq("project_id", projectId)
.maybeSingle();
if (escrowError) {
console.error("Escrow fetch error:", escrowError);
}
// Get current split rule to show latest allocation percentage
const { data: currentSplit } = await supabase
.from("revenue_splits")
.select("rule")
.eq("project_id", projectId)
.is("active_until", null)
.maybeSingle();
const userAllocationPercent = currentSplit?.rule?.[userId] || 0;
return {
success: true,
data: {
user_id: userId,
project_id: projectId,
total_earned: totalEarned.toFixed(2),
allocation_count: allocations_list.length,
current_allocation_percent: (userAllocationPercent * 100).toFixed(2),
escrow_balance: escrow?.balance || "0.00",
escrow_held: escrow?.held_amount || "0.00",
escrow_released: escrow?.released_amount || "0.00",
recent_allocations: allocations_list.slice(0, 10),
},
};
} catch (err: any) {
return {
success: false,
error: `Unexpected error: ${err.message}`,
};
}
}
/**
* Get summary statistics for a user's earnings
*/
export async function getEarningsSummary(userId: string): Promise<EarningsResult> {
try {
// Total earned across all projects
const { data: allocations, error: allocError } = await supabase
.from("split_allocations")
.select("allocated_amount")
.eq("user_id", userId);
if (allocError) {
return {
success: false,
error: `Failed to fetch allocations: ${allocError.message}`,
};
}
const totalEarned = (allocations || []).reduce(
(sum: number, alloc: any) => sum + parseFloat(alloc.allocated_amount),
0
);
// Total in escrow (across all projects)
const { data: escrowAccounts, error: escrowError } = await supabase
.from("escrow_accounts")
.select("balance, held_amount, released_amount")
.eq("user_id", userId);
if (escrowError) {
console.error("Escrow fetch error:", escrowError);
}
const totalEscrowBalance = (escrowAccounts || []).reduce(
(sum: number, escrow: any) => sum + parseFloat(escrow.balance),
0
);
const totalHeld = (escrowAccounts || []).reduce(
(sum: number, escrow: any) => sum + parseFloat(escrow.held_amount),
0
);
const totalReleased = (escrowAccounts || []).reduce(
(sum: number, escrow: any) => sum + parseFloat(escrow.released_amount),
0
);
// Count of projects user has earned from
const { data: projects, error: projectsError } = await supabase
.from("split_allocations")
.select("project_id")
.eq("user_id", userId);
if (projectsError) {
console.error("Projects fetch error:", projectsError);
}
// Get unique project count
const uniqueProjects = new Set((projects || []).map((p: any) => p.project_id));
return {
success: true,
data: {
user_id: userId,
total_earned: totalEarned.toFixed(2),
total_in_escrow: totalEscrowBalance.toFixed(2),
total_held_pending: totalHeld.toFixed(2),
total_paid_out: totalReleased.toFixed(2),
projects_earned_from: uniqueProjects.size,
},
};
} catch (err: any) {
return {
success: false,
error: `Unexpected error: ${err.message}`,
};
}
}
/**
* Get leaderboard - top earners on a project
*/
export async function getProjectLeaderboard(
projectId: string,
limit = 20
): Promise<EarningsResult> {
try {
// Get allocations grouped by user
const { data: allocations, error } = await supabase
.from("split_allocations")
.select("user_id, allocated_amount")
.eq("project_id", projectId);
if (error) {
return {
success: false,
error: `Failed to fetch allocations: ${error.message}`,
};
}
const earnerMap: Record<string, number> = {};
(allocations || []).forEach((alloc: any) => {
if (!earnerMap[alloc.user_id]) {
earnerMap[alloc.user_id] = 0;
}
earnerMap[alloc.user_id] += parseFloat(alloc.allocated_amount);
});
const leaderboard = Object.entries(earnerMap)
.map(([userId, totalEarned]) => ({
rank: 0, // Will be set below
user_id: userId,
total_earned: totalEarned.toFixed(2),
}))
.sort((a, b) => parseFloat(b.total_earned) - parseFloat(a.total_earned))
.slice(0, limit)
.map((entry, index) => ({
...entry,
rank: index + 1,
}));
return {
success: true,
data: {
project_id: projectId,
leaderboard,
count: leaderboard.length,
},
};
} catch (err: any) {
return {
success: false,
error: `Unexpected error: ${err.message}`,
};
}
}