Workspace Open Page – Implementation Spec
Historical design reference: Figma – node 4013-20296
Use this doc and the current implementation as the source of truth. The Figma link above is legacy context only.
Target file: ciq-mvp2-app/src/app/account/workspaces/[workspaceId]/page.tsx
Summary of changes
Section titled “Summary of changes”Use the target layout documented here: header row (title + status chips) → tabs → page-owned toolbar in the same bordered top section → group section + KB cards.
1. Header block (below breadcrumbs)
Section titled “1. Header block (below breadcrumbs)”- Left: Workspace avatar (36×36, rounded 6px, bg #f7f7f7, border #ccc) with
photo_cameraicon if no image, else workspace image. Then workspace name: 22px, semibold, tracking -0.22px, black. - Right: Bordered container (border #e5e5e5, rounded 8px) with:
- Description control (required): A clickable 36×36px area with the Material Symbols
notesicon (~20px) in a muted color (e.g. #4d4d4d). This control must appear before the vertical divider and before the app status chip. Clicking it opens the Description popover. - Divider (1px vertical #f0f0f0).
- App status chip (14px):
widgetsicon +Not activatedwhen no apps are connected.widgetsicon + app names separated by vertical dividers when apps are connected.
- Description control (required): A clickable 36×36px area with the Material Symbols
Breadcrumbs stay as now (Account > Workspaces > [Name]); they may be rendered by the shell, so keep the existing shell behavior if already present.
1.1 Description popover
Section titled “1.1 Description popover”When the user clicks the Description control, show a popover (anchored to the description button) that:
- Content: Shows the workspace description text (e.g.
workspace.description). If the workspace has no description, show a short placeholder such as “No description” (or leave the body empty and show that message). - Placement: Anchored to the trigger (typically below the button, aligned to its edge). It appears as a small panel next to the trigger — not a centered dialog/modal.
- No overlay: Do not show a full-screen overlay/backdrop.
- Dismissal: Clicking outside the popover or pressing Escape closes it. An explicit Close control inside the popover is optional (may be kept for accessibility), but outside-click + Escape is sufficient.
- Styling (use the same surface styling as the previous modal spec, but as a compact popover panel):
- Surface: white background, border #e5e5e5, rounded corners, shadow.
- Body: ~16px padding, 14px body text.
- Footer: optional; if present, may contain a subtle Close control.
2. Tabs
Section titled “2. Tabs”- Container:
bg-[#fafafa](neutral/20), rounded 12px, p-1, flex gap-1. - Tabs: “Knowledge Bases” (icon
database, show live KB count in label e.g.Knowledge Bases (5)), “Agents” (iconrobot, show live total agents count), “Channels” (icongraph_4, show live workspace connections count), “Users” (icongroup, show live users count), “Activity log” (iconfingerprint). 14px, medium weight. - Active tab: white bg, rounded 8px, shadow
0px 1px 2px 0px rgba(0,0,0,0.25), text #4d4d4d. - Inactive: text #808080, hover #333. No background.
- Use client state for active tab (e.g.
knowledge-bases|users|user-activity).
3. Toolbar
Section titled “3. Toolbar”For workspace tabs (Knowledge Bases, Agents, Channels, Users, Activity log), the toolbar must appear as part of the same visual top section as the header and tabs, separated only by spacing, not by another divider. The page should present a single divider below that combined top section.
- Left: “All groups” control (stacks icon + label + chevron). Use existing
GroupFilterComboboxor a simple Select; for MVP, if no groups in scope, show a read-only pill “All groups” or single group name. Then a filter icon button (filter_list), rounded 8px, border #ccc. - Right: Optional sort icon (
discover_tune), then view switcher (grid_view / dehaze / graph_1) in a pill (bg #f7f7f7, p-0.5, rounded 8px), then primary button “Add knowledge base” (black bg, white text, rounded 8px, 14px medium). View switcher and Add button are required; sort can be placeholder.
4. Content area
Section titled “4. Content area”- Group topper: Reusable
GroupBadgecomponent (_components/group-badge.tsx): shadcn Badge (variantsecondary, solid) with group name; on hover, three-dots menu with Rename, Add group, Delete. On this page only, Add group adds a KB group (workspace-scoped); validation is against KB group names in this workspace only (so a name that exists at the workspace level, e.g. “test”, is allowed). Rename and Delete apply to the workspace group. Same component is used on the Workspaces page for each group header. When the current workspace’s group is not in state, fallback to a plain Badge with group name only (no menu). - KB cards grid: Use the shared
KnowledgeBaseCardcomponent from@/app/account/knowledge-bases/_components/knowledge-base-card— the same component used on the Knowledge Bases page. Do not define a local card component.- Grid class:
grid grid-cols-[repeat(auto-fill,minmax(min(100%,360px),360px))] gap-4(matches Knowledge Bases page). - Enrichment: Call
enrichKnowledgeBasesWithChannels(kbs, safeConnections)wheresafeConnections = isConnectionsLoading ? [] : connections(fromuseChannels()). Pass the enriched array to the cards. - Props per card:
kb— enriched KB objectworkspaceName—workspace.nameopenHref—`/account/knowledge-bases/${kb.id}`onEdit— navigate toopenHrefonSetAsMain—actions.updateWorkspace(workspace.id, { defaultKbId: kb.id })isDefaultKb—workspace.defaultKbId === kb.idonDuplicate—actions.addKnowledgeBase({ ...kb, id: \${kb.id}-copy-${Date.now()}`, name: `${kb.name} (Copy)` })`onDelete—actions.deleteKnowledgeBase(kb.id)canDelete—kbs.length > 1(raw unenriched list length)
- Overflow menu: Full four-item menu — Edit, Set as default (disabled when
isDefaultKb), Duplicate, Delete (disabled when!canDelete). Identical to the Knowledge Bases page. - Empty state: If
kbs.length === 0, show<p className="text-[14px] text-muted-foreground">No knowledge bases in this workspace.</p>. - Details content scope: KB card Details render KB-local fields only (
Objectives,Documents).
- Grid class:
- Remove the old local
KbCardcomponent andDETAIL_LABELSconstant entirely frompage.tsx.
4.1 Node view (Knowledge Bases tab)
Section titled “4.1 Node view (Knowledge Bases tab)”- Add third view mode
nodein the same switcher as card/table. - Selecting
nodeopens the graph in a bottom sheet (sidebottom, full viewport height) instead of inline content. - The sheet header does not include a workspace dropdown.
- The workspace node header acts as the single workspace dropdown select for the node view, so users switch workspace directly from the node card.
- Workspace dropdown behavior:
- searchable input (
Search workspace...) for quick lookup - selecting another workspace updates the node graph in-place inside the open sheet (no sheet close/reopen animation)
- after workspace selection, the graph auto-fits to the viewport so the new network is immediately readable
- searchable input (
- Visualize workspace information in node view:
Workspacenode (root), showing compact workspace-card data groups in this exact order:Knowledge bases: <num>Agents: <num>Users: <num>Channels: <num>Apps: <labels>(comma-separated connected app names, orNot activated)
- Current implementation renders a horizontal row of node cards:
WorkspacenodeKnowledge Baseslist nodeAgentslist nodeUserslist nodeChannelslist node- one right-most
KBdetail node (Objectives,Documents)
- Initial state renders the
Workspace,Knowledge Bases,Agents,Users,Channels, andAppsnodes together. All five sections are open on first load. Clicking section rows in the workspace node toggles visibility of related nodes:Knowledge bases->Knowledge Baseslist +KBdetail nodeAgents->Agentslist nodeUsers->Userslist nodeChannels->Channelslist node
- Channels list rows use the six standard channel families (
Social comments,Review platforms,FB Messenger,Web chat,WhatsApp,SMS), but the node view only renders families whose live workspace-scoped count is greater than zero. - On sheet open, auto-fit the graph to the sheet viewport so users land on a readable full-network view by default.
- Node cards are draggable. Bezier edge connectors are rendered between each workspace section row and its corresponding visible collection node (
Knowledge Bases,Agents,Users,Channels). Edges appear only when the target collection node is visible and are removed when the section is toggled off. - Group filter respect: When the node sheet is showing the current page workspace, the graph displays only the knowledge bases that match the active KB group filter. When a different workspace is selected in the sheet dropdown, all KBs for that workspace are shown (no group filter applied).
- Closing the bottom sheet resets the toggle back to card view.
Data sources for workspace node metrics
Section titled “Data sources for workspace node metrics”| Metric | Source |
|---|---|
| Knowledge bases | Count of KBs passed to node view (post group-filter when sheet = page workspace) |
| Agents | getAgentsForWorkspace(workspaceId).length from AgentsContext — not KB-level agentsSetCount |
| Users | workspace.numberOfUsers (kept in sync with Users tab list length) |
| Channels | Total connections scoped to the node-sheet workspace KBs |
| Apps | Derived via getWorkspaceAppsFromKbsAndChannels + WORKSPACE_APP_LABEL |
4.2 Channels tab (workspace-scoped)
Section titled “4.2 Channels tab (workspace-scoped)”- Purpose: operational channels surface for the current workspace only.
- Reuses global channels feature actions and dialogs:
- Add channel wizard
- Connection settings dialog
- Delete connection confirmation
- Scope rule: show only connections linked to knowledge bases belonging to the current workspace.
- Filters:
- knowledge base multi-select
- platform multi-select for social/review tabs
- Tabs follow channel families:
social-comments,review-platforms,facebook-messenger,web-chat,whatsapp,sms- each tab label shows the current total count for that channel family in scope (e.g.
Social comments (3))
KB column behavior per channel family
Section titled “KB column behavior per channel family”The Knowledge base column behaves differently depending on the channel family — see the full ownership model in Account – Channels → “Channel ownership model: Agent → KB → Channel”.
| Channel family | KB column | Editable? |
|---|---|---|
| Social comments | Direct KB on connection | Yes — inline selector |
| Review platforms | Direct KB on connection | Yes — inline selector |
| FB Messenger | Inherited from assigned agent | No — read-only |
| Web chat | Inherited from assigned agent | No — read-only |
| Inherited from assigned agent | No — read-only | |
| SMS | Inherited from assigned agent | No — read-only |
For agent-managed families (FB Messenger, Web chat, WhatsApp, SMS), the KB cell displays the name of the KB owned by the connection’s assigned agent, or — when no agent is assigned yet. Users must go to the Agents tab to change the KB for these channels.
Auto-agent creation side-effect
Section titled “Auto-agent creation side-effect”On mount (and whenever agents or connections change), the Channels tab checks whether any agent-managed connections (facebook-messenger, web-chat, whatsapp, sms) in this workspace have a linked KB with no agent assigned. For each such KB, it automatically calls createAgent to provision a default agent named "{KB name} Agent" with no channel connections.
- This runs once per
workspaceId + kbIdpair per session (tracked viaattemptedAutoAgentKeysRef). - If
createAgentfails for a pair, the attempt key is removed so it can be retried. - The provisioned agent starts with
channelIds: []and must be configured manually in the Agents tab. - This side-effect is silent — no toast or UI feedback is shown to the user.
4.3 Agents tab (workspace-scoped)
Section titled “4.3 Agents tab (workspace-scoped)”Full agent lifecycle management surface. See Workspace – Agents Tab for the complete spec.
Summary:
- Toolbar: status filter (All / Active / Inactive) + “Add agent” primary CTA.
- Summary badges:
Total agentsandActive agents(counts fromAgentsContext). - Agents table: Name, Knowledge base (inline editable select with Sonner confirm toast), Active (toggle switch), Channels (count badge with hover card), Actions (Edit / Delete row dropdown).
- Edit/Create:
AgentModalwith a single General section only. - Delete:
DeleteAgentDialogconfirmation. Delete is blocked for active agents — the row shows a disabled “Delete (deactivate first)” menu item; the context also enforcesAGENT_ACTIVE_DELETE_BLOCKED. - Data source:
getAgentsForWorkspace(workspaceId)fromAgentsContext(not KB-levelagentsSetCount).
KB ownership role of agents: For agent-managed channel families (FB Messenger, Web chat, WhatsApp, SMS), the agent is the owner of the Knowledge base. Changing an agent’s KB here propagates to all connections assigned to that agent — the KB column in those channel rows updates automatically. This is the correct place to change the effective KB for any agent-managed channel. See Account – Channels → “Channel ownership model: Agent → KB → Channel” for the full model.
5. Styling and components
Section titled “5. Styling and components”- Use existing
Iconcomponent (Material Symbols) and project Tailwind conventions. - Reuse
GroupFilterComboboxorSelectfrom workspaces for “All groups” if groups are available; otherwise a static pill. - Colors: border #e5e5e5, #ebebeb; bg #f7f7f7, #fafafa; text #333, #666, #808080; red #ef4444; primary black #0d0d0d.
- Spacing: section padding 24px (
p-6); gaps 8px–12px.
6. Not-found state
Section titled “6. Not-found state”Keep existing “Workspace not found” block and “Back to Workspaces” link unchanged.
7. Data
Section titled “7. Data”- Workspace: from
selectors.getWorkspace(workspaceId)(with seed fallback whenstate.version === 0). - Group name: from
selectors.getGroupName(workspace.groupId)(withSEED_GROUPSfallback). - KB list:
selectors.getKnowledgeBasesForWorkspace(workspace.id)— real KB list from context, enriched with channel counts viaenrichKnowledgeBasesWithChannels. - Connections:
useChannels().connectionsanduseChannels().isConnectionsLoading. UsesafeConnections = isConnectionsLoading ? [] : connectionsto avoid showing stale counts. - Workspace channels scope: derive from workspace KB ids when rendering Channels tab.
- Workspace agents stage data (Agents tab): aggregate
knowledgeBase.agentsSetCount. - Node view agents count:
getAgentsForWorkspace(workspaceId).lengthfromAgentsContext— real agent count, not KB-levelagentsSetCount. - Workspace users count parity:
workspace.numberOfUsersis kept in sync with the Users tab list length, so card/table/node metrics use the same users total.
Global-vs-scoped responsibility split:
- Global hubs:
/account/channels: all workspaces/account/agents: all workspaces (currently coming soon)
- Workspace route:
- scoped operational context for one workspace (
/account/workspaces/[workspaceId])
- scoped operational context for one workspace (
Coder: Implement the above in src/app/account/workspaces/[workspaceId]/page.tsx. Preserve not-found handling and client-side only. Remove the local KbCard component and DETAIL_LABELS constant; replace with the shared KnowledgeBaseCard component with channel enrichment and full overflow menu as specified in Section 4.
8. Users tab
Section titled “8. Users tab”The Users tab shows a table of users belonging to this workspace. For MVP, data is mock-only (local React state seeded from SEED_WORKSPACE_USERS).
8.1 Data model
Section titled “8.1 Data model”WorkspaceUser (in src/types/workspaces.ts):
| Field | Type | Notes |
|---|---|---|
id | string | Unique user id |
name | string | Display name |
email | string | Email address |
avatarUrl | string? | Optional avatar URL |
workspaceRole | "admin" | "member" | Role within this workspace |
status | "active" | "invited" | "inactive" | Membership status |
lastActive | string? | ISO date of last activity; omit if never active |
joinedAt | string | ISO date when user joined or was invited |
8.2 Table columns
Section titled “8.2 Table columns”Above the users table, show a local section header titled Users plus a live table-item count and a view_column control button aligned to the right.
- The
view_columnbutton opens a popover with per-column toggles, matching the table column-visibility pattern used on other account list surfaces. - The first column (
user) is always visible and its toggle is disabled.
| Column | Always visible | Notes |
|---|---|---|
user | Yes | Avatar initial (or image) + name + email |
workspaceRole | No | Badge: Admin (primary tint) / Member (muted) |
status | No | Badge: Active (success), Invited (warning), Inactive (muted) |
lastActive | No | Relative date (e.g. “2d ago”) or em-dash if null |
| actions | Yes | Right-aligned row dropdown; visible on row hover |
Table headers are sortable (click header to toggle asc/desc) for: user, workspaceRole, status, and lastActive.
Deferred from MVP: Groups, Knowledge base access, Joined date.
8.3 Toolbar
Section titled “8.3 Toolbar”- Left:
Rolesingle-select filter (All rolesdefault,Admin,Member) that filters the Users table byworkspaceRole. - Right:
Invite userprimary button withperson_addicon; usesPAGE_LIST_TOOLBAR_CTA_CLS.
8.4 Row actions
Section titled “8.4 Row actions”- Change role — toggles the user’s
workspaceRolebetweenadminandmember. - Remove from workspace — opens a confirmation dialog (
RemoveUserDialog); disabled (and visually muted) when the row is the last admin in the workspace.
8.5 Dialogs
Section titled “8.5 Dialogs”InviteUserDialog(_components/invite-user-dialog.tsx): email input with basic validation; on submit, adds a newWorkspaceUserwithstatus: "invited"to local state.RemoveUserDialog(_components/remove-user-dialog.tsx): destructive confirmation; on confirm, removes the user from local state.
8.6 Empty state
Section titled “8.6 Empty state”When users.length === 0, the table body shows: No users in this workspace.
8.7 Pagination
Section titled “8.7 Pagination”Uses TablePagination component; default 25 rows per page.
8.8 Implementation files
Section titled “8.8 Implementation files”| File | Purpose |
|---|---|
src/types/workspaces.ts | WorkspaceUser type |
src/app/account/workspaces/_data/seed.ts | SEED_WORKSPACE_USERS mock data (4 users) |
src/app/account/workspaces/[workspaceId]/_components/workspace-users-table.tsx | Table component |
src/app/account/workspaces/[workspaceId]/_components/invite-user-dialog.tsx | Invite dialog |
src/app/account/workspaces/[workspaceId]/_components/remove-user-dialog.tsx | Remove confirmation dialog |
src/app/account/workspaces/[workspaceId]/page.tsx | State wiring and tab rendering |
9. Activity log tab
Section titled “9. Activity log tab”The Activity log tab is implemented as an MVP insight surface (mock event generation from workspace context).
9.1 Filter bar
Section titled “9.1 Filter bar”- Date range chips:
24h,7d,30d(single-select) - User filter: single-select (
All users+ workspace users) - Event type filter: single-select (
All events+ known event types)
9.2 Summary badges
Section titled “9.2 Summary badges”Five secondary badges:
Total eventsUnique actorsKB changesChannel changesAccess changes
9.3 Main content layout
Section titled “9.3 Main content layout”- Two-column grid:
- Left:
Activity feedlist (actor, action/entity summary, timestamp, source, status badge) - Right:
Activity by userranking bars
- Left:
- Empty state text when no events match current filters:
No activity for selected filters.
9.4 Data model and scope
Section titled “9.4 Data model and scope”- Event records are generated client-side from current workspace data (users, KBs, channel connections).
- This tab is MVP mock data, not a backend audit log endpoint.