mission-control/lib/data/projects.ts

134 lines
4.4 KiB
TypeScript

import { Task, fetchAllTasks } from "./tasks";
import { fetchGanttSnapshot } from "@/lib/data/gantt-snapshot";
export interface Project {
id: string;
name: string;
description?: string;
color: string;
createdAt: string;
}
export interface Sprint {
id: string;
name: string;
status: "planning" | "active" | "completed" | "cancelled";
startDate: string;
endDate: string;
projectId: string;
goal?: string;
}
export interface ProjectStats {
project: Project;
totalTasks: number;
completedTasks: number;
inProgressTasks: number;
urgentTasks: number;
highPriorityTasks: number;
progress: number;
recentTasks: Task[];
}
export async function fetchAllProjects(): Promise<Project[]> {
const snapshot = await fetchGanttSnapshot();
return snapshot.projects;
}
export async function countProjects(): Promise<number> {
const projects = await fetchAllProjects();
return projects.length;
}
export async function fetchAllSprints(): Promise<Sprint[]> {
const snapshot = await fetchGanttSnapshot();
return snapshot.sprints;
}
export async function fetchProjectSprints(projectId: string): Promise<Sprint[]> {
const sprints = await fetchAllSprints();
return sprints.filter((sprint) => sprint.projectId === projectId);
}
export async function fetchActiveSprint(): Promise<Sprint | null> {
const sprints = await fetchAllSprints();
const active = sprints.find((sprint) => sprint.status === "active");
return active || null;
}
export async function countSprintsByStatus(): Promise<{ planning: number; active: number; completed: number; total: number }> {
const sprints = await fetchAllSprints();
const counts = { planning: 0, active: 0, completed: 0, total: sprints.length };
for (const sprint of sprints) {
if (sprint.status === "planning") counts.planning++;
else if (sprint.status === "active") counts.active++;
else if (sprint.status === "completed") counts.completed++;
}
return counts;
}
export function calculateProjectStats(project: Project, tasks: Task[]): ProjectStats {
const projectTasks = tasks.filter((task) => task.projectId === project.id);
const completedTasks = projectTasks.filter((task) => task.status === "done");
const inProgressTasks = projectTasks.filter((task) => task.status === "in-progress");
const urgentTasks = projectTasks.filter((task) => task.priority === "urgent" && task.status !== "done");
const highPriorityTasks = projectTasks.filter((task) => task.priority === "high" && task.status !== "done");
const totalTasks = projectTasks.length;
const progress = totalTasks > 0 ? Math.round((completedTasks.length / totalTasks) * 100) : 0;
const recentTasks = [...projectTasks]
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())
.slice(0, 5);
return {
project,
totalTasks,
completedTasks: completedTasks.length,
inProgressTasks: inProgressTasks.length,
urgentTasks: urgentTasks.length,
highPriorityTasks: highPriorityTasks.length,
progress,
recentTasks,
};
}
export async function fetchProjectsWithStats(): Promise<ProjectStats[]> {
const [projects, tasks] = await Promise.all([fetchAllProjects(), fetchAllTasks()]);
return projects.map((project) => calculateProjectStats(project, tasks));
}
export function getProjectHealth(stats: ProjectStats): {
status: "healthy" | "warning" | "critical";
label: string;
color: string;
} {
const { progress, urgentTasks, highPriorityTasks, totalTasks } = stats;
if (urgentTasks > 0 || (totalTasks > 5 && progress === 0)) {
return { status: "critical", label: "Needs Attention", color: "text-red-500" };
}
if (highPriorityTasks > 2 || (totalTasks > 10 && progress < 30)) {
return { status: "warning", label: "At Risk", color: "text-yellow-500" };
}
return { status: "healthy", label: "On Track", color: "text-green-500" };
}
export function getSprintDaysRemaining(sprint: Sprint): number {
const endDate = new Date(sprint.endDate);
const today = new Date();
const diffTime = endDate.getTime() - today.getTime();
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
}
export function formatSprintDateRange(sprint: Sprint): string {
const start = new Date(sprint.startDate);
const end = new Date(sprint.endDate);
return `${start.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${end.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
})}`;
}