import { getGanttTaskUrl } from "@/lib/config/sites"; import { Task, fetchAllTasks } from "./tasks"; import { Project, fetchAllProjects } from "./projects"; export interface ProgressMetric { id: string; label: string; current: number; target: number; unit: string; percentage: number; icon: string; color: string; } export interface Milestone { id: string; title: string; description?: string; completedAt: string; category: "ios" | "financial" | "travel" | "family" | "business"; icon: string; } export interface NextStep { id: string; title: string; priority: "high" | "urgent"; projectName?: string; dueDate?: string; ganttBoardUrl: string; } export interface MissionProgress { retirement: ProgressMetric; iosPortfolio: ProgressMetric; sideHustleRevenue: ProgressMetric; travelFund: ProgressMetric; milestones: Milestone[]; nextSteps: NextStep[]; } const TARGET_APPS = 20; const TARGET_REVENUE = 10000; const TARGET_TRAVEL_FUND = 50000; const TARGET_RETIREMENT_TASKS = 100; const IOS_KEYWORDS = ["ios", "app", "swift", "mobile", "iphone", "ipad", "xcode"]; function isIOSProject(project: Project): boolean { const nameLower = project.name.toLowerCase(); const descLower = (project.description || "").toLowerCase(); return IOS_KEYWORDS.some((keyword) => nameLower.includes(keyword) || descLower.includes(keyword)); } function isIOSTask(task: Task): boolean { const titleLower = task.title.toLowerCase(); const descLower = (task.description || "").toLowerCase(); const hasIOSTag = task.tags.some((tag) => ["ios", "app", "swift", "mobile"].includes(tag.toLowerCase())); return hasIOSTag || IOS_KEYWORDS.some((keyword) => titleLower.includes(keyword) || descLower.includes(keyword)); } function calculatePercentage(current: number, target: number): number { if (target === 0) return 0; return Math.min(100, Math.max(0, Math.round((current / target) * 100))); } function byUpdatedDesc(a: Task, b: Task): number { return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(); } export async function getRetirementProgress(): Promise { const tasks = await fetchAllTasks(); const current = tasks.filter((task) => task.status === "done").length; return { id: "retirement", label: "Freedom Progress", current, target: TARGET_RETIREMENT_TASKS, unit: "tasks", percentage: calculatePercentage(current, TARGET_RETIREMENT_TASKS), icon: "Target", color: "from-emerald-500 to-teal-500", }; } export async function getiOSPortfolioProgress(): Promise { const [projects, tasks] = await Promise.all([fetchAllProjects(), fetchAllTasks()]); const iosProjects = projects.filter(isIOSProject); const completedIOSTasks = tasks.filter((task) => isIOSTask(task) && task.status === "done").length; const taskContribution = Math.floor(completedIOSTasks / 10); const current = Math.min(iosProjects.length + taskContribution, TARGET_APPS); return { id: "ios-portfolio", label: "iOS Portfolio", current, target: TARGET_APPS, unit: "apps", percentage: calculatePercentage(current, TARGET_APPS), icon: "Smartphone", color: "from-blue-500 to-cyan-500", }; } export async function getSideHustleRevenue(): Promise { const tasks = await fetchAllTasks(); const doneTasks = tasks.filter((task) => task.status === "done"); const milestones = doneTasks.filter((task) => task.tags.some((tag) => { const normalized = tag.toLowerCase(); return normalized === "revenue" || normalized === "milestone"; }) ); const estimatedRevenue = milestones.length * 500; return { id: "revenue", label: "Side Hustle Revenue", current: estimatedRevenue, target: TARGET_REVENUE, unit: "MRR", percentage: calculatePercentage(estimatedRevenue, TARGET_REVENUE), icon: "DollarSign", color: "from-amber-500 to-orange-500", }; } export async function getTravelFundProgress(): Promise { const tasks = await fetchAllTasks(); const doneTasks = tasks.filter((task) => task.status === "done"); const travelTasks = doneTasks.filter((task) => task.tags.some((tag) => tag.toLowerCase() === "travel")); const estimatedSavings = travelTasks.length * 500; return { id: "travel", label: "Travel Fund", current: estimatedSavings, target: TARGET_TRAVEL_FUND, unit: "saved", percentage: calculatePercentage(estimatedSavings, TARGET_TRAVEL_FUND), icon: "Plane", color: "from-purple-500 to-pink-500", }; } export async function getMissionMilestones(): Promise { const tasks = await fetchAllTasks(); const milestoneTasks = tasks .filter((task) => task.status === "done") .filter((task) => task.tags.some((tag) => { const normalized = tag.toLowerCase(); return normalized === "milestone" || normalized === "mission"; }) ) .sort(byUpdatedDesc); return milestoneTasks.map((task) => { let category: Milestone["category"] = "business"; const titleLower = task.title.toLowerCase(); const tagsLower = task.tags.map((tag) => tag.toLowerCase()); if (tagsLower.includes("ios") || tagsLower.includes("app") || titleLower.includes("app")) { category = "ios"; } else if (tagsLower.includes("travel") || titleLower.includes("travel")) { category = "travel"; } else if (tagsLower.includes("family") || titleLower.includes("family")) { category = "family"; } else if (tagsLower.includes("revenue") || tagsLower.includes("money") || titleLower.includes("$")) { category = "financial"; } const iconMap: Record = { ios: "Smartphone", financial: "DollarSign", travel: "Plane", family: "Heart", business: "Briefcase", }; return { id: task.id, title: task.title, description: task.description, completedAt: task.updatedAt, category, icon: iconMap[category], }; }); } export async function getNextMissionSteps(): Promise { const [tasks, projects] = await Promise.all([fetchAllTasks(), fetchAllProjects()]); const projectMap = new Map(projects.map((project) => [project.id, project.name])); const prioritized = tasks .filter((task) => (task.priority === "high" || task.priority === "urgent") && task.status !== "done") .sort((a, b) => { const aDue = a.dueDate ? new Date(a.dueDate).getTime() : Number.MAX_SAFE_INTEGER; const bDue = b.dueDate ? new Date(b.dueDate).getTime() : Number.MAX_SAFE_INTEGER; if (aDue !== bDue) return aDue - bDue; return byUpdatedDesc(a, b); }) .slice(0, 6); return prioritized.map((task) => ({ id: task.id, title: task.title, priority: task.priority as "high" | "urgent", projectName: projectMap.get(task.projectId), dueDate: task.dueDate, ganttBoardUrl: getGanttTaskUrl(task.id), })); } export async function fetchMissionProgress(): Promise { const [retirement, iosPortfolio, sideHustleRevenue, travelFund, milestones, nextSteps] = await Promise.all([ getRetirementProgress(), getiOSPortfolioProgress(), getSideHustleRevenue(), getTravelFundProgress(), getMissionMilestones(), getNextMissionSteps(), ]); return { retirement, iosPortfolio, sideHustleRevenue, travelFund, milestones, nextSteps, }; }