115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { createContext, useContext, useState, useEffect, ReactNode } from "react";
|
|
import { Task, TaskStatus, initialTasks } from "./types";
|
|
|
|
interface MissionControlStore {
|
|
tasks: Task[];
|
|
toggleTask: (id: string) => void;
|
|
updateTaskStatus: (id: string, status: TaskStatus) => void;
|
|
addTask: (task: Omit<Task, "id" | "order">) => void;
|
|
getProjectProgress: (projectId: string) => number;
|
|
getTasksByProject: (projectId: string) => Task[];
|
|
}
|
|
|
|
const MissionControlContext = createContext<MissionControlStore | null>(null);
|
|
|
|
const STORAGE_KEY = "sitemente:mission-control";
|
|
|
|
export function MissionControlProvider({ children }: { children: ReactNode }) {
|
|
const [tasks, setTasks] = useState<Task[]>(initialTasks);
|
|
|
|
// Load from localStorage on mount
|
|
useEffect(() => {
|
|
if (typeof window === "undefined") return;
|
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
if (saved) {
|
|
try {
|
|
const parsed = JSON.parse(saved);
|
|
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
setTasks(parsed);
|
|
}
|
|
} catch {
|
|
// Use default
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
// Save to localStorage on change
|
|
useEffect(() => {
|
|
if (typeof window === "undefined") return;
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
|
|
}, [tasks]);
|
|
|
|
const toggleTask = (id: string) => {
|
|
setTasks((prev) =>
|
|
prev.map((t) => {
|
|
if (t.id !== id) return t;
|
|
const newStatus: TaskStatus = t.status === "done" ? "todo" : "done";
|
|
return {
|
|
...t,
|
|
status: newStatus,
|
|
completedAt: newStatus === "done" ? new Date().toISOString().split("T")[0] : undefined,
|
|
};
|
|
})
|
|
);
|
|
};
|
|
|
|
const updateTaskStatus = (id: string, status: TaskStatus) => {
|
|
setTasks((prev) =>
|
|
prev.map((t) =>
|
|
t.id === id
|
|
? {
|
|
...t,
|
|
status,
|
|
completedAt: status === "done" ? new Date().toISOString().split("T")[0] : undefined,
|
|
}
|
|
: t
|
|
)
|
|
);
|
|
};
|
|
|
|
const addTask = (task: Omit<Task, "id" | "order">) => {
|
|
const newTask: Task = {
|
|
...task,
|
|
id: `t-${Date.now()}`,
|
|
order: tasks.length + 1,
|
|
};
|
|
setTasks((prev) => [...prev, newTask]);
|
|
};
|
|
|
|
const getProjectProgress = (projectId: string) => {
|
|
const projectTasks = tasks.filter((t) => t.project === projectId);
|
|
if (projectTasks.length === 0) return 0;
|
|
const done = projectTasks.filter((t) => t.status === "done").length;
|
|
return Math.round((done / projectTasks.length) * 100);
|
|
};
|
|
|
|
const getTasksByProject = (projectId: string) => {
|
|
return tasks.filter((t) => t.project === projectId).sort((a, b) => a.order - b.order);
|
|
};
|
|
|
|
return (
|
|
<MissionControlContext.Provider
|
|
value={{
|
|
tasks,
|
|
toggleTask,
|
|
updateTaskStatus,
|
|
addTask,
|
|
getProjectProgress,
|
|
getTasksByProject,
|
|
}}
|
|
>
|
|
{children}
|
|
</MissionControlContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useMissionControl() {
|
|
const ctx = useContext(MissionControlContext);
|
|
if (!ctx) {
|
|
throw new Error("useMissionControl must be used within MissionControlProvider");
|
|
}
|
|
return ctx;
|
|
}
|