Skip to content

Commit 9d845b4

Browse files
committed
feat(dashboard): reorganize scattered subpages into 5 unified workspaces
1 parent 36235fb commit 9d845b4

7 files changed

Lines changed: 561 additions & 470 deletions

File tree

apps/website/src/components/app-shell/topbar.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,22 @@ interface TopbarProps {
1919
const TITLES: Record<string, string> = {
2020
"/dashboard": "Home",
2121
"/dashboard/ask": "Ask Debo",
22-
"/dashboard/chat": "Chat",
22+
"/dashboard/chat": "Ask Debo",
2323
"/dashboard/journal": "Journal",
24-
"/dashboard/voice": "Voice",
24+
"/dashboard/voice": "Voice Notes",
2525
"/dashboard/media": "Media",
26-
"/dashboard/library": "Library",
27-
"/dashboard/tasks": "Tasks",
28-
"/dashboard/people": "People",
26+
"/dashboard/library": "Memory Hub",
27+
"/dashboard/tasks": "Work Hub",
28+
"/dashboard/people": "People Graph",
2929
"/dashboard/projects": "Projects",
3030
"/dashboard/mail": "Debo Mail",
31-
"/dashboard/connectors": "Connectors",
32-
"/dashboard/vault": "Vault",
31+
"/dashboard/connectors": "Developer Hub",
32+
"/dashboard/vault": "Secrets Vault",
3333
"/dashboard/inbox": "Memory Inbox",
34-
"/dashboard/decisions": "Decisions",
34+
"/dashboard/decisions": "Decisions Log",
3535
"/dashboard/timeline": "Timeline",
36-
"/dashboard/radar": "Radar",
37-
"/dashboard/debrief": "Debrief",
36+
"/dashboard/radar": "Follow-up Radar",
37+
"/dashboard/debrief": "Daily Debrief",
3838
"/dashboard/settings": "Settings",
3939
};
4040

apps/website/src/components/chat/chat-page.tsx

Lines changed: 214 additions & 182 deletions
Large diffs are not rendered by default.

apps/website/src/components/connectors/connectors-page.tsx

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { api } from "@/lib/api";
77
import type { Connector } from "@/lib/types";
88
import { toast } from "sonner";
99

10+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
11+
import { VaultPage } from "@/components/vault/vault-page";
12+
import { McpPage } from "@/components/mcp/mcp-page";
13+
import { DeboAppsPage } from "@/components/apps/debo-apps-page";
14+
1015
const PROVIDER_METADATA: Record<string, {
1116
name: string;
1217
description: string;
@@ -257,51 +262,79 @@ export function ConnectorsPage() {
257262
}
258263

259264
return (
260-
<div className="p-6 md:p-8 max-w-5xl mx-auto space-y-5">
261-
<div className="flex items-center justify-between">
262-
<div>
263-
<h1 className="text-2xl font-bold text-foreground font-[var(--font-nunito)]">
264-
Connectors
265-
</h1>
266-
<p className="text-sm text-muted-foreground mt-1">
267-
Bring your existing memory sources into Debo.
268-
</p>
269-
</div>
270-
{pollActive && (
271-
<div className="flex items-center gap-1.5 text-xs text-muted-foreground bg-muted/30 px-3 py-1.5 rounded-full border border-border">
272-
<Loader2 className="size-3 animate-spin text-primary" />
273-
<span>Awaiting authorization...</span>
274-
</div>
275-
)}
265+
<div className="flex flex-col h-full">
266+
{/* Developer Hub Header */}
267+
<div className="px-6 pt-6 pb-3 border-b border-border bg-card/40 shrink-0">
268+
<h1 className="text-2xl font-bold text-foreground font-[var(--font-nunito)]">Developer Hub</h1>
269+
<p className="text-xs text-muted-foreground mt-0.5">Integrate external accounts, manage credential secrets, configure the remote MCP server, and launch custom Debo apps.</p>
276270
</div>
277271

278-
<div className="rounded-2xl border-2 border-primary/15 bg-primary/5 px-3 py-2.5 flex items-start gap-2">
279-
<ShieldCheck className="size-4 text-primary mt-0.5 shrink-0" />
280-
<p className="text-xs text-muted-foreground">
281-
Connectors are optional. You control what Debo remembers and can
282-
disconnect anytime &mdash; all imported data will be removed.
283-
</p>
284-
</div>
272+
<Tabs defaultValue="connectors" className="flex-1 flex flex-col min-h-0">
273+
<div className="px-6 border-b border-border bg-card/20 py-2 shrink-0">
274+
<TabsList className="bg-muted/50 p-1 rounded-xl">
275+
<TabsTrigger value="connectors" className="rounded-lg text-xs font-bold px-4">Connectors</TabsTrigger>
276+
<TabsTrigger value="vault" className="rounded-lg text-xs font-bold px-4">Secrets Vault</TabsTrigger>
277+
<TabsTrigger value="mcp" className="rounded-lg text-xs font-bold px-4">MCP Server</TabsTrigger>
278+
<TabsTrigger value="apps" className="rounded-lg text-xs font-bold px-4">Debo Apps</TabsTrigger>
279+
</TabsList>
280+
</div>
285281

286-
<div className="space-y-6">
287-
{categoryEntries.map(([category, items]) => (
288-
<div key={category} className="space-y-3">
289-
<h2 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
290-
{category}
291-
</h2>
292-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
293-
{items.map((connector) => (
294-
<ConnectorCard
295-
key={connector.id}
296-
connector={connector}
297-
onConnect={handleConnect}
298-
onDisconnect={handleDisconnect}
299-
/>
300-
))}
282+
{/* Connectors Tab */}
283+
<TabsContent value="connectors" className="flex-1 overflow-y-auto focus-visible:outline-none focus-visible:ring-0 p-6 md:p-8 max-w-5xl w-full mx-auto mt-0 space-y-5">
284+
<div className="flex items-center justify-between">
285+
<div>
286+
<h2 className="text-xl font-bold text-foreground font-[var(--font-nunito)]">Account Connectors</h2>
287+
<p className="text-sm text-muted-foreground mt-1">Bring your existing memory sources into Debo.</p>
301288
</div>
289+
{pollActive && (
290+
<div className="flex items-center gap-1.5 text-xs text-muted-foreground bg-muted/30 px-3 py-1.5 rounded-full border border-border">
291+
<Loader2 className="size-3 animate-spin text-primary" />
292+
<span>Awaiting authorization...</span>
293+
</div>
294+
)}
302295
</div>
303-
))}
304-
</div>
296+
297+
<div className="rounded-2xl border-2 border-primary/15 bg-primary/5 px-3 py-2.5 flex items-start gap-2">
298+
<ShieldCheck className="size-4 text-primary mt-0.5 shrink-0" />
299+
<p className="text-xs text-muted-foreground">
300+
Connectors are optional. You control what Debo remembers and can
301+
disconnect anytime &mdash; all imported data will be removed.
302+
</p>
303+
</div>
304+
305+
<div className="space-y-6">
306+
{categoryEntries.map(([category, items]) => (
307+
<div key={category} className="space-y-3">
308+
<h3 className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
309+
{category}
310+
</h3>
311+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
312+
{items.map((connector) => (
313+
<ConnectorCard
314+
key={connector.id}
315+
connector={connector}
316+
onConnect={handleConnect}
317+
onDisconnect={handleDisconnect}
318+
/>
319+
))}
320+
</div>
321+
</div>
322+
))}
323+
</div>
324+
</TabsContent>
325+
326+
<TabsContent value="vault" className="flex-1 overflow-y-auto focus-visible:outline-none focus-visible:ring-0">
327+
<VaultPage />
328+
</TabsContent>
329+
330+
<TabsContent value="mcp" className="flex-1 overflow-y-auto focus-visible:outline-none focus-visible:ring-0">
331+
<McpPage />
332+
</TabsContent>
333+
334+
<TabsContent value="apps" className="flex-1 overflow-y-auto focus-visible:outline-none focus-visible:ring-0">
335+
<DeboAppsPage />
336+
</TabsContent>
337+
</Tabs>
305338
</div>
306339
);
307340
}

apps/website/src/components/journal/journal-page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ export function JournalPage({ fallbackData = [] }: JournalPageProps) {
225225
},
226226
{
227227
fallbackData,
228-
revalidateOnMount: false,
228+
revalidateOnMount: fallbackData.length === 0,
229229
revalidateOnFocus: false,
230230
}
231231
);

