@handled-ai/design-system 0.21.2 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/case-panel-activity-timeline.js +3 -3
- package/dist/components/case-panel-activity-timeline.js.map +1 -1
- package/dist/components/case-panel-detail.js +2 -2
- package/dist/components/case-panel-detail.js.map +1 -1
- package/dist/components/case-panel-why.js +1 -1
- package/dist/components/case-panel-why.js.map +1 -1
- package/dist/components/inbox-toolbar.js +2 -2
- package/dist/components/inbox-toolbar.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/case-panel-activity-timeline.test.tsx +1 -1
- package/src/components/__tests__/case-panel-detail.test.tsx +3 -3
- package/src/components/case-panel-activity-timeline.tsx +3 -3
- package/src/components/case-panel-detail.tsx +2 -2
- package/src/components/case-panel-why.tsx +1 -1
- package/src/components/inbox-toolbar.tsx +2 -2
|
@@ -17,7 +17,7 @@ const PAYLOAD_LABELS = {
|
|
|
17
17
|
deadline: "Deadline",
|
|
18
18
|
operatorNote: "Operator note",
|
|
19
19
|
assignment: "Assignment",
|
|
20
|
-
caseOpened: "
|
|
20
|
+
caseOpened: "Work item opened",
|
|
21
21
|
generic: "Update"
|
|
22
22
|
};
|
|
23
23
|
const STATUS_CLASSES = {
|
|
@@ -134,7 +134,7 @@ function ActorByline({ actor, timeLabel }) {
|
|
|
134
134
|
/* @__PURE__ */ jsx("span", { children: timeLabel })
|
|
135
135
|
] });
|
|
136
136
|
}
|
|
137
|
-
const verb = (_a = actor.verb) != null ? _a : "updated this
|
|
137
|
+
const verb = (_a = actor.verb) != null ? _a : "updated this work item";
|
|
138
138
|
const initial = actor.name.charAt(0).toUpperCase();
|
|
139
139
|
return /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-1.5 text-xs text-muted-foreground", "data-testid": "case-panel-activity-byline", children: [
|
|
140
140
|
actor.avatarUrl ? /* @__PURE__ */ jsx("img", { src: actor.avatarUrl, alt: actor.name, className: "h-4 w-4 rounded-full object-cover" }) : /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-muted-foreground/10 text-[8px] font-semibold text-muted-foreground", children: initial }),
|
|
@@ -269,7 +269,7 @@ function renderPayloadContent(payload, eventId, onPayloadAction) {
|
|
|
269
269
|
case "caseOpened":
|
|
270
270
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
271
271
|
/* @__PURE__ */ jsxs("p", { className: "text-foreground", children: [
|
|
272
|
-
"
|
|
272
|
+
"Work item opened",
|
|
273
273
|
payload.openedBy ? ` by ${payload.openedBy}` : "",
|
|
274
274
|
payload.source ? ` from ${payload.source}` : ""
|
|
275
275
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/case-panel-activity-timeline.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, ExternalLink } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { TONE_CLASSES, type TimelineEventTone } from \"./timeline-activity\"\n\nexport type CasePanelActivityTone = TimelineEventTone\n\nexport type CasePanelActivityActor =\n | { kind: \"system\" }\n | { kind: \"integration\"; name: string; iconUrl?: string }\n | { kind: \"user\"; name: string; avatarUrl?: string; verb?: string }\n\nexport type CasePanelPayloadAction = {\n kind: \"openSignal\"\n key: string\n eventId: string\n}\n\nexport type CasePanelActivityPayload =\n | {\n kind: \"signal\"\n key: string\n summary: string\n detail?: string\n actionLabel?: string\n }\n | {\n kind: \"scoreUpdate\"\n label?: string\n previousScore?: number\n nextScore: number\n reason?: string\n }\n | {\n kind: \"recommendation\"\n recommendation: string\n rationale?: string\n actionLabel?: string\n }\n | {\n kind: \"email\"\n from: string\n to?: string\n subject: string\n preview?: string\n body?: string\n }\n | {\n kind: \"salesforce\"\n objectLabel: string\n recordLabel?: string\n changeSummary: string\n deepLink?: {\n href: string\n label?: string\n }\n }\n | {\n kind: \"deadline\"\n dueLabel: string\n status?: \"upcoming\" | \"due\" | \"overdue\" | \"met\"\n description?: string\n /** 0..1 elapsed toward the deadline; renders a thin progress bar. */\n progress?: number\n }\n | {\n kind: \"operatorNote\"\n note: string\n }\n | {\n kind: \"assignment\"\n assignee: string\n from?: string\n role?: string\n }\n | {\n kind: \"caseOpened\"\n source?: string\n openedBy?: string\n description?: string\n }\n | {\n kind: \"generic\"\n description: string\n metadata?: Array<{ label: string; value: string }>\n }\n\nexport interface CasePanelActivityEvent {\n id: string\n title: string\n timeLabel: string\n tone: CasePanelActivityTone\n actor?: CasePanelActivityActor\n isSystemNoise?: boolean\n payload: CasePanelActivityPayload\n}\n\nexport interface CasePanelActivityTimelineProps {\n events: CasePanelActivityEvent[]\n className?: string\n title?: string\n defaultExpanded?: boolean\n defaultShowSystemEvents?: boolean\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n}\n\nconst NEUTRAL_DOT_CLASSES = \"bg-background border-border/60\"\nconst NEUTRAL_ICON_CLASSES = \"text-muted-foreground\"\n\nconst PAYLOAD_LABELS: Record<CasePanelActivityPayload[\"kind\"], string> = {\n signal: \"Signal\",\n scoreUpdate: \"Score update\",\n recommendation: \"Recommendation\",\n email: \"Email\",\n salesforce: \"Salesforce\",\n deadline: \"Deadline\",\n operatorNote: \"Operator note\",\n assignment: \"Assignment\",\n caseOpened: \"Case opened\",\n generic: \"Update\",\n}\n\nconst STATUS_CLASSES: Record<NonNullable<Extract<CasePanelActivityPayload, { kind: \"deadline\" }>[\"status\"]>, string> = {\n upcoming: \"border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900/40 dark:bg-blue-950/30 dark:text-blue-300\",\n due: \"border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/40 dark:bg-amber-950/30 dark:text-amber-300\",\n overdue: \"border-red-200 bg-red-50 text-red-700 dark:border-red-900/40 dark:bg-red-950/30 dark:text-red-300\",\n met: \"border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/40 dark:bg-emerald-950/30 dark:text-emerald-300\",\n}\n\nexport function CasePanelActivityTimeline({\n events,\n className,\n title = \"Activity\",\n defaultExpanded = false,\n defaultShowSystemEvents = false,\n onPayloadAction,\n}: CasePanelActivityTimelineProps) {\n const [expanded, setExpanded] = React.useState(defaultExpanded)\n const [showSystemEvents, setShowSystemEvents] = React.useState(defaultShowSystemEvents)\n\n const nonSystemEvents = events.filter((event) => !event.isSystemNoise)\n const visibleEvents = showSystemEvents ? events : nonSystemEvents\n const systemNoiseCount = events.length - nonSystemEvents.length\n const lastActivityText = nonSystemEvents[0]?.timeLabel ?? events[0]?.timeLabel ?? \"No activity yet\"\n\n return (\n <section className={cn(\"rounded-xl border border-border bg-card text-card-foreground shadow-sm\", className)}>\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/30\"\n aria-expanded={expanded}\n onClick={() => setExpanded((current) => !current)}\n >\n <span className=\"min-w-0\">\n <span className=\"flex items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n <span className=\"rounded-full border border-border bg-muted/40 px-2 py-0.5 text-[11px] font-medium text-muted-foreground\">\n {nonSystemEvents.length} {nonSystemEvents.length === 1 ? \"event\" : \"events\"}\n </span>\n </span>\n <span className=\"mt-1 block truncate text-xs text-muted-foreground\">Last activity {lastActivityText}</span>\n </span>\n <ChevronDown className={cn(\"h-4 w-4 shrink-0 text-muted-foreground transition-transform\", expanded && \"rotate-180\")} />\n </button>\n\n {expanded ? (\n <div className=\"border-t border-border px-4 py-4\">\n {systemNoiseCount > 0 ? (\n <label className=\"mb-4 flex items-center justify-between gap-3 rounded-lg border border-border/70 bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n <span>Show system events</span>\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border-border text-primary accent-current\"\n checked={showSystemEvents}\n onChange={(event) => setShowSystemEvents(event.target.checked)}\n />\n </label>\n ) : null}\n\n {visibleEvents.length > 0 ? (\n <div className=\"space-y-0\">\n {visibleEvents.map((event, index) => (\n <CasePanelActivityTimelineItem\n key={event.id}\n event={event}\n isLast={index === visibleEvents.length - 1}\n onPayloadAction={onPayloadAction}\n />\n ))}\n </div>\n ) : (\n <p className=\"rounded-lg border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground\">\n No activity to show.\n </p>\n )}\n </div>\n ) : null}\n </section>\n )\n}\n\nfunction CasePanelActivityTimelineItem({\n event,\n isLast,\n onPayloadAction,\n}: {\n event: CasePanelActivityEvent\n isLast: boolean\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n}) {\n const toneStyle = TONE_CLASSES[event.tone]\n const dotClasses = toneStyle ? toneStyle.dot : NEUTRAL_DOT_CLASSES\n const iconClasses = toneStyle ? toneStyle.icon : NEUTRAL_ICON_CLASSES\n\n return (\n <article className=\"group relative flex gap-3.5\" data-testid=\"case-panel-activity-event\">\n {!isLast ? <div className=\"absolute bottom-[-6px] left-[9px] top-5 w-px bg-border/60\" /> : null}\n <div className=\"relative z-10 mt-1 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-card\">\n <span\n aria-hidden=\"true\"\n className={cn(\"flex h-4.5 w-4.5 items-center justify-center rounded-full border ring-4 ring-card\", dotClasses, iconClasses)}\n data-testid=\"case-panel-activity-dot\"\n >\n <span className=\"h-1.5 w-1.5 rounded-full bg-current\" />\n </span>\n </div>\n <div className=\"min-w-0 flex-1 pb-5 pt-0.5\">\n <div className=\"flex min-w-0 flex-col gap-1 sm:flex-row sm:items-start sm:justify-between\">\n <h3 className=\"pr-4 text-[13px] font-medium leading-relaxed text-foreground\">{event.title}</h3>\n <time className=\"mt-0.5 shrink-0 whitespace-nowrap text-[11px] text-muted-foreground/70\">{event.timeLabel}</time>\n </div>\n <ActorByline actor={event.actor} timeLabel={event.timeLabel} />\n <PayloadCard event={event} onPayloadAction={onPayloadAction} />\n </div>\n </article>\n )\n}\n\nfunction ActorByline({ actor, timeLabel }: { actor?: CasePanelActivityActor; timeLabel: string }) {\n if (!actor || actor.kind === \"system\") return null\n\n if (actor.kind === \"integration\") {\n return (\n <div className=\"mt-1 flex items-center gap-1.5 text-xs text-muted-foreground\" data-testid=\"case-panel-activity-byline\">\n {actor.iconUrl ? <img src={actor.iconUrl} alt=\"\" className=\"h-4 w-4 rounded-sm object-cover\" /> : null}\n <span className=\"font-medium text-foreground\">{actor.name}</span>\n <span>synced this update</span>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{timeLabel}</span>\n </div>\n )\n }\n\n const verb = actor.verb ?? \"updated this case\"\n const initial = actor.name.charAt(0).toUpperCase()\n\n return (\n <div className=\"mt-1 flex items-center gap-1.5 text-xs text-muted-foreground\" data-testid=\"case-panel-activity-byline\">\n {actor.avatarUrl ? (\n <img src={actor.avatarUrl} alt={actor.name} className=\"h-4 w-4 rounded-full object-cover\" />\n ) : (\n <span className=\"flex h-4 w-4 items-center justify-center rounded-full bg-muted-foreground/10 text-[8px] font-semibold text-muted-foreground\">\n {initial}\n </span>\n )}\n <span className=\"font-medium text-foreground\">{actor.name}</span>\n <span>{verb}</span>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{timeLabel}</span>\n </div>\n )\n}\n\nfunction PayloadCard({\n event,\n onPayloadAction,\n}: {\n event: CasePanelActivityEvent\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n}) {\n const payload = event.payload\n\n return (\n <div className=\"mt-2 rounded-lg border border-border/80 bg-muted/20 px-3 py-2.5 text-sm\" data-testid={`case-panel-payload-${payload.kind}`}>\n <div className=\"mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground\">\n {PAYLOAD_LABELS[payload.kind]}\n </div>\n {renderPayloadContent(payload, event.id, onPayloadAction)}\n </div>\n )\n}\n\nfunction renderPayloadContent(\n payload: CasePanelActivityPayload,\n eventId: string,\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n) {\n switch (payload.kind) {\n case \"signal\":\n return (\n <div className=\"space-y-2\">\n <p className=\"text-foreground\">{payload.summary}</p>\n {payload.detail ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.detail}</p> : null}\n {onPayloadAction && payload.actionLabel ? (\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground transition-colors hover:text-foreground\"\n onClick={() => onPayloadAction({ kind: \"openSignal\", key: payload.key, eventId })}\n >\n {payload.actionLabel}\n </button>\n ) : null}\n </div>\n )\n case \"scoreUpdate\":\n return (\n <div className=\"space-y-1\">\n <p className=\"text-foreground\">\n {payload.label ?? \"Score\"}: {payload.previousScore !== undefined ? `${payload.previousScore} → ` : \"\"}{payload.nextScore}\n </p>\n {payload.reason ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.reason}</p> : null}\n </div>\n )\n case \"recommendation\":\n return (\n <div className=\"space-y-1\">\n <p className=\"font-medium text-foreground\">{payload.recommendation}</p>\n {payload.rationale ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.rationale}</p> : null}\n {payload.actionLabel ? <p className=\"text-xs font-medium text-muted-foreground\">{payload.actionLabel}</p> : null}\n </div>\n )\n case \"email\":\n return (\n <div className=\"space-y-1\">\n <p className=\"font-medium text-foreground\">{payload.subject}</p>\n <p className=\"text-xs text-muted-foreground\">\n From {payload.from}{payload.to ? ` to ${payload.to}` : \"\"}\n </p>\n {payload.preview ? <p className=\"text-sm text-foreground/90\">{payload.preview}</p> : null}\n {payload.body ? <p className=\"whitespace-pre-line text-xs leading-relaxed text-muted-foreground\">{payload.body}</p> : null}\n </div>\n )\n case \"salesforce\":\n return (\n <div className=\"space-y-2\">\n <p className=\"text-foreground\">\n <span className=\"font-medium\">{payload.objectLabel}</span>\n {payload.recordLabel ? <span className=\"text-muted-foreground\"> · {payload.recordLabel}</span> : null}\n </p>\n <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.changeSummary}</p>\n {payload.deepLink ? (\n <a\n href={payload.deepLink.href}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground transition-colors hover:text-foreground\"\n >\n {payload.deepLink.label ?? \"Open in Salesforce\"}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : null}\n </div>\n )\n case \"deadline\":\n return (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-foreground\">{payload.dueLabel}</span>\n {payload.status ? (\n <span className={cn(\"rounded-full border px-2 py-0.5 text-[11px] font-medium\", STATUS_CLASSES[payload.status])}>\n {payload.status}\n </span>\n ) : null}\n </div>\n {payload.description ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.description}</p> : null}\n {typeof payload.progress === \"number\" ? (\n <div\n data-slot=\"deadline-progress\"\n role=\"progressbar\"\n aria-valuenow={Math.round(Math.min(1, Math.max(0, payload.progress)) * 100)}\n aria-valuemin={0}\n aria-valuemax={100}\n className=\"bg-muted h-1.5 w-full overflow-hidden rounded-full\"\n >\n <div\n className={cn(\n \"h-full rounded-full\",\n payload.status === \"overdue\" ? \"bg-red-500\" : payload.status === \"due\" ? \"bg-amber-500\" : \"bg-foreground/70\"\n )}\n style={{ width: `${Math.min(1, Math.max(0, payload.progress)) * 100}%` }}\n />\n </div>\n ) : null}\n </div>\n )\n case \"operatorNote\":\n return <p className=\"whitespace-pre-line text-sm leading-relaxed text-foreground\">{payload.note}</p>\n case \"assignment\":\n return (\n <p className=\"text-foreground\">\n Assigned to <span className=\"font-medium\">{payload.assignee}</span>\n {payload.role ? <span className=\"text-muted-foreground\"> as {payload.role}</span> : null}\n {payload.from ? <span className=\"text-muted-foreground\"> from {payload.from}</span> : null}\n </p>\n )\n case \"caseOpened\":\n return (\n <div className=\"space-y-1\">\n <p className=\"text-foreground\">\n Case opened{payload.openedBy ? ` by ${payload.openedBy}` : \"\"}{payload.source ? ` from ${payload.source}` : \"\"}\n </p>\n {payload.description ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.description}</p> : null}\n </div>\n )\n case \"generic\":\n return (\n <div className=\"space-y-2\">\n <p className=\"text-foreground\">{payload.description}</p>\n {payload.metadata && payload.metadata.length > 0 ? (\n <dl className=\"grid gap-1 text-xs text-muted-foreground\">\n {payload.metadata.map((item) => (\n <div key={`${item.label}-${item.value}`} className=\"flex gap-2\">\n <dt className=\"font-medium text-foreground\">{item.label}</dt>\n <dd>{item.value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </div>\n )\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/case-panel-activity-timeline.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, ExternalLink } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { TONE_CLASSES, type TimelineEventTone } from \"./timeline-activity\"\n\nexport type CasePanelActivityTone = TimelineEventTone\n\nexport type CasePanelActivityActor =\n | { kind: \"system\" }\n | { kind: \"integration\"; name: string; iconUrl?: string }\n | { kind: \"user\"; name: string; avatarUrl?: string; verb?: string }\n\nexport type CasePanelPayloadAction = {\n kind: \"openSignal\"\n key: string\n eventId: string\n}\n\nexport type CasePanelActivityPayload =\n | {\n kind: \"signal\"\n key: string\n summary: string\n detail?: string\n actionLabel?: string\n }\n | {\n kind: \"scoreUpdate\"\n label?: string\n previousScore?: number\n nextScore: number\n reason?: string\n }\n | {\n kind: \"recommendation\"\n recommendation: string\n rationale?: string\n actionLabel?: string\n }\n | {\n kind: \"email\"\n from: string\n to?: string\n subject: string\n preview?: string\n body?: string\n }\n | {\n kind: \"salesforce\"\n objectLabel: string\n recordLabel?: string\n changeSummary: string\n deepLink?: {\n href: string\n label?: string\n }\n }\n | {\n kind: \"deadline\"\n dueLabel: string\n status?: \"upcoming\" | \"due\" | \"overdue\" | \"met\"\n description?: string\n /** 0..1 elapsed toward the deadline; renders a thin progress bar. */\n progress?: number\n }\n | {\n kind: \"operatorNote\"\n note: string\n }\n | {\n kind: \"assignment\"\n assignee: string\n from?: string\n role?: string\n }\n | {\n kind: \"caseOpened\"\n source?: string\n openedBy?: string\n description?: string\n }\n | {\n kind: \"generic\"\n description: string\n metadata?: Array<{ label: string; value: string }>\n }\n\nexport interface CasePanelActivityEvent {\n id: string\n title: string\n timeLabel: string\n tone: CasePanelActivityTone\n actor?: CasePanelActivityActor\n isSystemNoise?: boolean\n payload: CasePanelActivityPayload\n}\n\nexport interface CasePanelActivityTimelineProps {\n events: CasePanelActivityEvent[]\n className?: string\n title?: string\n defaultExpanded?: boolean\n defaultShowSystemEvents?: boolean\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n}\n\nconst NEUTRAL_DOT_CLASSES = \"bg-background border-border/60\"\nconst NEUTRAL_ICON_CLASSES = \"text-muted-foreground\"\n\nconst PAYLOAD_LABELS: Record<CasePanelActivityPayload[\"kind\"], string> = {\n signal: \"Signal\",\n scoreUpdate: \"Score update\",\n recommendation: \"Recommendation\",\n email: \"Email\",\n salesforce: \"Salesforce\",\n deadline: \"Deadline\",\n operatorNote: \"Operator note\",\n assignment: \"Assignment\",\n caseOpened: \"Work item opened\",\n generic: \"Update\",\n}\n\nconst STATUS_CLASSES: Record<NonNullable<Extract<CasePanelActivityPayload, { kind: \"deadline\" }>[\"status\"]>, string> = {\n upcoming: \"border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900/40 dark:bg-blue-950/30 dark:text-blue-300\",\n due: \"border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/40 dark:bg-amber-950/30 dark:text-amber-300\",\n overdue: \"border-red-200 bg-red-50 text-red-700 dark:border-red-900/40 dark:bg-red-950/30 dark:text-red-300\",\n met: \"border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/40 dark:bg-emerald-950/30 dark:text-emerald-300\",\n}\n\nexport function CasePanelActivityTimeline({\n events,\n className,\n title = \"Activity\",\n defaultExpanded = false,\n defaultShowSystemEvents = false,\n onPayloadAction,\n}: CasePanelActivityTimelineProps) {\n const [expanded, setExpanded] = React.useState(defaultExpanded)\n const [showSystemEvents, setShowSystemEvents] = React.useState(defaultShowSystemEvents)\n\n const nonSystemEvents = events.filter((event) => !event.isSystemNoise)\n const visibleEvents = showSystemEvents ? events : nonSystemEvents\n const systemNoiseCount = events.length - nonSystemEvents.length\n const lastActivityText = nonSystemEvents[0]?.timeLabel ?? events[0]?.timeLabel ?? \"No activity yet\"\n\n return (\n <section className={cn(\"rounded-xl border border-border bg-card text-card-foreground shadow-sm\", className)}>\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/30\"\n aria-expanded={expanded}\n onClick={() => setExpanded((current) => !current)}\n >\n <span className=\"min-w-0\">\n <span className=\"flex items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">{title}</span>\n <span className=\"rounded-full border border-border bg-muted/40 px-2 py-0.5 text-[11px] font-medium text-muted-foreground\">\n {nonSystemEvents.length} {nonSystemEvents.length === 1 ? \"event\" : \"events\"}\n </span>\n </span>\n <span className=\"mt-1 block truncate text-xs text-muted-foreground\">Last activity {lastActivityText}</span>\n </span>\n <ChevronDown className={cn(\"h-4 w-4 shrink-0 text-muted-foreground transition-transform\", expanded && \"rotate-180\")} />\n </button>\n\n {expanded ? (\n <div className=\"border-t border-border px-4 py-4\">\n {systemNoiseCount > 0 ? (\n <label className=\"mb-4 flex items-center justify-between gap-3 rounded-lg border border-border/70 bg-muted/20 px-3 py-2 text-xs text-muted-foreground\">\n <span>Show system events</span>\n <input\n type=\"checkbox\"\n className=\"h-4 w-4 rounded border-border text-primary accent-current\"\n checked={showSystemEvents}\n onChange={(event) => setShowSystemEvents(event.target.checked)}\n />\n </label>\n ) : null}\n\n {visibleEvents.length > 0 ? (\n <div className=\"space-y-0\">\n {visibleEvents.map((event, index) => (\n <CasePanelActivityTimelineItem\n key={event.id}\n event={event}\n isLast={index === visibleEvents.length - 1}\n onPayloadAction={onPayloadAction}\n />\n ))}\n </div>\n ) : (\n <p className=\"rounded-lg border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground\">\n No activity to show.\n </p>\n )}\n </div>\n ) : null}\n </section>\n )\n}\n\nfunction CasePanelActivityTimelineItem({\n event,\n isLast,\n onPayloadAction,\n}: {\n event: CasePanelActivityEvent\n isLast: boolean\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n}) {\n const toneStyle = TONE_CLASSES[event.tone]\n const dotClasses = toneStyle ? toneStyle.dot : NEUTRAL_DOT_CLASSES\n const iconClasses = toneStyle ? toneStyle.icon : NEUTRAL_ICON_CLASSES\n\n return (\n <article className=\"group relative flex gap-3.5\" data-testid=\"case-panel-activity-event\">\n {!isLast ? <div className=\"absolute bottom-[-6px] left-[9px] top-5 w-px bg-border/60\" /> : null}\n <div className=\"relative z-10 mt-1 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-card\">\n <span\n aria-hidden=\"true\"\n className={cn(\"flex h-4.5 w-4.5 items-center justify-center rounded-full border ring-4 ring-card\", dotClasses, iconClasses)}\n data-testid=\"case-panel-activity-dot\"\n >\n <span className=\"h-1.5 w-1.5 rounded-full bg-current\" />\n </span>\n </div>\n <div className=\"min-w-0 flex-1 pb-5 pt-0.5\">\n <div className=\"flex min-w-0 flex-col gap-1 sm:flex-row sm:items-start sm:justify-between\">\n <h3 className=\"pr-4 text-[13px] font-medium leading-relaxed text-foreground\">{event.title}</h3>\n <time className=\"mt-0.5 shrink-0 whitespace-nowrap text-[11px] text-muted-foreground/70\">{event.timeLabel}</time>\n </div>\n <ActorByline actor={event.actor} timeLabel={event.timeLabel} />\n <PayloadCard event={event} onPayloadAction={onPayloadAction} />\n </div>\n </article>\n )\n}\n\nfunction ActorByline({ actor, timeLabel }: { actor?: CasePanelActivityActor; timeLabel: string }) {\n if (!actor || actor.kind === \"system\") return null\n\n if (actor.kind === \"integration\") {\n return (\n <div className=\"mt-1 flex items-center gap-1.5 text-xs text-muted-foreground\" data-testid=\"case-panel-activity-byline\">\n {actor.iconUrl ? <img src={actor.iconUrl} alt=\"\" className=\"h-4 w-4 rounded-sm object-cover\" /> : null}\n <span className=\"font-medium text-foreground\">{actor.name}</span>\n <span>synced this update</span>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{timeLabel}</span>\n </div>\n )\n }\n\n const verb = actor.verb ?? \"updated this work item\"\n const initial = actor.name.charAt(0).toUpperCase()\n\n return (\n <div className=\"mt-1 flex items-center gap-1.5 text-xs text-muted-foreground\" data-testid=\"case-panel-activity-byline\">\n {actor.avatarUrl ? (\n <img src={actor.avatarUrl} alt={actor.name} className=\"h-4 w-4 rounded-full object-cover\" />\n ) : (\n <span className=\"flex h-4 w-4 items-center justify-center rounded-full bg-muted-foreground/10 text-[8px] font-semibold text-muted-foreground\">\n {initial}\n </span>\n )}\n <span className=\"font-medium text-foreground\">{actor.name}</span>\n <span>{verb}</span>\n <span className=\"text-muted-foreground/40\">·</span>\n <span>{timeLabel}</span>\n </div>\n )\n}\n\nfunction PayloadCard({\n event,\n onPayloadAction,\n}: {\n event: CasePanelActivityEvent\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n}) {\n const payload = event.payload\n\n return (\n <div className=\"mt-2 rounded-lg border border-border/80 bg-muted/20 px-3 py-2.5 text-sm\" data-testid={`case-panel-payload-${payload.kind}`}>\n <div className=\"mb-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground\">\n {PAYLOAD_LABELS[payload.kind]}\n </div>\n {renderPayloadContent(payload, event.id, onPayloadAction)}\n </div>\n )\n}\n\nfunction renderPayloadContent(\n payload: CasePanelActivityPayload,\n eventId: string,\n onPayloadAction?: (action: CasePanelPayloadAction) => void\n) {\n switch (payload.kind) {\n case \"signal\":\n return (\n <div className=\"space-y-2\">\n <p className=\"text-foreground\">{payload.summary}</p>\n {payload.detail ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.detail}</p> : null}\n {onPayloadAction && payload.actionLabel ? (\n <button\n type=\"button\"\n className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground transition-colors hover:text-foreground\"\n onClick={() => onPayloadAction({ kind: \"openSignal\", key: payload.key, eventId })}\n >\n {payload.actionLabel}\n </button>\n ) : null}\n </div>\n )\n case \"scoreUpdate\":\n return (\n <div className=\"space-y-1\">\n <p className=\"text-foreground\">\n {payload.label ?? \"Score\"}: {payload.previousScore !== undefined ? `${payload.previousScore} → ` : \"\"}{payload.nextScore}\n </p>\n {payload.reason ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.reason}</p> : null}\n </div>\n )\n case \"recommendation\":\n return (\n <div className=\"space-y-1\">\n <p className=\"font-medium text-foreground\">{payload.recommendation}</p>\n {payload.rationale ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.rationale}</p> : null}\n {payload.actionLabel ? <p className=\"text-xs font-medium text-muted-foreground\">{payload.actionLabel}</p> : null}\n </div>\n )\n case \"email\":\n return (\n <div className=\"space-y-1\">\n <p className=\"font-medium text-foreground\">{payload.subject}</p>\n <p className=\"text-xs text-muted-foreground\">\n From {payload.from}{payload.to ? ` to ${payload.to}` : \"\"}\n </p>\n {payload.preview ? <p className=\"text-sm text-foreground/90\">{payload.preview}</p> : null}\n {payload.body ? <p className=\"whitespace-pre-line text-xs leading-relaxed text-muted-foreground\">{payload.body}</p> : null}\n </div>\n )\n case \"salesforce\":\n return (\n <div className=\"space-y-2\">\n <p className=\"text-foreground\">\n <span className=\"font-medium\">{payload.objectLabel}</span>\n {payload.recordLabel ? <span className=\"text-muted-foreground\"> · {payload.recordLabel}</span> : null}\n </p>\n <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.changeSummary}</p>\n {payload.deepLink ? (\n <a\n href={payload.deepLink.href}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground transition-colors hover:text-foreground\"\n >\n {payload.deepLink.label ?? \"Open in Salesforce\"}\n <ExternalLink className=\"h-3 w-3\" />\n </a>\n ) : null}\n </div>\n )\n case \"deadline\":\n return (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-foreground\">{payload.dueLabel}</span>\n {payload.status ? (\n <span className={cn(\"rounded-full border px-2 py-0.5 text-[11px] font-medium\", STATUS_CLASSES[payload.status])}>\n {payload.status}\n </span>\n ) : null}\n </div>\n {payload.description ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.description}</p> : null}\n {typeof payload.progress === \"number\" ? (\n <div\n data-slot=\"deadline-progress\"\n role=\"progressbar\"\n aria-valuenow={Math.round(Math.min(1, Math.max(0, payload.progress)) * 100)}\n aria-valuemin={0}\n aria-valuemax={100}\n className=\"bg-muted h-1.5 w-full overflow-hidden rounded-full\"\n >\n <div\n className={cn(\n \"h-full rounded-full\",\n payload.status === \"overdue\" ? \"bg-red-500\" : payload.status === \"due\" ? \"bg-amber-500\" : \"bg-foreground/70\"\n )}\n style={{ width: `${Math.min(1, Math.max(0, payload.progress)) * 100}%` }}\n />\n </div>\n ) : null}\n </div>\n )\n case \"operatorNote\":\n return <p className=\"whitespace-pre-line text-sm leading-relaxed text-foreground\">{payload.note}</p>\n case \"assignment\":\n return (\n <p className=\"text-foreground\">\n Assigned to <span className=\"font-medium\">{payload.assignee}</span>\n {payload.role ? <span className=\"text-muted-foreground\"> as {payload.role}</span> : null}\n {payload.from ? <span className=\"text-muted-foreground\"> from {payload.from}</span> : null}\n </p>\n )\n case \"caseOpened\":\n return (\n <div className=\"space-y-1\">\n <p className=\"text-foreground\">\n Work item opened{payload.openedBy ? ` by ${payload.openedBy}` : \"\"}{payload.source ? ` from ${payload.source}` : \"\"}\n </p>\n {payload.description ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{payload.description}</p> : null}\n </div>\n )\n case \"generic\":\n return (\n <div className=\"space-y-2\">\n <p className=\"text-foreground\">{payload.description}</p>\n {payload.metadata && payload.metadata.length > 0 ? (\n <dl className=\"grid gap-1 text-xs text-muted-foreground\">\n {payload.metadata.map((item) => (\n <div key={`${item.label}-${item.value}`} className=\"flex gap-2\">\n <dt className=\"font-medium text-foreground\">{item.label}</dt>\n <dd>{item.value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </div>\n )\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -19,7 +19,7 @@ const detailWidthClasses = {
|
|
|
19
19
|
function CasePanelDetail({
|
|
20
20
|
children,
|
|
21
21
|
width = "comfortable",
|
|
22
|
-
"aria-label": ariaLabel = "
|
|
22
|
+
"aria-label": ariaLabel = "Work item detail",
|
|
23
23
|
className
|
|
24
24
|
}) {
|
|
25
25
|
return /* @__PURE__ */ jsx("section", { "aria-label": ariaLabel, className: cn("min-w-0 bg-background", className), children: /* @__PURE__ */ jsx("div", { className: detailWidthClasses[width], children }) });
|
|
@@ -112,7 +112,7 @@ function CasePanelSignalBrief({
|
|
|
112
112
|
}
|
|
113
113
|
function CasePanelMetadataRow({
|
|
114
114
|
children,
|
|
115
|
-
label = "
|
|
115
|
+
label = "Work item metadata",
|
|
116
116
|
className
|
|
117
117
|
}) {
|
|
118
118
|
return /* @__PURE__ */ jsx("div", { "aria-label": label, className: cn("mt-[18px] flex flex-wrap items-center gap-2", className), children });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/case-panel-detail.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n ArrowUpRight,\n Check,\n Copy,\n ExternalLink,\n} from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport type CasePanelDetailWidth = \"comfortable\" | \"modest\" | \"compact\" | \"full\"\n\nexport interface CasePanelDetailProps {\n children: React.ReactNode\n /** Reading measure for the detail content column. Defaults to the handoff's comfortable 880px measure. */\n width?: CasePanelDetailWidth\n /** Accessible label for the detail region. */\n \"aria-label\"?: string\n className?: string\n}\n\nconst detailWidthClasses: Record<CasePanelDetailWidth, string> = {\n comfortable: \"mx-auto w-full max-w-[880px] px-10 pb-[120px] pt-8\",\n modest: \"mx-auto w-full max-w-[720px] px-8 pb-[112px] pt-7\",\n compact: \"mx-auto w-full max-w-[640px] px-[60px] pb-[112px] pt-7\",\n full: \"w-full px-8 pb-[120px] pt-8\",\n}\n\nexport function CasePanelDetail({\n children,\n width = \"comfortable\",\n \"aria-label\": ariaLabel = \"Case detail\",\n className,\n}: CasePanelDetailProps) {\n return (\n <section aria-label={ariaLabel} className={cn(\"min-w-0 bg-background\", className)}>\n <div className={detailWidthClasses[width]}>{children}</div>\n </section>\n )\n}\n\nexport interface CasePanelHeaderProps {\n title: React.ReactNode\n /** Optional action, usually a Quick action trigger. */\n action?: React.ReactNode\n children?: React.ReactNode\n className?: string\n}\n\nexport function CasePanelHeader({\n title,\n action,\n children,\n className,\n}: CasePanelHeaderProps) {\n return (\n <header className={cn(\"mb-7\", className)}>\n <div className=\"flex items-start justify-between gap-6\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"m-0 max-w-[560px] text-[27px] font-bold leading-[1.18] tracking-[-0.02em] text-foreground\">\n {title}\n </h1>\n {children}\n </div>\n {action ? <div className=\"flex shrink-0 items-center gap-2\">{action}</div> : null}\n </div>\n </header>\n )\n}\n\nexport interface CasePanelIdentityLink {\n id: string\n label: string\n href?: string\n icon?: React.ReactNode\n kind?: \"icon\" | \"text\"\n disabled?: boolean\n target?: React.HTMLAttributeAnchorTarget\n rel?: string\n}\n\nexport interface CasePanelIdentitySublineProps {\n callsign?: string | null\n links?: CasePanelIdentityLink[]\n onCopyCallsign?: (callsign: string) => void\n copyLabel?: string\n copiedLabel?: string\n className?: string\n}\n\nexport function CasePanelIdentitySubline({\n callsign,\n links = [],\n onCopyCallsign,\n copyLabel = \"Copy call sign\",\n copiedLabel = \"Call sign copied\",\n className,\n}: CasePanelIdentitySublineProps) {\n const [copied, setCopied] = React.useState(false)\n const trimmedCallsign = callsign?.trim()\n const normalizedCallsign = trimmedCallsign\n ? trimmedCallsign.startsWith(\"@\")\n ? trimmedCallsign\n : `@${trimmedCallsign}`\n : null\n\n const handleCopy = React.useCallback(() => {\n if (!normalizedCallsign) return\n onCopyCallsign?.(normalizedCallsign)\n setCopied(true)\n window.setTimeout(() => setCopied(false), 1400)\n }, [normalizedCallsign, onCopyCallsign])\n\n return (\n <div className={cn(\"mt-[9px] inline-flex flex-wrap items-center gap-[7px] text-[13px] text-muted-foreground\", className)}>\n {normalizedCallsign ? (\n <>\n <span className=\"font-mono font-medium tracking-[0.01em] text-gray-700\">{normalizedCallsign}</span>\n <button\n type=\"button\"\n onClick={handleCopy}\n aria-label={copied ? copiedLabel : copyLabel}\n className=\"inline-flex h-[22px] w-[22px] items-center justify-center rounded-md text-gray-400 transition-colors hover:bg-accent hover:text-foreground\"\n >\n {copied ? <Check className=\"h-[13px] w-[13px] text-emerald-700\" aria-hidden=\"true\" /> : <Copy className=\"h-[13px] w-[13px]\" aria-hidden=\"true\" />}\n </button>\n </>\n ) : null}\n {links.length > 0 ? (\n <>\n {normalizedCallsign ? <span aria-hidden=\"true\" className=\"mx-[3px] h-3.5 w-px bg-gray-200\" /> : null}\n <span className=\"inline-flex items-center gap-1.5\">\n {links.map((link) => (\n <CasePanelIdentityLinkButton key={link.id} link={link} />\n ))}\n </span>\n </>\n ) : null}\n </div>\n )\n}\n\nfunction CasePanelIdentityLinkButton({ link }: { link: CasePanelIdentityLink }) {\n const disabled = link.disabled || !link.href\n const iconOnly = link.kind === \"icon\"\n const content = iconOnly ? (\n <>{link.icon ?? <ExternalLink className=\"h-[13px] w-[13px]\" aria-hidden=\"true\" />}</>\n ) : (\n <>\n {link.icon ? <span className=\"inline-flex h-3.5 w-3.5 items-center justify-center\">{link.icon}</span> : null}\n <span>{link.label}</span>\n <ArrowUpRight className=\"h-[11px] w-[11px] text-gray-400\" aria-hidden=\"true\" />\n </>\n )\n\n const className = iconOnly\n ? \"inline-flex h-[26px] w-[26px] items-center justify-center rounded-[7px] border border-border bg-background text-foreground shadow-[0_1px_1.5px_rgba(0,0,0,0.03)] transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-45\"\n : \"inline-flex h-[26px] items-center gap-1.5 rounded-[7px] border border-border bg-background px-2 text-xs font-medium text-foreground shadow-[0_1px_1.5px_rgba(0,0,0,0.03)] transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-45\"\n\n if (disabled) {\n return (\n <button type=\"button\" disabled aria-label={link.label} className={className}>\n {content}\n </button>\n )\n }\n\n return (\n <a\n href={link.href}\n aria-label={link.label}\n target={link.target ?? \"_blank\"}\n rel={link.rel ?? \"noopener noreferrer\"}\n className={className}\n >\n {content}\n </a>\n )\n}\n\nexport interface CasePanelSignalBriefProps {\n children: React.ReactNode\n label?: string\n className?: string\n}\n\nexport function CasePanelSignalBrief({\n children,\n label = \"Signal brief\",\n className,\n}: CasePanelSignalBriefProps) {\n return (\n <section className={cn(\"mt-7\", className)} aria-labelledby=\"case-panel-signal-brief-heading\">\n <div id=\"case-panel-signal-brief-heading\" className=\"mb-2.5 text-[11px] font-semibold uppercase tracking-[0.07em] text-muted-foreground\">\n {label}\n </div>\n <div className=\"text-base leading-[1.6] text-foreground [text-wrap:pretty]\">{children}</div>\n </section>\n )\n}\n\nexport interface CasePanelMetadataRowProps {\n children: React.ReactNode\n label?: string\n className?: string\n}\n\nexport function CasePanelMetadataRow({\n children,\n label = \"Case metadata\",\n className,\n}: CasePanelMetadataRowProps) {\n return (\n <div aria-label={label} className={cn(\"mt-[18px] flex flex-wrap items-center gap-2\", className)}>\n {children}\n </div>\n )\n}\n\nexport interface CasePanelDetailSlotsProps {\n why?: React.ReactNode\n actions?: React.ReactNode\n opportunity?: React.ReactNode\n timeline?: React.ReactNode\n playPanel?: React.ReactNode\n className?: string\n}\n\nexport function CasePanelDetailSlots({\n why,\n actions,\n opportunity,\n timeline,\n playPanel,\n className,\n}: CasePanelDetailSlotsProps) {\n return (\n <div className={cn(\"mt-7 space-y-6\", className)}>\n {why ? <div data-slot=\"why\">{why}</div> : null}\n {actions ? <div data-slot=\"actions\">{actions}</div> : null}\n {opportunity ? <div data-slot=\"opportunity\">{opportunity}</div> : null}\n {timeline ? <div data-slot=\"timeline\">{timeline}</div> : null}\n {playPanel ? <div data-slot=\"play-panel\">{playPanel}</div> : null}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/case-panel-detail.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n ArrowUpRight,\n Check,\n Copy,\n ExternalLink,\n} from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport type CasePanelDetailWidth = \"comfortable\" | \"modest\" | \"compact\" | \"full\"\n\nexport interface CasePanelDetailProps {\n children: React.ReactNode\n /** Reading measure for the detail content column. Defaults to the handoff's comfortable 880px measure. */\n width?: CasePanelDetailWidth\n /** Accessible label for the detail region. */\n \"aria-label\"?: string\n className?: string\n}\n\nconst detailWidthClasses: Record<CasePanelDetailWidth, string> = {\n comfortable: \"mx-auto w-full max-w-[880px] px-10 pb-[120px] pt-8\",\n modest: \"mx-auto w-full max-w-[720px] px-8 pb-[112px] pt-7\",\n compact: \"mx-auto w-full max-w-[640px] px-[60px] pb-[112px] pt-7\",\n full: \"w-full px-8 pb-[120px] pt-8\",\n}\n\nexport function CasePanelDetail({\n children,\n width = \"comfortable\",\n \"aria-label\": ariaLabel = \"Work item detail\",\n className,\n}: CasePanelDetailProps) {\n return (\n <section aria-label={ariaLabel} className={cn(\"min-w-0 bg-background\", className)}>\n <div className={detailWidthClasses[width]}>{children}</div>\n </section>\n )\n}\n\nexport interface CasePanelHeaderProps {\n title: React.ReactNode\n /** Optional action, usually a Quick action trigger. */\n action?: React.ReactNode\n children?: React.ReactNode\n className?: string\n}\n\nexport function CasePanelHeader({\n title,\n action,\n children,\n className,\n}: CasePanelHeaderProps) {\n return (\n <header className={cn(\"mb-7\", className)}>\n <div className=\"flex items-start justify-between gap-6\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"m-0 max-w-[560px] text-[27px] font-bold leading-[1.18] tracking-[-0.02em] text-foreground\">\n {title}\n </h1>\n {children}\n </div>\n {action ? <div className=\"flex shrink-0 items-center gap-2\">{action}</div> : null}\n </div>\n </header>\n )\n}\n\nexport interface CasePanelIdentityLink {\n id: string\n label: string\n href?: string\n icon?: React.ReactNode\n kind?: \"icon\" | \"text\"\n disabled?: boolean\n target?: React.HTMLAttributeAnchorTarget\n rel?: string\n}\n\nexport interface CasePanelIdentitySublineProps {\n callsign?: string | null\n links?: CasePanelIdentityLink[]\n onCopyCallsign?: (callsign: string) => void\n copyLabel?: string\n copiedLabel?: string\n className?: string\n}\n\nexport function CasePanelIdentitySubline({\n callsign,\n links = [],\n onCopyCallsign,\n copyLabel = \"Copy call sign\",\n copiedLabel = \"Call sign copied\",\n className,\n}: CasePanelIdentitySublineProps) {\n const [copied, setCopied] = React.useState(false)\n const trimmedCallsign = callsign?.trim()\n const normalizedCallsign = trimmedCallsign\n ? trimmedCallsign.startsWith(\"@\")\n ? trimmedCallsign\n : `@${trimmedCallsign}`\n : null\n\n const handleCopy = React.useCallback(() => {\n if (!normalizedCallsign) return\n onCopyCallsign?.(normalizedCallsign)\n setCopied(true)\n window.setTimeout(() => setCopied(false), 1400)\n }, [normalizedCallsign, onCopyCallsign])\n\n return (\n <div className={cn(\"mt-[9px] inline-flex flex-wrap items-center gap-[7px] text-[13px] text-muted-foreground\", className)}>\n {normalizedCallsign ? (\n <>\n <span className=\"font-mono font-medium tracking-[0.01em] text-gray-700\">{normalizedCallsign}</span>\n <button\n type=\"button\"\n onClick={handleCopy}\n aria-label={copied ? copiedLabel : copyLabel}\n className=\"inline-flex h-[22px] w-[22px] items-center justify-center rounded-md text-gray-400 transition-colors hover:bg-accent hover:text-foreground\"\n >\n {copied ? <Check className=\"h-[13px] w-[13px] text-emerald-700\" aria-hidden=\"true\" /> : <Copy className=\"h-[13px] w-[13px]\" aria-hidden=\"true\" />}\n </button>\n </>\n ) : null}\n {links.length > 0 ? (\n <>\n {normalizedCallsign ? <span aria-hidden=\"true\" className=\"mx-[3px] h-3.5 w-px bg-gray-200\" /> : null}\n <span className=\"inline-flex items-center gap-1.5\">\n {links.map((link) => (\n <CasePanelIdentityLinkButton key={link.id} link={link} />\n ))}\n </span>\n </>\n ) : null}\n </div>\n )\n}\n\nfunction CasePanelIdentityLinkButton({ link }: { link: CasePanelIdentityLink }) {\n const disabled = link.disabled || !link.href\n const iconOnly = link.kind === \"icon\"\n const content = iconOnly ? (\n <>{link.icon ?? <ExternalLink className=\"h-[13px] w-[13px]\" aria-hidden=\"true\" />}</>\n ) : (\n <>\n {link.icon ? <span className=\"inline-flex h-3.5 w-3.5 items-center justify-center\">{link.icon}</span> : null}\n <span>{link.label}</span>\n <ArrowUpRight className=\"h-[11px] w-[11px] text-gray-400\" aria-hidden=\"true\" />\n </>\n )\n\n const className = iconOnly\n ? \"inline-flex h-[26px] w-[26px] items-center justify-center rounded-[7px] border border-border bg-background text-foreground shadow-[0_1px_1.5px_rgba(0,0,0,0.03)] transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-45\"\n : \"inline-flex h-[26px] items-center gap-1.5 rounded-[7px] border border-border bg-background px-2 text-xs font-medium text-foreground shadow-[0_1px_1.5px_rgba(0,0,0,0.03)] transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-45\"\n\n if (disabled) {\n return (\n <button type=\"button\" disabled aria-label={link.label} className={className}>\n {content}\n </button>\n )\n }\n\n return (\n <a\n href={link.href}\n aria-label={link.label}\n target={link.target ?? \"_blank\"}\n rel={link.rel ?? \"noopener noreferrer\"}\n className={className}\n >\n {content}\n </a>\n )\n}\n\nexport interface CasePanelSignalBriefProps {\n children: React.ReactNode\n label?: string\n className?: string\n}\n\nexport function CasePanelSignalBrief({\n children,\n label = \"Signal brief\",\n className,\n}: CasePanelSignalBriefProps) {\n return (\n <section className={cn(\"mt-7\", className)} aria-labelledby=\"case-panel-signal-brief-heading\">\n <div id=\"case-panel-signal-brief-heading\" className=\"mb-2.5 text-[11px] font-semibold uppercase tracking-[0.07em] text-muted-foreground\">\n {label}\n </div>\n <div className=\"text-base leading-[1.6] text-foreground [text-wrap:pretty]\">{children}</div>\n </section>\n )\n}\n\nexport interface CasePanelMetadataRowProps {\n children: React.ReactNode\n label?: string\n className?: string\n}\n\nexport function CasePanelMetadataRow({\n children,\n label = \"Work item metadata\",\n className,\n}: CasePanelMetadataRowProps) {\n return (\n <div aria-label={label} className={cn(\"mt-[18px] flex flex-wrap items-center gap-2\", className)}>\n {children}\n </div>\n )\n}\n\nexport interface CasePanelDetailSlotsProps {\n why?: React.ReactNode\n actions?: React.ReactNode\n opportunity?: React.ReactNode\n timeline?: React.ReactNode\n playPanel?: React.ReactNode\n className?: string\n}\n\nexport function CasePanelDetailSlots({\n why,\n actions,\n opportunity,\n timeline,\n playPanel,\n className,\n}: CasePanelDetailSlotsProps) {\n return (\n <div className={cn(\"mt-7 space-y-6\", className)}>\n {why ? <div data-slot=\"why\">{why}</div> : null}\n {actions ? <div data-slot=\"actions\">{actions}</div> : null}\n {opportunity ? <div data-slot=\"opportunity\">{opportunity}</div> : null}\n {timeline ? <div data-slot=\"timeline\">{timeline}</div> : null}\n {playPanel ? <div data-slot=\"play-panel\">{playPanel}</div> : null}\n </div>\n )\n}\n"],"mappings":";AAqCM,SAgFE,UAhFF,KAsBE,YAtBF;AAnCN,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,UAAU;AAanB,MAAM,qBAA2D;AAAA,EAC/D,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,QAAQ;AAAA,EACR,cAAc,YAAY;AAAA,EAC1B;AACF,GAAyB;AACvB,SACE,oBAAC,aAAQ,cAAY,WAAW,WAAW,GAAG,yBAAyB,SAAS,GAC9E,8BAAC,SAAI,WAAW,mBAAmB,KAAK,GAAI,UAAS,GACvD;AAEJ;AAUO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,SACE,oBAAC,YAAO,WAAW,GAAG,QAAQ,SAAS,GACrC,+BAAC,SAAI,WAAU,0CACb;AAAA,yBAAC,SAAI,WAAU,kBACb;AAAA,0BAAC,QAAG,WAAU,6FACX,iBACH;AAAA,MACC;AAAA,OACH;AAAA,IACC,SAAS,oBAAC,SAAI,WAAU,oCAAoC,kBAAO,IAAS;AAAA,KAC/E,GACF;AAEJ;AAsBO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA,QAAQ,CAAC;AAAA,EACT;AAAA,EACA,YAAY;AAAA,EACZ,cAAc;AAAA,EACd;AACF,GAAkC;AAChC,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,kBAAkB,qCAAU;AAClC,QAAM,qBAAqB,kBACvB,gBAAgB,WAAW,GAAG,IAC5B,kBACA,IAAI,eAAe,KACrB;AAEJ,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,QAAI,CAAC,mBAAoB;AACzB,qDAAiB;AACjB,cAAU,IAAI;AACd,WAAO,WAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,EAChD,GAAG,CAAC,oBAAoB,cAAc,CAAC;AAEvC,SACE,qBAAC,SAAI,WAAW,GAAG,2FAA2F,SAAS,GACpH;AAAA,yBACC,iCACE;AAAA,0BAAC,UAAK,WAAU,yDAAyD,8BAAmB;AAAA,MAC5F;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAY,SAAS,cAAc;AAAA,UACnC,WAAU;AAAA,UAET,mBAAS,oBAAC,SAAM,WAAU,sCAAqC,eAAY,QAAO,IAAK,oBAAC,QAAK,WAAU,qBAAoB,eAAY,QAAO;AAAA;AAAA,MACjJ;AAAA,OACF,IACE;AAAA,IACH,MAAM,SAAS,IACd,iCACG;AAAA,2BAAqB,oBAAC,UAAK,eAAY,QAAO,WAAU,mCAAkC,IAAK;AAAA,MAChG,oBAAC,UAAK,WAAU,oCACb,gBAAM,IAAI,CAAC,SACV,oBAAC,+BAA0C,QAAT,KAAK,EAAgB,CACxD,GACH;AAAA,OACF,IACE;AAAA,KACN;AAEJ;AAEA,SAAS,4BAA4B,EAAE,KAAK,GAAoC;AA/IhF;AAgJE,QAAM,WAAW,KAAK,YAAY,CAAC,KAAK;AACxC,QAAM,WAAW,KAAK,SAAS;AAC/B,QAAM,UAAU,WACd,gCAAG,qBAAK,SAAL,YAAa,oBAAC,gBAAa,WAAU,qBAAoB,eAAY,QAAO,GAAG,IAElF,iCACG;AAAA,SAAK,OAAO,oBAAC,UAAK,WAAU,uDAAuD,eAAK,MAAK,IAAU;AAAA,IACxG,oBAAC,UAAM,eAAK,OAAM;AAAA,IAClB,oBAAC,gBAAa,WAAU,mCAAkC,eAAY,QAAO;AAAA,KAC/E;AAGF,QAAM,YAAY,WACd,wPACA;AAEJ,MAAI,UAAU;AACZ,WACE,oBAAC,YAAO,MAAK,UAAS,UAAQ,MAAC,cAAY,KAAK,OAAO,WACpD,mBACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,KAAK;AAAA,MACX,cAAY,KAAK;AAAA,MACjB,SAAQ,UAAK,WAAL,YAAe;AAAA,MACvB,MAAK,UAAK,QAAL,YAAY;AAAA,MACjB;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAQO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,QAAQ;AAAA,EACR;AACF,GAA8B;AAC5B,SACE,qBAAC,aAAQ,WAAW,GAAG,QAAQ,SAAS,GAAG,mBAAgB,mCACzD;AAAA,wBAAC,SAAI,IAAG,mCAAkC,WAAU,sFACjD,iBACH;AAAA,IACA,oBAAC,SAAI,WAAU,8DAA8D,UAAS;AAAA,KACxF;AAEJ;AAQO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,QAAQ;AAAA,EACR;AACF,GAA8B;AAC5B,SACE,oBAAC,SAAI,cAAY,OAAO,WAAW,GAAG,+CAA+C,SAAS,GAC3F,UACH;AAEJ;AAWO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,SACE,qBAAC,SAAI,WAAW,GAAG,kBAAkB,SAAS,GAC3C;AAAA,UAAM,oBAAC,SAAI,aAAU,OAAO,eAAI,IAAS;AAAA,IACzC,UAAU,oBAAC,SAAI,aAAU,WAAW,mBAAQ,IAAS;AAAA,IACrD,cAAc,oBAAC,SAAI,aAAU,eAAe,uBAAY,IAAS;AAAA,IACjE,WAAW,oBAAC,SAAI,aAAU,YAAY,oBAAS,IAAS;AAAA,IACxD,YAAY,oBAAC,SAAI,aAAU,cAAc,qBAAU,IAAS;AAAA,KAC/D;AAEJ;","names":[]}
|
|
@@ -40,7 +40,7 @@ function CasePanelWhy({
|
|
|
40
40
|
[onExpandedChange]
|
|
41
41
|
);
|
|
42
42
|
if (items.length === 0) return null;
|
|
43
|
-
return /* @__PURE__ */ jsxs("section", { className: cn("space-y-2", className), "aria-label": typeof label === "string" ? label : "
|
|
43
|
+
return /* @__PURE__ */ jsxs("section", { className: cn("space-y-2", className), "aria-label": typeof label === "string" ? label : "Work item panel why", children: [
|
|
44
44
|
label ? /* @__PURE__ */ jsx("div", { className: "text-[11px] font-semibold uppercase tracking-[0.07em] text-muted-foreground", children: label }) : null,
|
|
45
45
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: items.map((item) => {
|
|
46
46
|
var _a;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/case-panel-why.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { CirclePlus, Loader2, ThumbsDown } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { SignalApprovalActions, useSignalApproval } from \"./signal-feedback-inline\"\n\nexport type CasePanelWhyTone = \"neutral\" | \"alert\" | \"warn\" | \"info\" | \"success\"\n\nexport interface CasePanelWhyRow {\n id: string\n label: React.ReactNode\n value?: React.ReactNode\n meta?: React.ReactNode\n description?: React.ReactNode\n}\n\nexport interface CasePanelWhyItem {\n id: string\n label: React.ReactNode\n /** Count rendered in the summary pill as ×N when provided. */\n count?: number\n summary?: React.ReactNode\n details?: React.ReactNode\n rows?: CasePanelWhyRow[]\n icon?: React.ReactNode\n tone?: CasePanelWhyTone\n}\n\nexport interface CasePanelWhyProps {\n items: CasePanelWhyItem[]\n label?: React.ReactNode\n defaultExpandedId?: string | null\n onExpandedChange?: (itemId: string | null) => void\n className?: string\n}\n\nconst toneDotClasses: Record<CasePanelWhyTone, string> = {\n neutral: \"bg-muted-foreground/50\",\n alert: \"bg-red-500\",\n warn: \"bg-amber-500\",\n info: \"bg-blue-500\",\n success: \"bg-emerald-500\",\n}\n\nfunction makeDomId(...parts: Array<string | undefined>): string {\n return parts\n .filter((part): part is string => Boolean(part))\n .join(\"-\")\n .replace(/[^A-Za-z0-9_-]+/g, \"-\")\n}\n\nfunction DefaultWhyIcon({ tone = \"neutral\" }: { tone?: CasePanelWhyTone }) {\n return <span aria-hidden=\"true\" className={cn(\"h-1.5 w-1.5 rounded-full\", toneDotClasses[tone])} />\n}\n\nexport function CasePanelWhy({\n items,\n label = \"Why\",\n defaultExpandedId = null,\n onExpandedChange,\n className,\n}: CasePanelWhyProps) {\n const [expandedId, setExpandedId] = React.useState<string | null>(defaultExpandedId)\n const reactId = React.useId()\n const idPrefix = makeDomId(\"case-panel-why\", reactId)\n\n const toggleExpanded = React.useCallback(\n (itemId: string) => {\n setExpandedId((current) => {\n const next = current === itemId ? null : itemId\n onExpandedChange?.(next)\n return next\n })\n },\n [onExpandedChange],\n )\n\n if (items.length === 0) return null\n\n return (\n <section className={cn(\"space-y-2\", className)} aria-label={typeof label === \"string\" ? label : \"Case panel why\"}>\n {label ? (\n <div className=\"text-[11px] font-semibold uppercase tracking-[0.07em] text-muted-foreground\">\n {label}\n </div>\n ) : null}\n\n <div className=\"space-y-2\">\n {items.map((item) => {\n const isExpanded = expandedId === item.id\n const panelId = `${idPrefix}-panel-${makeDomId(item.id)}`\n const buttonId = `${idPrefix}-button-${makeDomId(item.id)}`\n\n return (\n <div key={item.id} className=\"space-y-2\">\n <button\n id={buttonId}\n type=\"button\"\n aria-expanded={isExpanded}\n aria-controls={panelId}\n onClick={() => toggleExpanded(item.id)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault()\n toggleExpanded(item.id)\n }\n }}\n className={cn(\n \"inline-flex min-h-8 max-w-full items-center gap-2 rounded-full border border-border bg-background px-3 py-1.5 text-left text-xs font-semibold text-foreground shadow-[0_1px_1.5px_rgba(0,0,0,0.03)] transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isExpanded ? \"bg-muted/30\" : \"bg-background\",\n )}\n >\n <span className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-muted\">\n {item.icon ?? <DefaultWhyIcon tone={item.tone} />}\n </span>\n <span className=\"min-w-0 truncate\">{item.label}</span>\n {typeof item.count === \"number\" ? (\n <span className=\"shrink-0 rounded-full bg-muted px-1.5 py-0 text-[10px] font-bold tabular-nums text-muted-foreground\">\n ×{item.count}\n </span>\n ) : null}\n </button>\n\n {isExpanded ? (\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={buttonId}\n className=\"mt-2 rounded-xl border border-border bg-background p-3 shadow-[0_1px_2px_rgba(0,0,0,0.03)]\"\n >\n {item.summary ? <div className=\"text-sm leading-6 text-foreground\">{item.summary}</div> : null}\n {item.details ? <div className=\"mt-2 text-xs leading-5 text-muted-foreground\">{item.details}</div> : null}\n {item.rows && item.rows.length > 0 ? (\n <ul className=\"mt-3 divide-y divide-border/50\" aria-label=\"Why details\">\n {item.rows.map((row) => (\n <li key={row.id} className=\"py-2 first:pt-0 last:pb-0\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium leading-5 text-foreground\">{row.label}</div>\n {row.description ? (\n <div className=\"mt-1 text-xs leading-5 text-muted-foreground\">{row.description}</div>\n ) : null}\n {row.meta ? <div className=\"mt-1 text-[11px] leading-4 text-muted-foreground/80\">{row.meta}</div> : null}\n </div>\n {row.value ? (\n <div className=\"shrink-0 text-right text-xs font-semibold tabular-nums text-foreground\">\n {row.value}\n </div>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n </section>\n )\n}\n\nexport interface CasePanelSignalApprovalActionsProps {\n className?: string\n}\n\nexport function CasePanelSignalApprovalActions({ className }: CasePanelSignalApprovalActionsProps) {\n const {\n approvalState,\n labels,\n hideApproveButton,\n approveButtonIconUrl,\n requestingApproval,\n requestApproval,\n requestDismiss,\n } = useSignalApproval()\n\n if (approvalState !== \"pending\") {\n return <SignalApprovalActions />\n }\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {!hideApproveButton ? (\n <button\n type=\"button\"\n onClick={requestApproval}\n disabled={requestingApproval}\n className=\"inline-flex h-8 items-center gap-1.5 rounded-md border border-foreground bg-foreground px-3 text-xs font-semibold text-background shadow-none transition-colors hover:bg-foreground/90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {requestingApproval ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : approveButtonIconUrl ? (\n <img src={approveButtonIconUrl} alt=\"\" className=\"h-3.5 w-3.5 object-contain\" draggable={false} />\n ) : (\n <CirclePlus className=\"h-3.5 w-3.5\" />\n )}\n {labels.approveButton}\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={requestDismiss}\n className=\"inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-muted-foreground shadow-none transition-colors hover:bg-muted hover:text-foreground\"\n >\n <ThumbsDown className=\"h-3.5 w-3.5\" />\n {labels.dismissButton}\n </button>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/case-panel-why.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { CirclePlus, Loader2, ThumbsDown } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { SignalApprovalActions, useSignalApproval } from \"./signal-feedback-inline\"\n\nexport type CasePanelWhyTone = \"neutral\" | \"alert\" | \"warn\" | \"info\" | \"success\"\n\nexport interface CasePanelWhyRow {\n id: string\n label: React.ReactNode\n value?: React.ReactNode\n meta?: React.ReactNode\n description?: React.ReactNode\n}\n\nexport interface CasePanelWhyItem {\n id: string\n label: React.ReactNode\n /** Count rendered in the summary pill as ×N when provided. */\n count?: number\n summary?: React.ReactNode\n details?: React.ReactNode\n rows?: CasePanelWhyRow[]\n icon?: React.ReactNode\n tone?: CasePanelWhyTone\n}\n\nexport interface CasePanelWhyProps {\n items: CasePanelWhyItem[]\n label?: React.ReactNode\n defaultExpandedId?: string | null\n onExpandedChange?: (itemId: string | null) => void\n className?: string\n}\n\nconst toneDotClasses: Record<CasePanelWhyTone, string> = {\n neutral: \"bg-muted-foreground/50\",\n alert: \"bg-red-500\",\n warn: \"bg-amber-500\",\n info: \"bg-blue-500\",\n success: \"bg-emerald-500\",\n}\n\nfunction makeDomId(...parts: Array<string | undefined>): string {\n return parts\n .filter((part): part is string => Boolean(part))\n .join(\"-\")\n .replace(/[^A-Za-z0-9_-]+/g, \"-\")\n}\n\nfunction DefaultWhyIcon({ tone = \"neutral\" }: { tone?: CasePanelWhyTone }) {\n return <span aria-hidden=\"true\" className={cn(\"h-1.5 w-1.5 rounded-full\", toneDotClasses[tone])} />\n}\n\nexport function CasePanelWhy({\n items,\n label = \"Why\",\n defaultExpandedId = null,\n onExpandedChange,\n className,\n}: CasePanelWhyProps) {\n const [expandedId, setExpandedId] = React.useState<string | null>(defaultExpandedId)\n const reactId = React.useId()\n const idPrefix = makeDomId(\"case-panel-why\", reactId)\n\n const toggleExpanded = React.useCallback(\n (itemId: string) => {\n setExpandedId((current) => {\n const next = current === itemId ? null : itemId\n onExpandedChange?.(next)\n return next\n })\n },\n [onExpandedChange],\n )\n\n if (items.length === 0) return null\n\n return (\n <section className={cn(\"space-y-2\", className)} aria-label={typeof label === \"string\" ? label : \"Work item panel why\"}>\n {label ? (\n <div className=\"text-[11px] font-semibold uppercase tracking-[0.07em] text-muted-foreground\">\n {label}\n </div>\n ) : null}\n\n <div className=\"space-y-2\">\n {items.map((item) => {\n const isExpanded = expandedId === item.id\n const panelId = `${idPrefix}-panel-${makeDomId(item.id)}`\n const buttonId = `${idPrefix}-button-${makeDomId(item.id)}`\n\n return (\n <div key={item.id} className=\"space-y-2\">\n <button\n id={buttonId}\n type=\"button\"\n aria-expanded={isExpanded}\n aria-controls={panelId}\n onClick={() => toggleExpanded(item.id)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault()\n toggleExpanded(item.id)\n }\n }}\n className={cn(\n \"inline-flex min-h-8 max-w-full items-center gap-2 rounded-full border border-border bg-background px-3 py-1.5 text-left text-xs font-semibold text-foreground shadow-[0_1px_1.5px_rgba(0,0,0,0.03)] transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isExpanded ? \"bg-muted/30\" : \"bg-background\",\n )}\n >\n <span className=\"inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-muted\">\n {item.icon ?? <DefaultWhyIcon tone={item.tone} />}\n </span>\n <span className=\"min-w-0 truncate\">{item.label}</span>\n {typeof item.count === \"number\" ? (\n <span className=\"shrink-0 rounded-full bg-muted px-1.5 py-0 text-[10px] font-bold tabular-nums text-muted-foreground\">\n ×{item.count}\n </span>\n ) : null}\n </button>\n\n {isExpanded ? (\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={buttonId}\n className=\"mt-2 rounded-xl border border-border bg-background p-3 shadow-[0_1px_2px_rgba(0,0,0,0.03)]\"\n >\n {item.summary ? <div className=\"text-sm leading-6 text-foreground\">{item.summary}</div> : null}\n {item.details ? <div className=\"mt-2 text-xs leading-5 text-muted-foreground\">{item.details}</div> : null}\n {item.rows && item.rows.length > 0 ? (\n <ul className=\"mt-3 divide-y divide-border/50\" aria-label=\"Why details\">\n {item.rows.map((row) => (\n <li key={row.id} className=\"py-2 first:pt-0 last:pb-0\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium leading-5 text-foreground\">{row.label}</div>\n {row.description ? (\n <div className=\"mt-1 text-xs leading-5 text-muted-foreground\">{row.description}</div>\n ) : null}\n {row.meta ? <div className=\"mt-1 text-[11px] leading-4 text-muted-foreground/80\">{row.meta}</div> : null}\n </div>\n {row.value ? (\n <div className=\"shrink-0 text-right text-xs font-semibold tabular-nums text-foreground\">\n {row.value}\n </div>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n </section>\n )\n}\n\nexport interface CasePanelSignalApprovalActionsProps {\n className?: string\n}\n\nexport function CasePanelSignalApprovalActions({ className }: CasePanelSignalApprovalActionsProps) {\n const {\n approvalState,\n labels,\n hideApproveButton,\n approveButtonIconUrl,\n requestingApproval,\n requestApproval,\n requestDismiss,\n } = useSignalApproval()\n\n if (approvalState !== \"pending\") {\n return <SignalApprovalActions />\n }\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {!hideApproveButton ? (\n <button\n type=\"button\"\n onClick={requestApproval}\n disabled={requestingApproval}\n className=\"inline-flex h-8 items-center gap-1.5 rounded-md border border-foreground bg-foreground px-3 text-xs font-semibold text-background shadow-none transition-colors hover:bg-foreground/90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {requestingApproval ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : approveButtonIconUrl ? (\n <img src={approveButtonIconUrl} alt=\"\" className=\"h-3.5 w-3.5 object-contain\" draggable={false} />\n ) : (\n <CirclePlus className=\"h-3.5 w-3.5\" />\n )}\n {labels.approveButton}\n </button>\n ) : null}\n <button\n type=\"button\"\n onClick={requestDismiss}\n className=\"inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-muted-foreground shadow-none transition-colors hover:bg-muted hover:text-foreground\"\n >\n <ThumbsDown className=\"h-3.5 w-3.5\" />\n {labels.dismissButton}\n </button>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
@@ -69,7 +69,7 @@ function InboxToolbar({
|
|
|
69
69
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
70
70
|
assignee === "me" && "Assigned to me",
|
|
71
71
|
assignee === "team" && "My Team",
|
|
72
|
-
assignee === "all" && "All
|
|
72
|
+
assignee === "all" && "All work items"
|
|
73
73
|
] }),
|
|
74
74
|
/* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 opacity-50" })
|
|
75
75
|
]
|
|
@@ -94,7 +94,7 @@ function InboxToolbar({
|
|
|
94
94
|
] }),
|
|
95
95
|
/* @__PURE__ */ jsxs(DropdownMenuRadioItem, { value: "all", className: "gap-2 text-xs", children: [
|
|
96
96
|
/* @__PURE__ */ jsx(List, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
97
|
-
"All
|
|
97
|
+
"All work items"
|
|
98
98
|
] })
|
|
99
99
|
]
|
|
100
100
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/inbox-toolbar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n User,\n Users,\n List,\n Filter,\n ChevronDown,\n X,\n} from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Badge } from \"./badge\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport type AssigneeFilter = \"me\" | \"team\" | \"all\"\n\nexport interface InboxFilterCategory {\n id: string\n label: string\n icon: React.ReactNode\n options: string[]\n}\n\nexport interface InboxToolbarProps {\n assignee: AssigneeFilter\n onAssigneeChange: (value: AssigneeFilter) => void\n filterCategories: InboxFilterCategory[]\n selectedFilters: Record<string, string>\n onFilterChange: (categoryId: string, value: string) => void\n onClearFilters: () => void\n className?: string\n}\n\nconst FILTER_PILL_COLORS: Record<string, string> = {\n status: \"bg-muted text-foreground border-border hover:bg-muted/80\",\n category: \"bg-blue-50 text-blue-700 border-blue-100 hover:bg-blue-100\",\n account: \"bg-indigo-50 text-indigo-700 border-indigo-100 hover:bg-indigo-100\",\n company: \"bg-purple-50 text-purple-700 border-purple-100 hover:bg-purple-100\",\n}\n\nfunction getFilterPillColor(categoryId: string) {\n return FILTER_PILL_COLORS[categoryId] ?? FILTER_PILL_COLORS.status\n}\n\nexport function InboxToolbar({\n assignee,\n onAssigneeChange,\n filterCategories,\n selectedFilters,\n onFilterChange,\n onClearFilters,\n className,\n}: InboxToolbarProps) {\n const hasActiveFilters = Object.values(selectedFilters).some(\n (v) => v !== \"all\"\n )\n\n return (\n <div\n className={cn(\n \"flex items-center gap-2 overflow-x-auto px-4 py-2 border-b border-border\",\n className\n )}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 shrink-0 gap-1.5 border-border bg-background text-xs font-medium shadow-sm\"\n >\n {assignee === \"me\" && (\n <User className=\"h-3.5 w-3.5 text-brand-purple\" />\n )}\n {assignee === \"team\" && (\n <Users className=\"h-3.5 w-3.5 text-blue-600\" />\n )}\n {assignee === \"all\" && (\n <List className=\"h-3.5 w-3.5 text-muted-foreground\" />\n )}\n <span>\n {assignee === \"me\" && \"Assigned to me\"}\n {assignee === \"team\" && \"My Team\"}\n {assignee === \"all\" && \"All Cases\"}\n </span>\n <ChevronDown className=\"h-3 w-3 opacity-50\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-[180px]\">\n <DropdownMenuLabel>Assignee</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={assignee}\n onValueChange={(v) => onAssigneeChange(v as AssigneeFilter)}\n >\n <DropdownMenuRadioItem value=\"me\" className=\"gap-2 text-xs\">\n <User className=\"h-3.5 w-3.5 text-brand-purple\" />\n Assigned to me\n </DropdownMenuRadioItem>\n <DropdownMenuRadioItem value=\"team\" className=\"gap-2 text-xs\">\n <Users className=\"h-3.5 w-3.5 text-blue-600\" />\n My Team\n </DropdownMenuRadioItem>\n <DropdownMenuRadioItem value=\"all\" className=\"gap-2 text-xs\">\n <List className=\"h-3.5 w-3.5 text-muted-foreground\" />\n All Cases\n </DropdownMenuRadioItem>\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n\n <div className=\"mx-1 h-4 w-px shrink-0 bg-border\" />\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 shrink-0 gap-1.5 border-dashed border-border bg-transparent text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground\"\n >\n <Filter className=\"h-3.5 w-3.5\" />\n Filter\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-[220px]\">\n <DropdownMenuLabel>Add Filter</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {filterCategories.map((category) => (\n <DropdownMenuSub key={category.id}>\n <DropdownMenuSubTrigger className=\"gap-2 text-xs\">\n {category.icon}\n {category.label}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"max-h-[300px] w-[180px] overflow-y-auto\">\n <DropdownMenuRadioGroup\n value={selectedFilters[category.id] ?? \"all\"}\n onValueChange={(v) => onFilterChange(category.id, v)}\n >\n <DropdownMenuRadioItem value=\"all\" className=\"text-xs\">\n All\n </DropdownMenuRadioItem>\n {category.options.map((option) => (\n <DropdownMenuRadioItem\n key={option}\n value={option}\n className=\"text-xs\"\n >\n {option}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n {Object.entries(selectedFilters).map(([categoryId, value]) => {\n if (value === \"all\") return null\n const category = filterCategories.find((c) => c.id === categoryId)\n if (!category) return null\n\n return (\n <Badge\n key={categoryId}\n variant=\"secondary\"\n className={cn(\n \"h-7 shrink-0 gap-1 border pl-2 pr-1 font-normal transition-colors\",\n getFilterPillColor(categoryId)\n )}\n >\n <span className=\"text-[10px] opacity-60\">{category.label}:</span>\n <span className=\"max-w-[120px] truncate text-[10px] font-medium\">\n {value}\n </span>\n <button\n type=\"button\"\n onClick={() => onFilterChange(categoryId, \"all\")}\n className=\"ml-0.5 rounded-sm p-0.5 hover:bg-foreground/10\"\n >\n <X className=\"h-2.5 w-2.5\" />\n <span className=\"sr-only\">Remove</span>\n </button>\n </Badge>\n )\n })}\n\n {hasActiveFilters && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto h-7 shrink-0 px-2 text-xs text-muted-foreground hover:text-foreground\"\n onClick={onClearFilters}\n >\n Clear All\n </Button>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/inbox-toolbar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n User,\n Users,\n List,\n Filter,\n ChevronDown,\n X,\n} from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Badge } from \"./badge\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\n\nexport type AssigneeFilter = \"me\" | \"team\" | \"all\"\n\nexport interface InboxFilterCategory {\n id: string\n label: string\n icon: React.ReactNode\n options: string[]\n}\n\nexport interface InboxToolbarProps {\n assignee: AssigneeFilter\n onAssigneeChange: (value: AssigneeFilter) => void\n filterCategories: InboxFilterCategory[]\n selectedFilters: Record<string, string>\n onFilterChange: (categoryId: string, value: string) => void\n onClearFilters: () => void\n className?: string\n}\n\nconst FILTER_PILL_COLORS: Record<string, string> = {\n status: \"bg-muted text-foreground border-border hover:bg-muted/80\",\n category: \"bg-blue-50 text-blue-700 border-blue-100 hover:bg-blue-100\",\n account: \"bg-indigo-50 text-indigo-700 border-indigo-100 hover:bg-indigo-100\",\n company: \"bg-purple-50 text-purple-700 border-purple-100 hover:bg-purple-100\",\n}\n\nfunction getFilterPillColor(categoryId: string) {\n return FILTER_PILL_COLORS[categoryId] ?? FILTER_PILL_COLORS.status\n}\n\nexport function InboxToolbar({\n assignee,\n onAssigneeChange,\n filterCategories,\n selectedFilters,\n onFilterChange,\n onClearFilters,\n className,\n}: InboxToolbarProps) {\n const hasActiveFilters = Object.values(selectedFilters).some(\n (v) => v !== \"all\"\n )\n\n return (\n <div\n className={cn(\n \"flex items-center gap-2 overflow-x-auto px-4 py-2 border-b border-border\",\n className\n )}\n >\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 shrink-0 gap-1.5 border-border bg-background text-xs font-medium shadow-sm\"\n >\n {assignee === \"me\" && (\n <User className=\"h-3.5 w-3.5 text-brand-purple\" />\n )}\n {assignee === \"team\" && (\n <Users className=\"h-3.5 w-3.5 text-blue-600\" />\n )}\n {assignee === \"all\" && (\n <List className=\"h-3.5 w-3.5 text-muted-foreground\" />\n )}\n <span>\n {assignee === \"me\" && \"Assigned to me\"}\n {assignee === \"team\" && \"My Team\"}\n {assignee === \"all\" && \"All work items\"}\n </span>\n <ChevronDown className=\"h-3 w-3 opacity-50\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-[180px]\">\n <DropdownMenuLabel>Assignee</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuRadioGroup\n value={assignee}\n onValueChange={(v) => onAssigneeChange(v as AssigneeFilter)}\n >\n <DropdownMenuRadioItem value=\"me\" className=\"gap-2 text-xs\">\n <User className=\"h-3.5 w-3.5 text-brand-purple\" />\n Assigned to me\n </DropdownMenuRadioItem>\n <DropdownMenuRadioItem value=\"team\" className=\"gap-2 text-xs\">\n <Users className=\"h-3.5 w-3.5 text-blue-600\" />\n My Team\n </DropdownMenuRadioItem>\n <DropdownMenuRadioItem value=\"all\" className=\"gap-2 text-xs\">\n <List className=\"h-3.5 w-3.5 text-muted-foreground\" />\n All work items\n </DropdownMenuRadioItem>\n </DropdownMenuRadioGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n\n <div className=\"mx-1 h-4 w-px shrink-0 bg-border\" />\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 shrink-0 gap-1.5 border-dashed border-border bg-transparent text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground\"\n >\n <Filter className=\"h-3.5 w-3.5\" />\n Filter\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-[220px]\">\n <DropdownMenuLabel>Add Filter</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {filterCategories.map((category) => (\n <DropdownMenuSub key={category.id}>\n <DropdownMenuSubTrigger className=\"gap-2 text-xs\">\n {category.icon}\n {category.label}\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"max-h-[300px] w-[180px] overflow-y-auto\">\n <DropdownMenuRadioGroup\n value={selectedFilters[category.id] ?? \"all\"}\n onValueChange={(v) => onFilterChange(category.id, v)}\n >\n <DropdownMenuRadioItem value=\"all\" className=\"text-xs\">\n All\n </DropdownMenuRadioItem>\n {category.options.map((option) => (\n <DropdownMenuRadioItem\n key={option}\n value={option}\n className=\"text-xs\"\n >\n {option}\n </DropdownMenuRadioItem>\n ))}\n </DropdownMenuRadioGroup>\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n {Object.entries(selectedFilters).map(([categoryId, value]) => {\n if (value === \"all\") return null\n const category = filterCategories.find((c) => c.id === categoryId)\n if (!category) return null\n\n return (\n <Badge\n key={categoryId}\n variant=\"secondary\"\n className={cn(\n \"h-7 shrink-0 gap-1 border pl-2 pr-1 font-normal transition-colors\",\n getFilterPillColor(categoryId)\n )}\n >\n <span className=\"text-[10px] opacity-60\">{category.label}:</span>\n <span className=\"max-w-[120px] truncate text-[10px] font-medium\">\n {value}\n </span>\n <button\n type=\"button\"\n onClick={() => onFilterChange(categoryId, \"all\")}\n className=\"ml-0.5 rounded-sm p-0.5 hover:bg-foreground/10\"\n >\n <X className=\"h-2.5 w-2.5\" />\n <span className=\"sr-only\">Remove</span>\n </button>\n </Badge>\n )\n })}\n\n {hasActiveFilters && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto h-7 shrink-0 px-2 text-xs text-muted-foreground hover:text-foreground\"\n onClick={onClearFilters}\n >\n Clear All\n </Button>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -110,7 +110,7 @@ describe("CasePanelActivityTimeline", () => {
|
|
|
110
110
|
expect(screen.getByTestId("case-panel-payload-deadline").textContent).toContain("Due tomorrow")
|
|
111
111
|
expect(screen.getByTestId("case-panel-payload-operatorNote").textContent).toContain("Customer asked for legal review.")
|
|
112
112
|
expect(screen.getByTestId("case-panel-payload-assignment").textContent).toContain("Assigned to Jordan as Owner from Maya")
|
|
113
|
-
expect(screen.getByTestId("case-panel-payload-caseOpened").textContent).toContain("
|
|
113
|
+
expect(screen.getByTestId("case-panel-payload-caseOpened").textContent).toContain("Work item opened by Ari from Gmail")
|
|
114
114
|
|
|
115
115
|
fireEvent.click(screen.getByRole("button", { name: "Open signal" }))
|
|
116
116
|
expect(onPayloadAction).toHaveBeenCalledWith({ kind: "openSignal", key: "sig-1", eventId: "signal" })
|
|
@@ -109,7 +109,7 @@ describe("CasePanelDetail structure", () => {
|
|
|
109
109
|
const { container } = renderBasicPanel()
|
|
110
110
|
|
|
111
111
|
const brief = screen.getByText("Key account signals indicate elevated churn risk.")
|
|
112
|
-
const metadata = screen.getByLabelText("
|
|
112
|
+
const metadata = screen.getByLabelText("Work item metadata")
|
|
113
113
|
const position = brief.compareDocumentPosition(metadata)
|
|
114
114
|
|
|
115
115
|
expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
|
|
@@ -157,9 +157,9 @@ describe("CasePanelDetail structure", () => {
|
|
|
157
157
|
it("provides accessibility labels for the detail region and copy/link controls", () => {
|
|
158
158
|
renderBasicPanel()
|
|
159
159
|
|
|
160
|
-
expect(screen.getByRole("region", { name: "
|
|
160
|
+
expect(screen.getByRole("region", { name: "Work item detail" })).toBeTruthy()
|
|
161
161
|
expect(screen.getByRole("button", { name: "Copy call sign" })).toBeTruthy()
|
|
162
162
|
expect(screen.getByRole("link", { name: "Open in Salesforce" })).toBeTruthy()
|
|
163
|
-
expect(screen.getByLabelText("
|
|
163
|
+
expect(screen.getByLabelText("Work item metadata")).toBeTruthy()
|
|
164
164
|
})
|
|
165
165
|
})
|
|
@@ -118,7 +118,7 @@ const PAYLOAD_LABELS: Record<CasePanelActivityPayload["kind"], string> = {
|
|
|
118
118
|
deadline: "Deadline",
|
|
119
119
|
operatorNote: "Operator note",
|
|
120
120
|
assignment: "Assignment",
|
|
121
|
-
caseOpened: "
|
|
121
|
+
caseOpened: "Work item opened",
|
|
122
122
|
generic: "Update",
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -253,7 +253,7 @@ function ActorByline({ actor, timeLabel }: { actor?: CasePanelActivityActor; tim
|
|
|
253
253
|
)
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
const verb = actor.verb ?? "updated this
|
|
256
|
+
const verb = actor.verb ?? "updated this work item"
|
|
257
257
|
const initial = actor.name.charAt(0).toUpperCase()
|
|
258
258
|
|
|
259
259
|
return (
|
|
@@ -409,7 +409,7 @@ function renderPayloadContent(
|
|
|
409
409
|
return (
|
|
410
410
|
<div className="space-y-1">
|
|
411
411
|
<p className="text-foreground">
|
|
412
|
-
|
|
412
|
+
Work item opened{payload.openedBy ? ` by ${payload.openedBy}` : ""}{payload.source ? ` from ${payload.source}` : ""}
|
|
413
413
|
</p>
|
|
414
414
|
{payload.description ? <p className="text-xs leading-relaxed text-muted-foreground">{payload.description}</p> : null}
|
|
415
415
|
</div>
|
|
@@ -30,7 +30,7 @@ const detailWidthClasses: Record<CasePanelDetailWidth, string> = {
|
|
|
30
30
|
export function CasePanelDetail({
|
|
31
31
|
children,
|
|
32
32
|
width = "comfortable",
|
|
33
|
-
"aria-label": ariaLabel = "
|
|
33
|
+
"aria-label": ariaLabel = "Work item detail",
|
|
34
34
|
className,
|
|
35
35
|
}: CasePanelDetailProps) {
|
|
36
36
|
return (
|
|
@@ -208,7 +208,7 @@ export interface CasePanelMetadataRowProps {
|
|
|
208
208
|
|
|
209
209
|
export function CasePanelMetadataRow({
|
|
210
210
|
children,
|
|
211
|
-
label = "
|
|
211
|
+
label = "Work item metadata",
|
|
212
212
|
className,
|
|
213
213
|
}: CasePanelMetadataRowProps) {
|
|
214
214
|
return (
|
|
@@ -79,7 +79,7 @@ export function CasePanelWhy({
|
|
|
79
79
|
if (items.length === 0) return null
|
|
80
80
|
|
|
81
81
|
return (
|
|
82
|
-
<section className={cn("space-y-2", className)} aria-label={typeof label === "string" ? label : "
|
|
82
|
+
<section className={cn("space-y-2", className)} aria-label={typeof label === "string" ? label : "Work item panel why"}>
|
|
83
83
|
{label ? (
|
|
84
84
|
<div className="text-[11px] font-semibold uppercase tracking-[0.07em] text-muted-foreground">
|
|
85
85
|
{label}
|
|
@@ -94,7 +94,7 @@ export function InboxToolbar({
|
|
|
94
94
|
<span>
|
|
95
95
|
{assignee === "me" && "Assigned to me"}
|
|
96
96
|
{assignee === "team" && "My Team"}
|
|
97
|
-
{assignee === "all" && "All
|
|
97
|
+
{assignee === "all" && "All work items"}
|
|
98
98
|
</span>
|
|
99
99
|
<ChevronDown className="h-3 w-3 opacity-50" />
|
|
100
100
|
</Button>
|
|
@@ -116,7 +116,7 @@ export function InboxToolbar({
|
|
|
116
116
|
</DropdownMenuRadioItem>
|
|
117
117
|
<DropdownMenuRadioItem value="all" className="gap-2 text-xs">
|
|
118
118
|
<List className="h-3.5 w-3.5 text-muted-foreground" />
|
|
119
|
-
All
|
|
119
|
+
All work items
|
|
120
120
|
</DropdownMenuRadioItem>
|
|
121
121
|
</DropdownMenuRadioGroup>
|
|
122
122
|
</DropdownMenuContent>
|