feat(#37-41): Add domains, ideas, relationships, agents, widgets schema

Schema additions for issues #37-41:

New models:
- Domain (#37): Life domains (work, marriage, homelab, etc.)
- Idea (#38): Brain dumps with pgvector embeddings
- Relationship (#39): Generic entity linking (blocks, depends_on)
- Agent (#40): ClawdBot agent tracking with metrics
- AgentSession (#40): Conversation session tracking
- WidgetDefinition (#41): HUD widget registry
- UserLayout (#41): Per-user dashboard configuration

Updated models:
- Task, Event, Project: Added domainId foreign key
- User, Workspace: Added new relations

New enums:
- IdeaStatus: CAPTURED, PROCESSING, ACTIONABLE, ARCHIVED, DISCARDED
- RelationshipType: BLOCKS, BLOCKED_BY, DEPENDS_ON, etc.
- AgentStatus: IDLE, WORKING, WAITING, ERROR, TERMINATED
- EntityType: Added IDEA, DOMAIN

Migration: 20260129182803_add_domains_ideas_agents_widgets
This commit is contained in:
Jason Woltje
2026-01-29 12:29:21 -06:00
parent a220c2dc0a
commit 973502f26e
308 changed files with 18374 additions and 113 deletions

View File

@@ -0,0 +1,70 @@
import type { Task } from "@mosaic/shared";
import { TaskItem } from "./TaskItem";
import { getDateGroupLabel } from "@/lib/utils/date-format";
interface TaskListProps {
tasks: Task[];
isLoading: boolean;
}
export function TaskList({ tasks, isLoading }: TaskListProps) {
if (isLoading) {
return (
<div className="flex justify-center items-center p-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
<span className="ml-3 text-gray-600">Loading tasks...</span>
</div>
);
}
// Handle null/undefined tasks gracefully
if (!tasks || tasks.length === 0) {
return (
<div className="text-center p-8 text-gray-500">
<p className="text-lg">No tasks scheduled</p>
<p className="text-sm mt-2">Your task list is clear</p>
</div>
);
}
// Group tasks by date
const groupedTasks = tasks.reduce((groups, task) => {
if (!task.dueDate) {
return groups;
}
const label = getDateGroupLabel(task.dueDate);
if (!groups[label]) {
groups[label] = [];
}
groups[label].push(task);
return groups;
}, {} as Record<string, Task[]>);
const groupOrder = ["Today", "Tomorrow", "This Week", "Next Week", "Later"];
return (
<main className="space-y-6">
{groupOrder.map((groupLabel) => {
const groupTasks = groupedTasks[groupLabel];
if (!groupTasks || groupTasks.length === 0) {
return null;
}
return (
<section key={groupLabel}>
<h2 className="text-lg font-semibold text-gray-700 mb-3">
{groupLabel}
</h2>
<ul className="space-y-2">
{groupTasks.map((task) => (
<li key={task.id}>
<TaskItem task={task} />
</li>
))}
</ul>
</section>
);
})}
</main>
);
}