From f0f7a0b5247ce499f198b2d7e1ce8954384205e0 Mon Sep 17 00:00:00 2001 From: bot-hermes Date: Wed, 9 Sep 2026 00:38:53 +0000 Subject: [PATCH] UI redesign: data-dense professional treatment across entire app Foundation: - Rewrote CSS design tokens for light/dark modes (compact density default) - Added JetBrains Mono for monospace accents on numeric data/timestamps - Vibrant semantic color palette (tasks=blue, habits=green, projects=purple, notes=amber) - Removed accent color toggle system, pinned interactive accent blue - New shell surface tokens (sidebar-bg, sidebar-border, topbar-bg) Shell: - Sidebar: colored entity-type icons, always-visible shortcuts, left accent border - Topbar: reduced to h-10, backdrop blur, monospace search shortcut Pages (all 12 existing): - Border treatment (no shadows), hover:bg-muted/20 transitions - font-mono for all numeric data, timestamps, IDs, status codes - Tighter spacing (gap-3, space-y-3), text-xl headers New pages: - Inbox (/inbox) - focused attention view with overdue/today habits/recent notes - Reports (/reports) - richer analytics with stat cards, charts, CSV export - Templates (/templates) - localStorage-based task/note template management Entity detail pages: - Consistent border-t pt-3 dividers, monospace timestamps - Added TagManager to projects (consistency fix) - Removed Card wrappers from activity/comments (flat sections) Login: split-layout with branding panel and floating colored dots Shared components: colored EmptyState icons, monospace error messages Command palette & shortcuts: new nav items for Inbox/Reports/Analytics Cleanup: removed all hover:shadow-md and ACCENT_PALETTE references --- apps/web/index.html | 3 + .../src/components/entities/detail-page.tsx | 24 +- .../components/entities/entity-activity.tsx | 52 +-- .../components/entities/entity-comments.tsx | 62 ++- .../src/components/entities/tag-manager.tsx | 5 +- .../src/components/shell/command-palette.tsx | 17 +- .../src/components/shell/shortcuts-help.tsx | 14 +- apps/web/src/components/shell/sidebar.tsx | 102 +++-- .../src/components/shell/theme-provider.tsx | 11 +- apps/web/src/components/shell/topbar.tsx | 24 +- apps/web/src/components/state.tsx | 30 +- apps/web/src/hooks/use-keyboard-shortcuts.ts | 2 + apps/web/src/index.css | 98 +++-- apps/web/src/lib/status-colors.ts | 8 +- apps/web/src/lib/stores/use-theme-store.ts | 16 - apps/web/src/routeTree.ts | 6 + apps/web/src/routes/_app.tsx | 2 +- apps/web/src/routes/_app/agents/activity.tsx | 70 +-- apps/web/src/routes/_app/analytics.tsx | 64 +-- apps/web/src/routes/_app/calendar.tsx | 18 +- apps/web/src/routes/_app/daily.tsx | 78 ++-- apps/web/src/routes/_app/dashboard.tsx | 231 +++++----- apps/web/src/routes/_app/graph.tsx | 40 +- apps/web/src/routes/_app/habits.tsx | 33 +- apps/web/src/routes/_app/habits/$id.tsx | 62 +-- apps/web/src/routes/_app/inbox.tsx | 316 +++++++++++++ apps/web/src/routes/_app/notes.tsx | 114 +++-- apps/web/src/routes/_app/notes/$id.tsx | 10 +- apps/web/src/routes/_app/projects.tsx | 198 ++++++--- apps/web/src/routes/_app/projects/$id.tsx | 34 +- apps/web/src/routes/_app/reports.tsx | 416 ++++++++++++++++++ apps/web/src/routes/_app/search.tsx | 73 +-- apps/web/src/routes/_app/settings.tsx | 27 +- apps/web/src/routes/_app/tasks.tsx | 48 +- apps/web/src/routes/_app/tasks/$id.tsx | 16 +- apps/web/src/routes/_app/templates.tsx | 277 ++++++++++++ apps/web/src/routes/login.tsx | 154 ++++--- apps/web/tailwind.config.ts | 8 + 38 files changed, 1998 insertions(+), 765 deletions(-) create mode 100644 apps/web/src/routes/_app/inbox.tsx create mode 100644 apps/web/src/routes/_app/reports.tsx create mode 100644 apps/web/src/routes/_app/templates.tsx 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 = ( -