Skip to content

Component: Knowledge Base Card

Display KB identity, status, linkage, and KB-local detail counts in a standard shadcn Card. Supports navigation to KB detail via optional openHref.

  1. Section 1 — Header (2-line layout):
    • Line 1: Material icon database + KB name (title, flex-1 min-w-0 truncate) + three-dots overflow menu trigger (more_horiz icon, opacity-0 group-hover:opacity-100) — flex row, items-center.
    • Line 2: Workspace name + optional group Badge — rendered on the same line below the title row. When groupName is provided, a <Badge variant="secondary"> appears inline after the workspace name.
    • The icon must not span both lines; it aligns only with the title on line 1.
  2. Section 2: KB status (Incomplete / Complete), Linkage (Independent / Linked).
  3. Section 3 (small title “Details”): KB-local values only — Objectives, Documents.
CardHeader (flex-col, p-4 pb-4, border-b border-border)
├── div.title-row (flex flex-row items-center gap-3)
│ ├── [when isDefaultKb] Tooltip > (icon wrapper + h3) else span.database + h3
│ │ icon wrapper: relative shrink-0; database 24px; [when isDefaultKb] circle marker size-1.5 rounded-full bg-foreground absolute -right-0.5 -top-0.5 (theme-aware)
│ ├── h3 (flex-1 min-w-0 truncate text-[16px] font-semibold leading-tight) ← KB name
│ └── div.menu-trigger (relative flex-shrink-0, opacity-0 group-hover:opacity-100)
│ ├── button (more_horiz icon, 20px)
│ └── [when open] overlay (fixed inset-0 z-10) + dropdown (absolute right-0 top-full z-20)
└── div (mt-1 flex items-center gap-2) ← workspace + group row
├── p (text-[14px] text-muted-foreground) ← workspace name
└── [when groupName] Badge variant="secondary" ← KB group name
  • CardHeader switches from flex-row to flex-col so the two lines stack.
  • The inner div.title-row is flex flex-row items-center gap-3 — this centers the icon with the title text.
  • The workspace <p> sits outside div.title-row, naturally flowing to a new line.
  • Remove items-start from CardHeader; it is no longer needed once the header is flex-col.
  • CardHeader has a 1px bottom border (border-b border-border) matching the card’s own border color (--border theme token). This visually separates the header from the card body.

When the KB is the workspace default: (1) the lead (database) icon shows a small circular marker at its top-right: size-1.5 rounded-full bg-foreground, absolute -right-0.5 -top-0.5. The marker uses bg-foreground so its color follows the active theme (e.g. dark in light theme, light in dark theme). (2) The title section (icon + KB name) is wrapped in a tooltip; on hover, the hint “Default knowledge base” is shown (Tooltip from @/components/ui/tooltip, TooltipContent side="top").

All section small titles — KB status, Linkage, Details — use the same canonical style:

mb-1 text-[13px] font-semibold text-zinc-950

No section title should deviate in margin, weight, or color. (The “Details” title was previously inconsistent — mb-2 font-medium text-[#666] — and must be corrected to match.)

  • Inputs:
    • kb (KnowledgeBase) — the enriched KB record.
    • workspaceName (string) — display name of the owning workspace.
    • groupName? (string) — display name of the KB group (workspace-scoped). When provided, shown as a Badge after the workspace name in the card header. Resolved by the parent from kbGroups via kb.groupId.
    • openHref? (string) — when set, clicking the card (outside the menu trigger) navigates here.
    • onEdit? (kb) => void — called when Edit is clicked; falls back to openHref navigation if omitted.
    • onSetAsMain? (kb) => void — called to set this KB as the workspace default.
    • isDefaultKb? (boolean) — disables “Set as default” when true (this KB is already the default).
    • onDuplicate (kb) => void — called to duplicate the KB.
    • onDelete (kb) => void — called to delete the KB.
    • canDelete (boolean) — disables Delete when false (workspace has only one KB).
    • className? (string) — optional CSS class override on the root <Card>.
  • Outputs: Card click navigates to openHref when provided (and menu is not open). Menu actions invoke the corresponding callbacks.

Note on data: The card uses KB-local fields only (kbDraft/objectivesCount, kbDocuments/documentsCount) and does not render channel/app aggregates.

The card shows a three-dots (more_horiz) button in the header row on hover. The button is hidden by default (opacity-0) and becomes visible when the card is hovered (group-hover:opacity-100). Clicking the button opens an inline dropdown popover.

ActionIconBehaviourDisabled when
EditeditCalls onEdit(kb) if provided; parent opens the Edit knowledge base dialog. If onEdit is omitted, falls back to navigating to openHref. Closes menu.Never
Set as defaultstarCalls onSetAsMain(kb). Parent calls updateWorkspace(kb.workspaceId, { defaultKbId: kb.id }). Closes menu.isDefaultKb === true (this KB is already the workspace default)
Duplicatecontent_copyCalls onDuplicate(kb). Parent creates a copy with name "{kb.name} (Copy)" and same workspaceId. Closes menu.Never
(divider)Visual border-t separator
DeletedeleteCalls onDelete(kb). Closes menu. Styled destructive (text-destructive).canDelete === false (workspace has only one KB — see business rule 7 in Default Workspace, Knowledge Base & Agent)

w-[180px], rounded-[10px], border border-border, bg-popover, shadow-md, absolute right-0 top-full mt-1 z-20. A fixed inset-0 z-10 overlay closes the menu on outside click. Escape key also closes the menu.

When openHref is set, the card is clickable. Clicks on the menu trigger (menuTriggerRef) are ignored (via stopPropagation on the trigger div and a contains check in handleCardClick). Clicks while the menu is open are also ignored.

  • Edit: When user clicks Edit, parent sets state to open the Edit knowledge base dialog with the selected KB. On dialog submit, parent calls actions.updateKnowledgeBase(kbId, { name, workspaceId }) and, if the user changed the workspace’s group in the dialog, actions.updateWorkspace(workspaceId, { groupId }); then closes the dialog.
  • canDelete: true when the workspace (kb.workspaceId) has more than one KB in the full context KB list (not the filtered list).
  • isDefaultKb: true when workspace.defaultKbId === kb.id (look up workspace via selectors.getWorkspace(kb.workspaceId)).
  • onSetAsMain: calls actions.updateWorkspace(kb.workspaceId, { defaultKbId: kb.id }).
  • onDuplicate: calls actions.addKnowledgeBase({ ...kb, id: newId, name: \${kb.name} (Copy)` })`.
  • onDelete: calls actions.deleteKnowledgeBase(kb.id).
  • Status badge: Incomplete (orange) vs Complete (green)
  • Default: KB shown
  • Loading: skeleton
  • Error: cannot load KB summary
  • Disabled: user lacks kb.view
  • Clickable card must be keyboard-accessible
  • Badges must have text equivalents

Security & privacy considerations (if applicable)

Section titled “Security & privacy considerations (if applicable)”
  • Do not expose KB internal content on the card
  • kb.card.clicked (optional)