128 lines
4.1 KiB
TypeScript
128 lines
4.1 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;
|
|
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 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",
|
|
})}`;
|
|
}
|