apps/website/src/components/library/library-page.tsx

Lines changed: 103 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import { SourceCard } from "@/components/library/source-card";
77
import { api } from "@/lib/api";
88
import type { MemorySource, SourceType } from "@/lib/types";
99

10+
// Tab consolidations
11+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
12+
import { JournalPage } from "@/components/journal/journal-page";
13+
import { VoicePage } from "@/components/voice/voice-page";
14+
import { MailShell } from "@/components/mail/mail-shell";
15+
import { MemoryInboxPage } from "@/components/inbox/memory-inbox-page";
16+
1017
function normalizeSource(raw: any): MemorySource {
1118
return {
1219
id: raw.id ?? crypto.randomUUID(),
@@ -50,108 +57,109 @@ export function LibraryPage() {
5057
});
5158
}, [sources, activeType, search, sortMode]);
5259

53-
const header = (
54-
<div className="px-6 pt-6 pb-3">
55-
<h1 className="text-2xl font-bold text-foreground font-[var(--font-nunito)]">
56-
Library
57-
</h1>
58-
<p className="text-sm text-muted-foreground mt-1">
59-
Everything you&apos;ve captured, in one place.
60-
</p>
61-
</div>
62-
);
63-
64-
// Only show loading skeletons if there's no data already cached
65-
if (isLoading && sources.length === 0) {
66-
return (
67-
<div className="flex flex-col h-full">
68-
{header}
69-
<div className="px-6 pb-4">
70-
<FilterBar
71-
search={search}
72-
onSearchChange={setSearch}
73-
activeType={activeType}
74-
onTypeChange={setActiveType}
75-
viewMode={viewMode}
76-
onViewModeChange={setViewMode}
77-
sortMode={sortMode}
78-
onSortModeChange={setSortMode}
79-
/>
80-
</div>
81-
<div className="flex-1 overflow-y-auto px-6 pb-6">
82-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
83-
{[0, 1, 2, 3, 4, 5].map((i) => (
84-
<div
85-
key={i}
86-
className="rounded-2xl border-2 border-border bg-card p-4 h-36 animate-pulse"
87-
/>
88-
))}
89-
</div>
90-
</div>
91-
</div>
92-
);
93-
}
94-
95-
if (error && sources.length === 0) {
96-
return (
97-
<div className="flex flex-col h-full">
98-
{header}
99-
<div className="flex-1 flex items-center justify-center">
100-
<div className="flex flex-col items-center text-center gap-3">
101-
<div className="size-10 rounded-xl bg-accent flex items-center justify-center">
102-
<FileTextIcon />
103-
</div>
104-
<p className="text-xs text-muted-foreground max-w-[28ch]">
105-
Could not load sources. Make sure the API is running.
106-
</p>
107-
</div>
108-
</div>
109-
</div>
110-
);
111-
}
60+
// Loading indicator for Timeline only (since other tabs manage their own loading state)
61+
const timelineLoading = isLoading && sources.length === 0;
11262

