Files
gridpilot.gg/apps/api/src/domain/admin/use-cases/GetDashboardStatsUseCase.ts
Marc Mintel 1b0a1f4aee
Some checks failed
Contract Testing / contract-tests (pull_request) Failing after 7m11s
Contract Testing / contract-snapshot (pull_request) Has been skipped
view data fixes
2026-01-24 23:29:55 +01:00

184 lines
6.0 KiB
TypeScript

import type { AdminUser } from '@core/admin/domain/entities/AdminUser';
import type { AdminUserRepository } from '@core/admin/domain/repositories/AdminUserRepository';
import { AuthorizationService } from '@core/admin/domain/services/AuthorizationService';
import { UserId } from '@core/admin/domain/value-objects/UserId';
import type { UserRole } from '@core/admin/domain/value-objects/UserRole';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
export interface DashboardStatsResult {
totalUsers: number;
activeUsers: number;
suspendedUsers: number;
deletedUsers: number;
systemAdmins: number;
recentLogins: number;
newUsersToday: number;
userGrowth: {
label: string;
value: number;
color: string;
}[];
roleDistribution: {
label: string;
value: number;
color: string;
}[];
statusDistribution: {
active: number;
suspended: number;
deleted: number;
};
activityTimeline: {
date: string;
newUsers: number;
logins: number;
}[];
}
export type GetDashboardStatsInput = {
actorId: string;
};
export type GetDashboardStatsErrorCode = 'AUTHORIZATION_ERROR' | 'REPOSITORY_ERROR';
export type GetDashboardStatsApplicationError = ApplicationErrorCode<GetDashboardStatsErrorCode, { message: string; details?: unknown }>;
export class GetDashboardStatsUseCase {
constructor(
private readonly adminUserRepo: AdminUserRepository,
) {}
async execute(input: GetDashboardStatsInput): Promise<Result<DashboardStatsResult, GetDashboardStatsApplicationError>> {
try {
// Get actor (current user)
const actor = await this.adminUserRepo.findById(UserId.fromString(input.actorId));
if (!actor) {
return Result.err({
code: 'AUTHORIZATION_ERROR',
details: { message: 'Actor not found' },
});
}
// Check authorization
if (!AuthorizationService.canListUsers(actor)) {
return Result.err({
code: 'AUTHORIZATION_ERROR',
details: { message: 'User is not authorized to view dashboard' },
});
}
// Get all users
const allUsersResult = await this.adminUserRepo.list();
const allUsers = allUsersResult.users;
// Calculate basic stats
const totalUsers = allUsers.length;
const activeUsers = allUsers.filter((u: AdminUser) => u.status.value === 'active').length;
const suspendedUsers = allUsers.filter((u: AdminUser) => u.status.value === 'suspended').length;
const deletedUsers = allUsers.filter((u: AdminUser) => u.status.value === 'deleted').length;
const systemAdmins = allUsers.filter((u: AdminUser) => u.isSystemAdmin()).length;
// Recent logins (last 24 hours)
const oneDayAgo = new Date();
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
const recentLogins = allUsers.filter((u: AdminUser) => u.lastLoginAt && u.lastLoginAt > oneDayAgo).length;
// New users today
const today = new Date();
today.setHours(0, 0, 0, 0);
const newUsersToday = allUsers.filter((u: AdminUser) => u.createdAt > today).length;
// Role distribution
const roleCounts: Record<string, number> = {};
allUsers.forEach((user: AdminUser) => {
user.roles.forEach((role: UserRole) => {
const roleValue = role.value;
roleCounts[roleValue] = (roleCounts[roleValue] || 0) + 1;
});
});
const roleDistribution = Object.entries(roleCounts).map(([role, count]) => ({
label: role.charAt(0).toUpperCase() + role.slice(1),
value: count,
color: role === 'owner' ? 'text-purple-500' : role === 'admin' ? 'text-blue-500' : 'text-gray-500',
}));
// User growth (last 7 days)
const userGrowth: DashboardStatsResult['userGrowth'] = [];
if (allUsers.length > 0) {
for (let i = 6; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
const count = allUsers.filter((u: AdminUser) => {
const userDate = u.createdAt;
return userDate.toDateString() === date.toDateString();
}).length;
userGrowth.push({
label: dateStr,
value: count,
color: 'text-primary-blue',
});
}
}
// Activity timeline (last 7 days)
const activityTimeline: DashboardStatsResult['activityTimeline'] = [];
if (allUsers.length > 0) {
for (let i = 6; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
const newUsers = allUsers.filter((u: AdminUser) => {
const userDate = u.createdAt;
return userDate.toDateString() === date.toDateString();
}).length;
const logins = allUsers.filter((u: AdminUser) => {
const loginDate = u.lastLoginAt;
return loginDate && loginDate.toDateString() === date.toDateString();
}).length;
activityTimeline.push({
date: dateStr,
newUsers,
logins,
});
}
}
const result: DashboardStatsResult = {
totalUsers,
activeUsers,
suspendedUsers,
deletedUsers,
systemAdmins,
recentLogins,
newUsersToday,
userGrowth,
roleDistribution,
statusDistribution: {
active: activeUsers,
suspended: suspendedUsers,
deleted: deletedUsers,
},
activityTimeline,
};
return Result.ok(result);
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to get dashboard stats';
return Result.err({
code: 'REPOSITORY_ERROR',
details: { message },
});
}
}
}