diff --git a/apps/web/src/features/workbench/ChannelContent.svelte b/apps/web/src/features/workbench/ChannelContent.svelte new file mode 100644 index 0000000..9e81962 --- /dev/null +++ b/apps/web/src/features/workbench/ChannelContent.svelte @@ -0,0 +1,30 @@ + + +{#if channel.type === 'overview'} + +{:else if channel.type === 'inbox'} + +{:else if channel.type === 'tasks'} + +{:else if channel.type === 'ai_sessions'} + +{:else if channel.type === 'notes_sources'} + +{:else if channel.type === 'cron'} + +{:else} + +{/if} diff --git a/apps/web/src/features/workbench/ChannelContent.test.ts b/apps/web/src/features/workbench/ChannelContent.test.ts new file mode 100644 index 0000000..41c4368 --- /dev/null +++ b/apps/web/src/features/workbench/ChannelContent.test.ts @@ -0,0 +1,39 @@ +import '@testing-library/jest-dom/vitest'; +import { fireEvent, render, screen } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; +import ChannelContent from './ChannelContent.svelte'; +import { getProjectWorkspace } from './mockData'; + +describe('ChannelContent', () => { + const workspace = getProjectWorkspace(1); + + it('renders different templates for system and custom channels', () => { + for (const type of ['overview', 'inbox', 'tasks', 'ai_sessions', 'notes_sources', 'cron', 'custom_link'] as const) { + const channel = workspace.channels.find((item) => item.type === type); + if (!channel) throw new Error(`missing ${type}`); + + render(ChannelContent, { props: { workspace, channel, onInspect: () => {} } }); + } + + expect(screen.getByText('Open external channel')).toBeInTheDocument(); + }); + + it('sends selected task details to inspector', async () => { + let inspectedTitle = ''; + const channel = workspace.channels.find((item) => item.type === 'tasks'); + if (!channel) throw new Error('missing task channel'); + + render(ChannelContent, { + props: { + workspace, + channel, + onInspect: (item) => { + inspectedTitle = item.title; + }, + }, + }); + + await fireEvent.click(screen.getByRole('button', { name: 'Inspect Confirm homepage information architecture' })); + expect(inspectedTitle).toBe('Confirm homepage information architecture'); + }); +}); diff --git a/apps/web/src/features/workbench/ObjectInspector.svelte b/apps/web/src/features/workbench/ObjectInspector.svelte new file mode 100644 index 0000000..f36f387 --- /dev/null +++ b/apps/web/src/features/workbench/ObjectInspector.svelte @@ -0,0 +1,37 @@ + + + diff --git a/apps/web/src/features/workbench/ProjectWorkbench.svelte b/apps/web/src/features/workbench/ProjectWorkbench.svelte index 4ab9ffd..3d29c30 100644 --- a/apps/web/src/features/workbench/ProjectWorkbench.svelte +++ b/apps/web/src/features/workbench/ProjectWorkbench.svelte @@ -1,14 +1,19 @@
@@ -32,10 +41,8 @@ onSelectChannel={selectChannel} />
-

{workspace.channels.find((channel) => channel.id === selectedChannelID)?.title}

+
- +
diff --git a/apps/web/src/features/workbench/channels/AISessionsChannel.svelte b/apps/web/src/features/workbench/channels/AISessionsChannel.svelte new file mode 100644 index 0000000..3b52383 --- /dev/null +++ b/apps/web/src/features/workbench/channels/AISessionsChannel.svelte @@ -0,0 +1,27 @@ + + +
+

Project intelligence

AI Sessions

+
+
+ {#each sessions as session} + + {/each} +
+ {#if selectedSession} +

{selectedSession.title}

{selectedSession.summary}

References: {selectedSession.references.join(', ')}

+ {/if} +
+
diff --git a/apps/web/src/features/workbench/channels/CronChannel.svelte b/apps/web/src/features/workbench/channels/CronChannel.svelte new file mode 100644 index 0000000..7a6f2c3 --- /dev/null +++ b/apps/web/src/features/workbench/channels/CronChannel.svelte @@ -0,0 +1,19 @@ + + +
+

Scheduled work

Cron Plans

+
+ {#each plans as plan} +

{plan.title}

{plan.schedule} · {plan.nextRun}

{plan.enabled ? 'Enabled' : 'Disabled'} · {plan.lastResult}
+ {/each} +
+
diff --git a/apps/web/src/features/workbench/channels/CustomLinkChannel.svelte b/apps/web/src/features/workbench/channels/CustomLinkChannel.svelte new file mode 100644 index 0000000..0fa9111 --- /dev/null +++ b/apps/web/src/features/workbench/channels/CustomLinkChannel.svelte @@ -0,0 +1,16 @@ + + + diff --git a/apps/web/src/features/workbench/channels/InboxChannel.svelte b/apps/web/src/features/workbench/channels/InboxChannel.svelte new file mode 100644 index 0000000..2d25d63 --- /dev/null +++ b/apps/web/src/features/workbench/channels/InboxChannel.svelte @@ -0,0 +1,22 @@ + + +
+

Message Flow

Inbox

+
+ {#each messages as message} +
+
{message.source} · {message.time}

{message.title}

{message.summary}

{message.status} · #{message.tag}
+ +
+ {/each} +
+
diff --git a/apps/web/src/features/workbench/channels/NotesSourcesChannel.svelte b/apps/web/src/features/workbench/channels/NotesSourcesChannel.svelte new file mode 100644 index 0000000..256ef8b --- /dev/null +++ b/apps/web/src/features/workbench/channels/NotesSourcesChannel.svelte @@ -0,0 +1,19 @@ + + +
+

Project reference

Notes & Sources

+
+ {#each items as item} +
{item.kind}

{item.title}

{item.source} · {item.updatedAt} · #{item.tag}
+ {/each} +
+
diff --git a/apps/web/src/features/workbench/channels/OverviewChannel.svelte b/apps/web/src/features/workbench/channels/OverviewChannel.svelte new file mode 100644 index 0000000..6d478cf --- /dev/null +++ b/apps/web/src/features/workbench/channels/OverviewChannel.svelte @@ -0,0 +1,29 @@ + + +
+
+

{workspace.project.name}

+

Overview

+
+
+ {#each metrics(workspace) as metric} + + {/each} +
+
diff --git a/apps/web/src/features/workbench/channels/TasksChannel.svelte b/apps/web/src/features/workbench/channels/TasksChannel.svelte new file mode 100644 index 0000000..b59e47e --- /dev/null +++ b/apps/web/src/features/workbench/channels/TasksChannel.svelte @@ -0,0 +1,23 @@ + + +
+

Work Plan

Tasks

+
+ {#each tasks as task} +
+ +

{task.title}

{task.summary}

{task.owner} · {task.due} · #{task.tag}
+ +
+ {/each} +
+