feat: Mission Control dashboard for project management
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"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;
|
||||
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 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,
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user