feat: add domains, ideas, layouts, widgets API modules
- Add DomainsModule with full CRUD, search, and activity logging - Add IdeasModule with quick capture endpoint - Add LayoutsModule for user dashboard layouts - Add WidgetsModule for widget definitions (read-only) - Update ActivityService with domain/idea logging methods - Register all new modules in AppModule
This commit is contained in:
134
apps/web/src/components/widgets/TasksWidget.tsx
Normal file
134
apps/web/src/components/widgets/TasksWidget.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Tasks Widget - displays task summary and list
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { CheckCircle, Circle, Clock, AlertCircle } from "lucide-react";
|
||||
import type { WidgetProps } from "@mosaic/shared";
|
||||
|
||||
interface Task {
|
||||
id: string;
|
||||
title: string;
|
||||
status: string;
|
||||
priority: string;
|
||||
dueDate?: string;
|
||||
}
|
||||
|
||||
export function TasksWidget({ }: WidgetProps) {
|
||||
const [tasks, setTasks] = useState<Task[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
// Mock data for now - will fetch from API later
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
setTasks([
|
||||
{
|
||||
id: "1",
|
||||
title: "Complete project documentation",
|
||||
status: "IN_PROGRESS",
|
||||
priority: "HIGH",
|
||||
dueDate: "2024-02-01",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "Review pull requests",
|
||||
status: "NOT_STARTED",
|
||||
priority: "MEDIUM",
|
||||
dueDate: "2024-02-02",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "Update dependencies",
|
||||
status: "COMPLETED",
|
||||
priority: "LOW",
|
||||
dueDate: "2024-01-30",
|
||||
},
|
||||
]);
|
||||
setIsLoading(false);
|
||||
}, 500);
|
||||
}, []);
|
||||
|
||||
const getPriorityIcon = (priority: string) => {
|
||||
switch (priority) {
|
||||
case "HIGH":
|
||||
return <AlertCircle className="w-4 h-4 text-red-500" />;
|
||||
case "MEDIUM":
|
||||
return <Clock className="w-4 h-4 text-yellow-500" />;
|
||||
case "LOW":
|
||||
return <Circle className="w-4 h-4 text-gray-400" />;
|
||||
default:
|
||||
return <Circle className="w-4 h-4 text-gray-400" />;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
return status === "COMPLETED" ? (
|
||||
<CheckCircle className="w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<Circle className="w-4 h-4 text-gray-400" />
|
||||
);
|
||||
};
|
||||
|
||||
const stats = {
|
||||
total: tasks.length,
|
||||
inProgress: tasks.filter((t) => t.status === "IN_PROGRESS").length,
|
||||
completed: tasks.filter((t) => t.status === "COMPLETED").length,
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-gray-500 text-sm">Loading tasks...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full space-y-3">
|
||||
{/* Summary stats */}
|
||||
<div className="grid grid-cols-3 gap-2 text-center">
|
||||
<div className="bg-gray-50 rounded p-2">
|
||||
<div className="text-2xl font-bold text-gray-900">{stats.total}</div>
|
||||
<div className="text-xs text-gray-500">Total</div>
|
||||
</div>
|
||||
<div className="bg-blue-50 rounded p-2">
|
||||
<div className="text-2xl font-bold text-blue-600">{stats.inProgress}</div>
|
||||
<div className="text-xs text-blue-500">In Progress</div>
|
||||
</div>
|
||||
<div className="bg-green-50 rounded p-2">
|
||||
<div className="text-2xl font-bold text-green-600">{stats.completed}</div>
|
||||
<div className="text-xs text-green-500">Done</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Task list */}
|
||||
<div className="flex-1 overflow-auto space-y-2">
|
||||
{tasks.length === 0 ? (
|
||||
<div className="text-center text-gray-500 text-sm py-4">No tasks yet</div>
|
||||
) : (
|
||||
tasks.slice(0, 5).map((task) => (
|
||||
<div
|
||||
key={task.id}
|
||||
className="flex items-center gap-3 p-2 hover:bg-gray-50 rounded transition-colors"
|
||||
>
|
||||
{getStatusIcon(task.status)}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-sm font-medium text-gray-900 truncate">
|
||||
{task.title}
|
||||
</div>
|
||||
{task.dueDate && (
|
||||
<div className="text-xs text-gray-500">
|
||||
Due: {new Date(task.dueDate).toLocaleDateString()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{getPriorityIcon(task.priority)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user