11363
return (
11464
<div className="flex flex-col h-full">
115-
{header}
116-
<div className="px-6 pb-4">
117-
<FilterBar
118-
search={search}
119-
onSearchChange={setSearch}
120-
activeType={activeType}
121-
onTypeChange={setActiveType}
122-
viewMode={viewMode}
123-
onViewModeChange={setViewMode}
124-
sortMode={sortMode}
125-
onSortModeChange={setSortMode}
126-
/>
65+
<div className="px-6 pt-6 pb-3 border-b border-border bg-card/40">
66+
<h1 className="text-2xl font-bold text-foreground font-[var(--font-nunito)]">
67+
Memory Hub
68+
</h1>
69+
<p className="text-xs text-muted-foreground mt-0.5">
70+
Your centralized workspace for Timeline, Journals, Voice recordings, Mail sync, and Inbox review.
71+
</p>
12772
</div>
12873

129-
<div className="flex-1 overflow-y-auto px-6 pb-6">
130-
{filtered.length === 0 ? (
131-
<div className="flex flex-col items-center justify-center py-16 text-center gap-3">
132-
<div className="size-10 rounded-xl bg-accent flex items-center justify-center">
133-
<FileTextIcon />
134-
</div>
135-
<p className="text-xs text-muted-foreground max-w-[28ch]">
136-
{search || activeType !== "all"
137-
? "No sources match your filters."
138-
: "Capture a journal, voice note, or link to start your library."}
139-
</p>
140-
</div>
141-
) : viewMode === "grid" ? (
142-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
143-
{filtered.map((source: MemorySource) => (
144-
<SourceCard key={source.id} source={source} variant="grid" />
145-
))}
74+
<Tabs defaultValue="timeline" className="flex-1 flex flex-col min-h-0">
75+
<div className="px-6 border-b border-border bg-card/20 py-2 shrink-0">
76+
<TabsList className="bg-muted/50 p-1 rounded-xl">
77+
<TabsTrigger value="timeline" className="rounded-lg text-xs font-bold px-4">Timeline</TabsTrigger>
78+
<TabsTrigger value="journal" className="rounded-lg text-xs font-bold px-4">Journal</TabsTrigger>
79+
<TabsTrigger value="voice" className="rounded-lg text-xs font-bold px-4">Voice Notes</TabsTrigger>
80+
<TabsTrigger value="mail" className="rounded-lg text-xs font-bold px-4">Debo Mail</TabsTrigger>
81+
<TabsTrigger value="inbox" className="rounded-lg text-xs font-bold px-4">Inbox Review</TabsTrigger>
82+
</TabsList>
83+
</div>
84+
85+
<TabsContent value="timeline" className="flex-1 flex flex-col min-h-0 focus-visible:outline-none focus-visible:ring-0">
86+
<div className="px-6 py-4">
87+
<FilterBar
88+
search={search}
89+
onSearchChange={setSearch}
90+
activeType={activeType}
91+
onTypeChange={setActiveType}
92+
viewMode={viewMode}
93+
onViewModeChange={setViewMode}
94+
sortMode={sortMode}
95+
onSortModeChange={setSortMode}
96+
/>
14697
</div>
147-
) : (
148-
<div className="space-y-2">
149-
{filtered.map((source: MemorySource) => (
150-
<SourceCard key={source.id} source={source} variant="list" />
151-
))}
98+
99+
<div className="flex-1 overflow-y-auto px-6 pb-6">
100+
{timelineLoading ? (
101+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
102+
{[0, 1, 2, 3, 4, 5].map((i) => (
103+
<div
104+
key={i}
105+
className="rounded-2xl border-2 border-border bg-card p-4 h-36 animate-pulse"
106+
/>
107+
))}
108+
</div>
109+
) : error && sources.length === 0 ? (
110+
<div className="flex-1 flex items-center justify-center">
111+
<div className="flex flex-col items-center text-center gap-3">
112+
<div className="size-10 rounded-xl bg-accent flex items-center justify-center">
113+
<FileTextIcon />
114+
</div>
115+
<p className="text-xs text-muted-foreground max-w-[28ch]">
116+
Could not load sources. Make sure the API is running.
117+
</p>
118+
</div>
119+
</div>
120+
) : filtered.length === 0 ? (
121+
<div className="flex flex-col items-center justify-center py-16 text-center gap-3">
122+
<div className="size-10 rounded-xl bg-accent flex items-center justify-center">
123+
<FileTextIcon />
124+
</div>
125+
<p className="text-xs text-muted-foreground max-w-[28ch]">
126+
{search || activeType !== "all"
127+
? "No sources match your filters."
128+
: "Capture a journal, voice note, or link to start your library."}
129+
</p>
130+
</div>
131+
) : viewMode === "grid" ? (
132+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
133+
{filtered.map((source: MemorySource) => (
134+
<SourceCard key={source.id} source={source} variant="grid" />
135+
))}
136+
</div>
137+
) : (
138+
<div className="space-y-2">
139+
{filtered.map((source: MemorySource) => (
140+
<SourceCard key={source.id} source={source} variant="list" />
141+
))}
142+
</div>
143+
)}
152144
</div>
153-
)}
154-
</div>
145+
</TabsContent>
146+
147+
<TabsContent value="journal" className="flex-1 overflow-y-auto focus-visible:outline-none focus-visible:ring-0">
148+
<JournalPage />
149+
</TabsContent>
150+
151+
<TabsContent value="voice" className="flex-1 overflow-y-auto focus-visible:outline-none focus-visible:ring-0">
152+
<VoicePage />
153+
</TabsContent>
154+
155+
<TabsContent value="mail" className="flex-1 overflow-hidden focus-visible:outline-none focus-visible:ring-0">
156+
<MailShell />
157+
</TabsContent>
158+
159+
<TabsContent value="inbox" className="flex-1 overflow-y-auto focus-visible:outline-none focus-visible:ring-0">
160+
<MemoryInboxPage />
161+
</TabsContent>
162+
</Tabs>
155163
</div>
156164
);
157165
}

0 commit comments

Comments
 (0)