feat(kb-ui): humanise provenance "Added by" via an injected resolver
ProvenancePanel/ObjectViewer gain an optional resolvePrincipal(p) prop so the
consumer can turn a {type,id} principal ref into a human label — the lib can't
know who "you" is. Falls back to the raw type·id when the resolver returns
undefined, so existing callers are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import type { ObjectOutline, ObjectSummary, OutlineSection, TextSlice } from "./types";
|
import type { ObjectOutline, ObjectSummary, OutlineSection, PrincipalRef, TextSlice } from "./types";
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
SensitivityBadge,
|
SensitivityBadge,
|
||||||
@@ -160,11 +160,20 @@ export const CatalogCard: FC<{ outline: ObjectOutline; className?: string }> = (
|
|||||||
|
|
||||||
// ---- ProvenancePanel ------------------------------------------------------
|
// ---- ProvenancePanel ------------------------------------------------------
|
||||||
|
|
||||||
export const ProvenancePanel: FC<{ outline: ObjectOutline; className?: string }> = ({ outline, className }) => {
|
export const ProvenancePanel: FC<{
|
||||||
|
outline: ObjectOutline;
|
||||||
|
/** Turn a principal ref into a human label ("You", an agent's name, …). The
|
||||||
|
* lib can't know who "you" is, so the app supplies this; falls back to the
|
||||||
|
* raw type·id when it returns undefined. */
|
||||||
|
resolvePrincipal?: (p: string | PrincipalRef) => string | undefined;
|
||||||
|
className?: string;
|
||||||
|
}> = ({ outline, resolvePrincipal, className }) => {
|
||||||
const p = outline.provenance;
|
const p = outline.provenance;
|
||||||
|
const addedBy =
|
||||||
|
(p.created_by != null ? resolvePrincipal?.(p.created_by) : undefined) ?? formatPrincipal(p.created_by) ?? "—";
|
||||||
const rows: [string, ReactNode][] = [
|
const rows: [string, ReactNode][] = [
|
||||||
["Source", p.source_ref ? `${p.source_type} · ${p.source_ref}` : p.source_type],
|
["Source", p.source_ref ? `${p.source_type} · ${p.source_ref}` : p.source_type],
|
||||||
["Added by", formatPrincipal(p.created_by) ?? "—"],
|
["Added by", addedBy],
|
||||||
["Added", formatDate(p.inserted_at)],
|
["Added", formatDate(p.inserted_at)],
|
||||||
["Effective", p.effective_at ? formatDate(p.effective_at) : "—"],
|
["Effective", p.effective_at ? formatDate(p.effective_at) : "—"],
|
||||||
["Last verified", p.verified_at ? formatDate(p.verified_at) : "Not confirmed"],
|
["Last verified", p.verified_at ? formatDate(p.verified_at) : "Not confirmed"],
|
||||||
@@ -499,6 +508,8 @@ export interface ObjectViewerProps {
|
|||||||
readText: TextReaderProps["readText"];
|
readText: TextReaderProps["readText"];
|
||||||
/** Resolve a browser-openable blob URL (for images + download). */
|
/** Resolve a browser-openable blob URL (for images + download). */
|
||||||
resolveBlobUrl?: () => Promise<string>;
|
resolveBlobUrl?: () => Promise<string>;
|
||||||
|
/** Humanise the "Added by" principal (see ProvenancePanel). */
|
||||||
|
resolvePrincipal?: (p: string | PrincipalRef) => string | undefined;
|
||||||
highlight?: { start: number; end: number };
|
highlight?: { start: number; end: number };
|
||||||
onOpenObject?: (objectId: string, span?: { start: number; end: number }) => void;
|
onOpenObject?: (objectId: string, span?: { start: number; end: number }) => void;
|
||||||
actions?: ObjectActions;
|
actions?: ObjectActions;
|
||||||
@@ -509,6 +520,7 @@ export const ObjectViewer: FC<ObjectViewerProps> = ({
|
|||||||
outline,
|
outline,
|
||||||
readText,
|
readText,
|
||||||
resolveBlobUrl,
|
resolveBlobUrl,
|
||||||
|
resolvePrincipal,
|
||||||
highlight,
|
highlight,
|
||||||
onOpenObject,
|
onOpenObject,
|
||||||
actions,
|
actions,
|
||||||
@@ -602,7 +614,7 @@ export const ObjectViewer: FC<ObjectViewerProps> = ({
|
|||||||
<OutlineNav sections={outline.outline} activeId={activeSection} onSelect={(s) => setActiveSection(s.id)} />
|
<OutlineNav sections={outline.outline} activeId={activeSection} onSelect={(s) => setActiveSection(s.id)} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ProvenancePanel outline={outline} />
|
<ProvenancePanel outline={outline} resolvePrincipal={resolvePrincipal} />
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user