diff --git a/apps/web/index.html b/apps/web/index.html index ecc800c..64294f0 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -5,6 +5,9 @@ Project E + + +
diff --git a/apps/web/src/components/entities/detail-page.tsx b/apps/web/src/components/entities/detail-page.tsx index 603af2b..5b34ab4 100644 --- a/apps/web/src/components/entities/detail-page.tsx +++ b/apps/web/src/components/entities/detail-page.tsx @@ -43,35 +43,35 @@ export function EntityDetailPage({ const navigate = useNavigate(); return ( -
-
+
+
-
- {icon &&
{icon}
} -
-

{title}

+
+ {icon &&
{icon}
} +
+

{title}

{badges && ( -
{badges}
+
{badges}
)}
- {actions &&
{actions}
} + {actions &&
{actions}
}
- + {tabs.map((tab) => ( - + {tab.label} ))} diff --git a/apps/web/src/components/entities/entity-activity.tsx b/apps/web/src/components/entities/entity-activity.tsx index 4765dc8..5c893f1 100644 --- a/apps/web/src/components/entities/entity-activity.tsx +++ b/apps/web/src/components/entities/entity-activity.tsx @@ -12,7 +12,6 @@ import { type LucideIcon, } from "lucide-react"; import { formatDistanceToNow, parseISO } from "date-fns"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { ErrorState, LoadingState } from "@/components/state"; import { useApiQuery } from "@/lib/api"; import type { ActivityItem } from "@/lib/types"; @@ -66,15 +65,15 @@ function ActivityRow({ item }: { item: ActivityItem }) { const summary = changesSummary(item.action, item.changes); return ( -
-
- +
+
+

{item.actor} {meta.label} - + {formatDistanceToNow(parseISO(item.createdAt), { addSuffix: true })}

@@ -102,29 +101,24 @@ export function EntityActivity({ entityType, entityId, limit }: EntityActivityPr const items = data?.items ?? []; return ( - - - Activity - - - {isLoading ? : null} - {isError ? ( - refetch()} - /> - ) : null} - {!isLoading && !isError && items.length === 0 ? ( -

No activity yet

- ) : null} - {!isLoading && !isError && items.length > 0 ? ( -
- {items.map((item) => ( - - ))} -
- ) : null} -
-
+
+ {isLoading ? : null} + {isError ? ( + refetch()} + /> + ) : null} + {!isLoading && !isError && items.length === 0 ? ( +

No activity yet

+ ) : null} + {!isLoading && !isError && items.length > 0 ? ( +
+ {items.map((item) => ( + + ))} +
+ ) : null} +
); } diff --git a/apps/web/src/components/entities/entity-comments.tsx b/apps/web/src/components/entities/entity-comments.tsx index 425dc14..91ef415 100644 --- a/apps/web/src/components/entities/entity-comments.tsx +++ b/apps/web/src/components/entities/entity-comments.tsx @@ -5,7 +5,6 @@ import { toast } from "sonner"; import { formatDistanceToNow, parseISO } from "date-fns"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Textarea } from "@/components/ui/textarea"; import { ErrorState, LoadingState } from "@/components/state"; import { api, useApiMutation, useApiQuery } from "@/lib/api"; @@ -166,7 +165,7 @@ function CommentThread({ node, onDelete, deletePending }: CommentThreadProps) {

{comment.author} - + {formatDistanceToNow(parseISO(comment.createdAt), { addSuffix: true })}

@@ -250,37 +249,32 @@ export function EntityComments({ entityType, entityId }: EntityCommentsProps) { const tree = useMemo(() => buildCommentTree(data?.items ?? []), [data?.items]); return ( - - - Comments - - - - {isLoading ? : null} - {isError ? ( - refetch()} - /> - ) : null} - {!isLoading && !isError && tree.length === 0 ? ( -

- No comments yet — start the conversation. -

- ) : null} - {!isLoading && !isError && tree.length > 0 ? ( -
- {tree.map((node) => ( - deleteMutation.mutate(id)} - deletePending={deleteMutation.isPending} - /> - ))} -
- ) : null} -
-
+
+ + {isLoading ? : null} + {isError ? ( + refetch()} + /> + ) : null} + {!isLoading && !isError && tree.length === 0 ? ( +

+ No comments yet — start the conversation. +

+ ) : null} + {!isLoading && !isError && tree.length > 0 ? ( +
+ {tree.map((node) => ( + deleteMutation.mutate(id)} + deletePending={deleteMutation.isPending} + /> + ))} +
+ ) : null} +
); } diff --git a/apps/web/src/components/entities/tag-manager.tsx b/apps/web/src/components/entities/tag-manager.tsx index f0e8513..d89c77c 100644 --- a/apps/web/src/components/entities/tag-manager.tsx +++ b/apps/web/src/components/entities/tag-manager.tsx @@ -6,14 +6,15 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { X } from "lucide-react"; import type { Tag, PaginatedResponse } from "@/lib/types"; -const ENTITY_ROUTES: Record<"task" | "habit" | "note", string> = { +const ENTITY_ROUTES: Record<"task" | "habit" | "note" | "project", string> = { task: "tasks", habit: "habits", note: "notes", + project: "projects", }; interface TagManagerProps { - entityType: "task" | "habit" | "note"; + entityType: "task" | "habit" | "note" | "project"; entityId: string; tags: Tag[]; } diff --git a/apps/web/src/components/shell/command-palette.tsx b/apps/web/src/components/shell/command-palette.tsx index 5c9e73b..14024fa 100644 --- a/apps/web/src/components/shell/command-palette.tsx +++ b/apps/web/src/components/shell/command-palette.tsx @@ -14,8 +14,9 @@ import { Search, Sun, Moon, - Palette, LogOut, + Inbox, + FileBarChart, type LucideIcon, } from "lucide-react"; import { @@ -27,7 +28,7 @@ import { CommandList, CommandSeparator, } from "@/components/ui/command"; -import { useThemeStore, type AccentColor, ACCENT_PALETTE } from "@/lib/stores/use-theme-store"; +import { useThemeStore } from "@/lib/stores/use-theme-store"; import { useCreateDialogStore } from "@/lib/stores/use-create-dialog-store"; import { useApiDomain } from "@/lib/stores/use-active-domain-store"; @@ -38,6 +39,7 @@ interface NavItem { } const navItems: NavItem[] = [ + { label: "Inbox", href: "/inbox", icon: Inbox }, { label: "Dashboard", href: "/", icon: LayoutDashboard }, { label: "Tasks", href: "/tasks", icon: ListTodo }, { label: "Habits", href: "/habits", icon: Flame }, @@ -45,6 +47,8 @@ const navItems: NavItem[] = [ { label: "Notes", href: "/notes", icon: NotebookPen }, { label: "Graph", href: "/graph", icon: Share2 }, { label: "Calendar", href: "/calendar", icon: CalendarDays }, + { label: "Analytics", href: "/analytics", icon: LayoutDashboard }, + { label: "Reports", href: "/reports", icon: FileBarChart }, { label: "Agent Activity", href: "/agents/activity", icon: Bot }, { label: "Settings", href: "/settings", icon: Settings }, ]; @@ -73,7 +77,7 @@ function addRecentPage(href: string) { export function CommandPalette() { const navigate = useNavigate(); const location = useLocation(); - const { mode, setMode, accent, setAccent } = useThemeStore(); + const { mode, setMode } = useThemeStore(); const activeDomainId = useApiDomain(); const [open, setOpen] = useState(false); const [searchResults, setSearchResults] = useState< @@ -152,13 +156,6 @@ export function CommandPalette() { icon: mode === "dark" ? Sun : Moon, action: () => setMode(mode === "dark" ? "light" : "dark"), }, - ...(Object.entries(ACCENT_PALETTE) as [AccentColor, { name: string; hsl: string }][]).map( - ([key, val]) => ({ - label: `Accent: ${val.name}`, - icon: Palette, - action: () => setAccent(key), - }) - ), ]; // Settings actions diff --git a/apps/web/src/components/shell/shortcuts-help.tsx b/apps/web/src/components/shell/shortcuts-help.tsx index 3cf698a..c902094 100644 --- a/apps/web/src/components/shell/shortcuts-help.tsx +++ b/apps/web/src/components/shell/shortcuts-help.tsx @@ -10,6 +10,7 @@ import { const shortcutGroups = [ { heading: "Navigation", + color: "text-blue-500", shortcuts: [ { keys: "g then d", description: "Go to Dashboard" }, { keys: "g then t", description: "Go to Tasks" }, @@ -23,6 +24,7 @@ const shortcutGroups = [ }, { heading: "Create", + color: "text-amber-500", shortcuts: [ { keys: "n then t", description: "New task" }, { keys: "n then h", description: "New habit" }, @@ -33,6 +35,7 @@ const shortcutGroups = [ }, { heading: "General", + color: "text-muted-foreground", shortcuts: [ { keys: "⌘K / Ctrl+K", description: "Open command palette" }, { keys: "/", description: "Open command palette" }, @@ -43,6 +46,7 @@ const shortcutGroups = [ }, { heading: "Command Palette", + color: "text-muted-foreground", shortcuts: [ { keys: "↑↓", description: "Navigate items" }, { keys: "Enter", description: "Select item" }, @@ -62,24 +66,24 @@ export function ShortcutsHelp() { return ( - + Keyboard Shortcuts Use these shortcuts to navigate quickly. Press two-key combos like "g then t" in sequence within 1.5 seconds. -
+
{shortcutGroups.map((group) => (
-

+

{group.heading}

-
+
{group.shortcuts.map((s) => (
{s.description} diff --git a/apps/web/src/components/shell/sidebar.tsx b/apps/web/src/components/shell/sidebar.tsx index fcdbc7b..4562d16 100644 --- a/apps/web/src/components/shell/sidebar.tsx +++ b/apps/web/src/components/shell/sidebar.tsx @@ -9,7 +9,6 @@ import { Flame, FolderKanban, NotebookPen, - BookOpen, Share2, CalendarDays, Search, @@ -21,6 +20,9 @@ import { ChevronRight, LogOut, User, + Inbox, + FileBarChart, + FileStack, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; @@ -56,6 +58,7 @@ interface NavItem { } const navItems: NavItem[] = [ + { href: "/inbox", label: "Inbox", icon: Inbox, shortcut: "g i" }, { href: "/", label: "Dashboard", icon: LayoutDashboard, shortcut: "g d" }, { href: "/tasks", label: "Tasks", icon: ListTodo, shortcut: "g t" }, { href: "/habits", label: "Habits", icon: Flame, shortcut: "g h" }, @@ -65,14 +68,40 @@ const navItems: NavItem[] = [ { href: "/graph", label: "Graph", icon: Share2, shortcut: "g g" }, { href: "/search", label: "Search", icon: Search }, { href: "/analytics", label: "Analytics", icon: BarChart3 }, + { href: "/reports", label: "Reports", icon: FileBarChart, shortcut: "g r" }, { href: "/agents/activity", label: "Agent Activity", icon: Bot }, { href: "/daily", label: "Daily Notes", icon: PenLine }, + { href: "/templates", label: "Templates", icon: FileStack }, ]; const bottomItems: NavItem[] = [ { href: "/settings", label: "Settings", icon: Settings, shortcut: "g s" }, ]; +/** Map nav href to an ENTITY key for icon coloring. */ +function entityKey(href: string): string { + if (href === "/") return "dashboard"; + const segment = href.split("/")[1]; + return segment || "dashboard"; +} + +const ICON_COLORS: Record = { + dashboard: "hsl(var(--foreground))", + inbox: "#f97316", + tasks: "#3b82f6", + habits: "#10b981", + projects: "#8b5cf6", + notes: "#f59e0b", + calendar: "hsl(var(--foreground))", + graph: "hsl(var(--foreground))", + search: "hsl(var(--foreground))", + analytics: "hsl(var(--foreground))", + reports: "#6366f1", + agents: "hsl(var(--foreground))", + daily: "hsl(var(--foreground))", + templates: "#6366f1", +}; + export function Sidebar() { const location = useLocation(); const navigate = useNavigate(); @@ -81,9 +110,6 @@ export function Sidebar() { const userName = user?.name || user?.email?.split("@")[0] || "User"; const userInitials = (user?.name || user?.email || "U").slice(0, 2).toUpperCase(); - // Sidebar position (left/right) is set in Settings. Read once on mount and - // update live via the "sidebar-position-change" custom event dispatched by - // the settings page. const [sidebarPos, setSidebarPos] = useState<"left" | "right">(() => typeof window !== "undefined" && localStorage.getItem("sidebar-position") === "right" ? "right" @@ -106,25 +132,31 @@ export function Sidebar() { const renderNavLink = (item: NavItem, isCollapsed: boolean, onNavigate?: () => void) => { const active = isActive(item.href); + const key = entityKey(item.href); + const iconColor = ICON_COLORS[key] ?? "hsl(var(--foreground))"; + const link = ( -