@handled-ai/design-system 0.18.24 → 0.18.26
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/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/case-panel-activity-timeline.js +5 -5
- package/dist/components/case-panel-activity-timeline.js.map +1 -1
- package/dist/components/case-panel-detail.d.ts +3 -1
- package/dist/components/case-panel-detail.js +29 -15
- package/dist/components/case-panel-detail.js.map +1 -1
- package/dist/components/case-panel-email-composer.d.ts +2 -1
- package/dist/components/case-panel-email-composer.js +4 -2
- package/dist/components/case-panel-email-composer.js.map +1 -1
- package/dist/components/pill.d.ts +1 -1
- package/dist/components/tabs.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/case-panel-activity-timeline.test.tsx +14 -1
- package/src/components/__tests__/case-panel-detail.test.tsx +17 -1
- package/src/components/__tests__/case-panel-email-composer.test.tsx +7 -0
- package/src/components/case-panel-activity-timeline.tsx +2 -2
- package/src/components/case-panel-detail.tsx +31 -13
- package/src/components/case-panel-email-composer.tsx +25 -21
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const badgeVariants: (props?: ({
|
|
6
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" |
|
|
6
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
|
|
7
7
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
8
8
|
declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
|
|
9
9
|
asChild?: boolean;
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" |
|
|
6
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
|
@@ -155,26 +155,26 @@ function PayloadCard({
|
|
|
155
155
|
] });
|
|
156
156
|
}
|
|
157
157
|
function renderPayloadContent(payload, eventId, onPayloadAction) {
|
|
158
|
-
var _a, _b
|
|
158
|
+
var _a, _b;
|
|
159
159
|
switch (payload.kind) {
|
|
160
160
|
case "signal":
|
|
161
161
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
162
162
|
/* @__PURE__ */ jsx("p", { className: "text-foreground", children: payload.summary }),
|
|
163
163
|
payload.detail ? /* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: payload.detail }) : null,
|
|
164
|
-
onPayloadAction ? /* @__PURE__ */ jsx(
|
|
164
|
+
onPayloadAction && payload.actionLabel ? /* @__PURE__ */ jsx(
|
|
165
165
|
"button",
|
|
166
166
|
{
|
|
167
167
|
type: "button",
|
|
168
168
|
className: "inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground transition-colors hover:text-foreground",
|
|
169
169
|
onClick: () => onPayloadAction({ kind: "openSignal", key: payload.key, eventId }),
|
|
170
|
-
children:
|
|
170
|
+
children: payload.actionLabel
|
|
171
171
|
}
|
|
172
172
|
) : null
|
|
173
173
|
] });
|
|
174
174
|
case "scoreUpdate":
|
|
175
175
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
176
176
|
/* @__PURE__ */ jsxs("p", { className: "text-foreground", children: [
|
|
177
|
-
(
|
|
177
|
+
(_a = payload.label) != null ? _a : "Score",
|
|
178
178
|
": ",
|
|
179
179
|
payload.previousScore !== void 0 ? `${payload.previousScore} \u2192 ` : "",
|
|
180
180
|
payload.nextScore
|
|
@@ -216,7 +216,7 @@ function renderPayloadContent(payload, eventId, onPayloadAction) {
|
|
|
216
216
|
rel: "noreferrer noopener",
|
|
217
217
|
className: "inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground transition-colors hover:text-foreground",
|
|
218
218
|
children: [
|
|
219
|
-
(
|
|
219
|
+
(_b = payload.deepLink.label) != null ? _b : "Open in Salesforce",
|
|
220
220
|
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" })
|
|
221
221
|
]
|
|
222
222
|
}
|
|
@@ -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 }\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 ? (\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 ?? \"Open signal\"}\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 </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 }\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 </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":[]}
|
|
@@ -25,9 +25,11 @@ interface CasePanelIdentityLink {
|
|
|
25
25
|
icon?: React.ReactNode;
|
|
26
26
|
kind?: "icon" | "text";
|
|
27
27
|
disabled?: boolean;
|
|
28
|
+
target?: React.HTMLAttributeAnchorTarget;
|
|
29
|
+
rel?: string;
|
|
28
30
|
}
|
|
29
31
|
interface CasePanelIdentitySublineProps {
|
|
30
|
-
callsign
|
|
32
|
+
callsign?: string | null;
|
|
31
33
|
links?: CasePanelIdentityLink[];
|
|
32
34
|
onCopyCallsign?: (callsign: string) => void;
|
|
33
35
|
copyLabel?: string;
|
|
@@ -46,32 +46,36 @@ function CasePanelIdentitySubline({
|
|
|
46
46
|
className
|
|
47
47
|
}) {
|
|
48
48
|
const [copied, setCopied] = React.useState(false);
|
|
49
|
-
const
|
|
49
|
+
const trimmedCallsign = callsign == null ? void 0 : callsign.trim();
|
|
50
|
+
const normalizedCallsign = trimmedCallsign ? trimmedCallsign.startsWith("@") ? trimmedCallsign : `@${trimmedCallsign}` : null;
|
|
50
51
|
const handleCopy = React.useCallback(() => {
|
|
52
|
+
if (!normalizedCallsign) return;
|
|
51
53
|
onCopyCallsign == null ? void 0 : onCopyCallsign(normalizedCallsign);
|
|
52
54
|
setCopied(true);
|
|
53
55
|
window.setTimeout(() => setCopied(false), 1400);
|
|
54
56
|
}, [normalizedCallsign, onCopyCallsign]);
|
|
55
57
|
return /* @__PURE__ */ jsxs("div", { className: cn("mt-[9px] inline-flex flex-wrap items-center gap-[7px] text-[13px] text-muted-foreground", className), children: [
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
normalizedCallsign ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
59
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium tracking-[0.01em] text-gray-700", children: normalizedCallsign }),
|
|
60
|
+
/* @__PURE__ */ jsx(
|
|
61
|
+
"button",
|
|
62
|
+
{
|
|
63
|
+
type: "button",
|
|
64
|
+
onClick: handleCopy,
|
|
65
|
+
"aria-label": copied ? copiedLabel : copyLabel,
|
|
66
|
+
className: "inline-flex h-[22px] w-[22px] items-center justify-center rounded-md text-gray-400 transition-colors hover:bg-accent hover:text-foreground",
|
|
67
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-[13px] w-[13px] text-emerald-700", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Copy, { className: "h-[13px] w-[13px]", "aria-hidden": "true" })
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
] }) : null,
|
|
67
71
|
links.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
68
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mx-[3px] h-3.5 w-px bg-gray-200" }),
|
|
72
|
+
normalizedCallsign ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mx-[3px] h-3.5 w-px bg-gray-200" }) : null,
|
|
69
73
|
/* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-1.5", children: links.map((link) => /* @__PURE__ */ jsx(CasePanelIdentityLinkButton, { link }, link.id)) })
|
|
70
74
|
] }) : null
|
|
71
75
|
] });
|
|
72
76
|
}
|
|
73
77
|
function CasePanelIdentityLinkButton({ link }) {
|
|
74
|
-
var _a;
|
|
78
|
+
var _a, _b, _c;
|
|
75
79
|
const disabled = link.disabled || !link.href;
|
|
76
80
|
const iconOnly = link.kind === "icon";
|
|
77
81
|
const content = iconOnly ? /* @__PURE__ */ jsx(Fragment, { children: (_a = link.icon) != null ? _a : /* @__PURE__ */ jsx(ExternalLink, { className: "h-[13px] w-[13px]", "aria-hidden": "true" }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -83,7 +87,17 @@ function CasePanelIdentityLinkButton({ link }) {
|
|
|
83
87
|
if (disabled) {
|
|
84
88
|
return /* @__PURE__ */ jsx("button", { type: "button", disabled: true, "aria-label": link.label, className, children: content });
|
|
85
89
|
}
|
|
86
|
-
return /* @__PURE__ */ jsx(
|
|
90
|
+
return /* @__PURE__ */ jsx(
|
|
91
|
+
"a",
|
|
92
|
+
{
|
|
93
|
+
href: link.href,
|
|
94
|
+
"aria-label": link.label,
|
|
95
|
+
target: (_b = link.target) != null ? _b : "_blank",
|
|
96
|
+
rel: (_c = link.rel) != null ? _c : "noopener noreferrer",
|
|
97
|
+
className,
|
|
98
|
+
children: content
|
|
99
|
+
}
|
|
100
|
+
);
|
|
87
101
|
}
|
|
88
102
|
function CasePanelSignalBrief({
|
|
89
103
|
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\" | \"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 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-5\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"m-0 text-[27px] font-bold leading-[1.18] tracking-[-0.02em] text-foreground [text-wrap:balance]\">\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}\n\nexport interface CasePanelIdentitySublineProps {\n callsign: string\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 normalizedCallsign = callsign.startsWith(\"@\") ? callsign : `@${callsign}`\n\n const handleCopy = React.useCallback(() => {\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 <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 {links.length > 0 ? (\n <>\n <span aria-hidden=\"true\" className=\"mx-[3px] h-3.5 w-px bg-gray-200\" />\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 href={link.href} aria-label={link.label} className={className}>\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\" | \"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 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-5\">\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"m-0 text-[27px] font-bold leading-[1.18] tracking-[-0.02em] text-foreground [text-wrap:balance]\">\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":[]}
|
|
@@ -41,6 +41,7 @@ interface CasePanelEmailComposerProps extends Omit<React.HTMLAttributes<HTMLDivE
|
|
|
41
41
|
sendBarActions?: React.ReactNode;
|
|
42
42
|
signatureControl?: React.ReactNode;
|
|
43
43
|
disabledReason?: React.ReactNode;
|
|
44
|
+
showSendBar?: boolean;
|
|
44
45
|
disabled?: boolean;
|
|
45
46
|
sendDisabled?: boolean;
|
|
46
47
|
sendLabel?: React.ReactNode;
|
|
@@ -56,6 +57,6 @@ interface CasePanelEmailComposerProps extends Omit<React.HTMLAttributes<HTMLDivE
|
|
|
56
57
|
showRecipientActionChips?: boolean;
|
|
57
58
|
onComposerKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
|
|
58
59
|
}
|
|
59
|
-
declare function CasePanelEmailComposer({ title, providerLabel, recipientRows, from, to, cc, bcc, subject, toState, draftEditor, toolbar, sendBarActions, signatureControl, disabledReason, disabled, sendDisabled, sendLabel, onSendIntent, onContactsIntent, onAccountDetailsIntent, onAddCcIntent, onAddBccIntent, contactsLabel, accountDetailsLabel, addCcLabel, addBccLabel, showRecipientActionChips, onComposerKeyDown, className, ...props }: CasePanelEmailComposerProps): React.JSX.Element;
|
|
60
|
+
declare function CasePanelEmailComposer({ title, providerLabel, recipientRows, from, to, cc, bcc, subject, toState, draftEditor, toolbar, sendBarActions, signatureControl, disabledReason, showSendBar, disabled, sendDisabled, sendLabel, onSendIntent, onContactsIntent, onAccountDetailsIntent, onAddCcIntent, onAddBccIntent, contactsLabel, accountDetailsLabel, addCcLabel, addBccLabel, showRecipientActionChips, onComposerKeyDown, className, ...props }: CasePanelEmailComposerProps): React.JSX.Element;
|
|
60
61
|
|
|
61
62
|
export { CasePanelEmailComposer, CasePanelEmailComposerChip, type CasePanelEmailComposerChipProps, type CasePanelEmailComposerProps, CasePanelEmailComposerRow, type CasePanelEmailComposerRowProps, type CasePanelEmailComposerRowState, CasePanelEmailComposerSignatureRow, type CasePanelEmailComposerSignatureRowProps, type CasePanelEmailComposerSlotHelpers };
|
|
@@ -163,6 +163,7 @@ function CasePanelEmailComposer(_g) {
|
|
|
163
163
|
sendBarActions,
|
|
164
164
|
signatureControl,
|
|
165
165
|
disabledReason,
|
|
166
|
+
showSendBar = true,
|
|
166
167
|
disabled = false,
|
|
167
168
|
sendDisabled = false,
|
|
168
169
|
sendLabel = "Send",
|
|
@@ -193,6 +194,7 @@ function CasePanelEmailComposer(_g) {
|
|
|
193
194
|
"sendBarActions",
|
|
194
195
|
"signatureControl",
|
|
195
196
|
"disabledReason",
|
|
197
|
+
"showSendBar",
|
|
196
198
|
"disabled",
|
|
197
199
|
"sendDisabled",
|
|
198
200
|
"sendLabel",
|
|
@@ -272,7 +274,7 @@ function CasePanelEmailComposer(_g) {
|
|
|
272
274
|
/* @__PURE__ */ jsx("div", { "data-slot": "case-panel-email-composer-editor", className: "min-h-40 px-4 py-4 text-sm text-foreground", children: draftEditor != null ? draftEditor : /* @__PURE__ */ jsx("div", { className: "text-muted-foreground", children: "Draft editor slot" }) }),
|
|
273
275
|
signatureControl ? /* @__PURE__ */ jsx("div", { "data-slot": "case-panel-email-composer-signature-control", children: signatureControl }) : /* @__PURE__ */ jsx(CasePanelEmailComposerSignatureRow, { disabled }),
|
|
274
276
|
/* @__PURE__ */ jsx("div", { "data-slot": "case-panel-email-composer-toolbar", className: "flex min-h-11 items-center gap-2 border-t border-border/70 bg-muted/10 px-3 py-2", children: toolbar != null ? toolbar : /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Toolbar" }) }),
|
|
275
|
-
/* @__PURE__ */ jsxs("div", { "data-slot": "case-panel-email-composer-send-bar", className: "flex items-center justify-between gap-3 border-t border-border/70 bg-background px-3 py-3", children: [
|
|
277
|
+
showSendBar ? /* @__PURE__ */ jsxs("div", { "data-slot": "case-panel-email-composer-send-bar", className: "flex items-center justify-between gap-3 border-t border-border/70 bg-background px-3 py-3", children: [
|
|
276
278
|
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: disabledReason ? /* @__PURE__ */ jsx("div", { id: disabledReasonId, "data-slot": "case-panel-email-composer-disabled-reason", className: "truncate text-xs text-muted-foreground", children: disabledReason }) : null }),
|
|
277
279
|
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
278
280
|
sendBarActions,
|
|
@@ -289,7 +291,7 @@ function CasePanelEmailComposer(_g) {
|
|
|
289
291
|
}
|
|
290
292
|
)
|
|
291
293
|
] })
|
|
292
|
-
] })
|
|
294
|
+
] }) : null
|
|
293
295
|
] })
|
|
294
296
|
]
|
|
295
297
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/case-panel-email-composer.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"../lib/utils\"\nimport { BRAND_ICONS } from \"../lib/icons\"\n\nexport type CasePanelEmailComposerRowState = \"default\" | \"confirmed\" | \"unconfirmed\"\n\nexport interface CasePanelEmailComposerRowProps extends React.HTMLAttributes<HTMLDivElement> {\n label: React.ReactNode\n children?: React.ReactNode\n value?: React.ReactNode\n placeholder?: React.ReactNode\n actions?: React.ReactNode\n state?: CasePanelEmailComposerRowState\n}\n\nexport function CasePanelEmailComposerRow({\n label,\n children,\n value,\n placeholder,\n actions,\n state = \"default\",\n className,\n ...props\n}: CasePanelEmailComposerRowProps) {\n const content = children ?? value ?? placeholder\n const rowClassName =\n state === \"unconfirmed\"\n ? \"flex min-h-11 items-stretch border-b border-amber-200/80 bg-amber-50/75 text-sm dark:border-amber-900/50 dark:bg-amber-950/20\"\n : \"flex min-h-11 items-stretch border-b border-border/70 bg-background text-sm\"\n const labelClassName =\n state === \"unconfirmed\"\n ? \"flex w-[60px] shrink-0 items-center border-r border-amber-200/80 px-3 text-[11px] font-semibold uppercase tracking-wide text-amber-700 dark:border-amber-900/50 dark:text-amber-300\"\n : \"flex w-[60px] shrink-0 items-center border-r border-border/70 px-3 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\"\n const contentClassName =\n state === \"unconfirmed\"\n ? \"flex min-w-0 flex-1 items-center gap-2 px-3 py-2 text-amber-950 dark:text-amber-100\"\n : \"flex min-w-0 flex-1 items-center gap-2 px-3 py-2 text-foreground\"\n const placeholderClassName =\n state === \"unconfirmed\"\n ? \"min-w-0 truncate text-amber-700/80 dark:text-amber-200/80\"\n : \"min-w-0 truncate text-muted-foreground\"\n\n return (\n <div\n data-slot=\"case-panel-email-composer-row\"\n data-state={state}\n className={cn(rowClassName, className)}\n {...props}\n >\n <div data-slot=\"case-panel-email-composer-row-label\" className={labelClassName}>\n {label}\n </div>\n <div data-slot=\"case-panel-email-composer-row-content\" className={contentClassName}>\n {content ? (\n <div className=\"min-w-0 flex-1 truncate\">{content}</div>\n ) : (\n <div className={placeholderClassName}>Add {label}</div>\n )}\n {actions ? <div className=\"flex shrink-0 items-center gap-1.5\">{actions}</div> : null}\n </div>\n </div>\n )\n}\n\nexport interface CasePanelEmailComposerChipProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n children: React.ReactNode\n}\n\nexport function CasePanelEmailComposerChip({\n children,\n className,\n type = \"button\",\n ...props\n}: CasePanelEmailComposerChipProps) {\n return (\n <button\n data-slot=\"case-panel-email-composer-chip\"\n type={type}\n className={cn(\n \"inline-flex h-6 items-center justify-center rounded-full border border-border bg-background px-2.5 text-[11px] font-medium text-muted-foreground shadow-xs transition-colors hover:bg-muted/60 hover:text-foreground disabled:pointer-events-none disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n}\n\nexport interface CasePanelEmailComposerSignatureRowProps extends React.HTMLAttributes<HTMLDivElement> {\n children?: React.ReactNode\n label?: React.ReactNode\n checked?: boolean\n disabled?: boolean\n}\n\nexport function CasePanelEmailComposerSignatureRow({\n children,\n label = \"Include signature\",\n checked = true,\n disabled,\n className,\n ...props\n}: CasePanelEmailComposerSignatureRowProps) {\n return (\n <div\n data-slot=\"case-panel-email-composer-signature-row\"\n className={cn(\n \"flex items-center justify-between border-t border-border/70 bg-muted/20 px-4 py-2.5 text-xs text-muted-foreground\",\n className,\n )}\n {...props}\n >\n {children ?? (\n <div className=\"flex items-center gap-2\">\n <span\n aria-hidden=\"true\"\n data-checked={checked ? \"true\" : \"false\"}\n className={\n checked\n ? \"flex size-4 items-center justify-center rounded border border-foreground bg-foreground text-[10px] leading-none text-background\"\n : \"flex size-4 items-center justify-center rounded border border-border bg-background text-[10px] leading-none text-background\"\n }\n >\n {checked ? \"✓\" : \"\"}\n </span>\n <span className={disabled ? \"text-muted-foreground/60\" : \"text-muted-foreground\"}>{label}</span>\n </div>\n )}\n </div>\n )\n}\n\nexport interface CasePanelEmailComposerSlotHelpers {\n Row: typeof CasePanelEmailComposerRow\n Chip: typeof CasePanelEmailComposerChip\n SignatureRow: typeof CasePanelEmailComposerSignatureRow\n}\n\nexport interface CasePanelEmailComposerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onKeyDown\" | \"title\"> {\n title?: React.ReactNode\n providerLabel?: React.ReactNode\n recipientRows?: React.ReactNode | ((helpers: CasePanelEmailComposerSlotHelpers) => React.ReactNode)\n from?: React.ReactNode\n to?: React.ReactNode\n cc?: React.ReactNode\n bcc?: React.ReactNode\n subject?: React.ReactNode\n toState?: CasePanelEmailComposerRowState\n draftEditor?: React.ReactNode\n toolbar?: React.ReactNode\n sendBarActions?: React.ReactNode\n signatureControl?: React.ReactNode\n disabledReason?: React.ReactNode\n disabled?: boolean\n sendDisabled?: boolean\n sendLabel?: React.ReactNode\n onSendIntent?: () => void\n onContactsIntent?: () => void\n onAccountDetailsIntent?: () => void\n onAddCcIntent?: () => void\n onAddBccIntent?: () => void\n contactsLabel?: React.ReactNode\n accountDetailsLabel?: React.ReactNode\n addCcLabel?: React.ReactNode\n addBccLabel?: React.ReactNode\n showRecipientActionChips?: boolean\n onComposerKeyDown?: React.KeyboardEventHandler<HTMLDivElement>\n}\n\nconst slotHelpers: CasePanelEmailComposerSlotHelpers = {\n Row: CasePanelEmailComposerRow,\n Chip: CasePanelEmailComposerChip,\n SignatureRow: CasePanelEmailComposerSignatureRow,\n}\n\nexport function CasePanelEmailComposer({\n title = \"Draft\",\n providerLabel = \"Gmail\",\n recipientRows,\n from,\n to,\n cc,\n bcc,\n subject,\n toState = \"default\",\n draftEditor,\n toolbar,\n sendBarActions,\n signatureControl,\n disabledReason,\n disabled = false,\n sendDisabled = false,\n sendLabel = \"Send\",\n onSendIntent,\n onContactsIntent,\n onAccountDetailsIntent,\n onAddCcIntent,\n onAddBccIntent,\n contactsLabel = \"Contacts\",\n accountDetailsLabel = \"Account details\",\n addCcLabel = \"Add Cc\",\n addBccLabel = \"Add Bcc\",\n showRecipientActionChips = true,\n onComposerKeyDown,\n className,\n ...props\n}: CasePanelEmailComposerProps) {\n const disabledReasonId = React.useId()\n const sendIsDisabled = disabled || sendDisabled || Boolean(disabledReason)\n\n const handleSendIntent = React.useCallback(() => {\n if (sendIsDisabled) return\n onSendIntent?.()\n }, [onSendIntent, sendIsDisabled])\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n onComposerKeyDown?.(event)\n if (event.defaultPrevented) return\n if (event.key === \"Enter\" && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n handleSendIntent()\n }\n },\n [handleSendIntent, onComposerKeyDown],\n )\n\n const renderedRecipientRows =\n typeof recipientRows === \"function\"\n ? recipientRows(slotHelpers)\n : recipientRows ?? (\n <>\n <CasePanelEmailComposerRow label=\"From\" value={from} placeholder=\"Sender\" />\n <CasePanelEmailComposerRow label=\"To\" value={to} placeholder=\"Recipient\" state={toState} />\n {cc ? <CasePanelEmailComposerRow label=\"Cc\" value={cc} /> : null}\n {bcc ? <CasePanelEmailComposerRow label=\"Bcc\" value={bcc} /> : null}\n <CasePanelEmailComposerRow label=\"Subject\" value={subject} placeholder=\"Subject\" />\n </>\n )\n\n const showActionChips =\n showRecipientActionChips &&\n (onContactsIntent || onAccountDetailsIntent || onAddCcIntent || onAddBccIntent)\n\n return (\n <div\n data-slot=\"case-panel-email-composer\"\n aria-disabled={disabled ? \"true\" : undefined}\n className={cn(\n \"overflow-hidden rounded-xl border border-border bg-background shadow-sm\",\n className,\n )}\n onKeyDown={handleKeyDown}\n {...props}\n >\n <div data-slot=\"case-panel-email-composer-header\" className=\"flex items-center justify-between border-b border-border/70 bg-muted/20 px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <img\n src={BRAND_ICONS.gmail.icon}\n alt=\"Gmail\"\n className=\"size-4 shrink-0 object-contain\"\n draggable={false}\n />\n <div className=\"min-w-0 truncate text-sm font-semibold text-foreground\">{title}</div>\n </div>\n <div className=\"shrink-0 text-[11px] font-medium text-muted-foreground\">{providerLabel}</div>\n </div>\n\n <div data-slot=\"case-panel-email-composer-card\" className=\"bg-background\">\n <div data-slot=\"case-panel-email-composer-recipient-rows\">{renderedRecipientRows}</div>\n\n {showActionChips ? (\n <div data-slot=\"case-panel-email-composer-chip-row\" className=\"flex flex-wrap items-center gap-1.5 border-b border-border/70 bg-muted/10 px-3 py-2\">\n {onContactsIntent ? (\n <CasePanelEmailComposerChip onClick={onContactsIntent} disabled={disabled}>\n {contactsLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n {onAccountDetailsIntent ? (\n <CasePanelEmailComposerChip onClick={onAccountDetailsIntent} disabled={disabled}>\n {accountDetailsLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n {onAddCcIntent ? (\n <CasePanelEmailComposerChip onClick={onAddCcIntent} disabled={disabled}>\n {addCcLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n {onAddBccIntent ? (\n <CasePanelEmailComposerChip onClick={onAddBccIntent} disabled={disabled}>\n {addBccLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n </div>\n ) : null}\n\n <div data-slot=\"case-panel-email-composer-editor\" className=\"min-h-40 px-4 py-4 text-sm text-foreground\">\n {draftEditor ?? <div className=\"text-muted-foreground\">Draft editor slot</div>}\n </div>\n\n {signatureControl ? (\n <div data-slot=\"case-panel-email-composer-signature-control\">{signatureControl}</div>\n ) : (\n <CasePanelEmailComposerSignatureRow disabled={disabled} />\n )}\n\n <div data-slot=\"case-panel-email-composer-toolbar\" className=\"flex min-h-11 items-center gap-2 border-t border-border/70 bg-muted/10 px-3 py-2\">\n {toolbar ?? <div className=\"text-xs font-medium text-muted-foreground\">Toolbar</div>}\n </div>\n\n <div data-slot=\"case-panel-email-composer-send-bar\" className=\"flex items-center justify-between gap-3 border-t border-border/70 bg-background px-3 py-3\">\n <div className=\"min-w-0 flex-1\">\n {disabledReason ? (\n <div id={disabledReasonId} data-slot=\"case-panel-email-composer-disabled-reason\" className=\"truncate text-xs text-muted-foreground\">\n {disabledReason}\n </div>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {sendBarActions}\n <button\n type=\"button\"\n data-slot=\"case-panel-email-composer-send-button\"\n aria-describedby={disabledReason ? disabledReasonId : undefined}\n disabled={sendIsDisabled}\n onClick={handleSendIntent}\n className=\"inline-flex h-8 items-center justify-center rounded-md bg-foreground px-3 text-xs font-semibold text-background shadow-xs transition-colors hover:bg-foreground/90 disabled:pointer-events-none disabled:bg-muted disabled:text-muted-foreground\"\n >\n {sendLabel}\n </button>\n </div>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/case-panel-email-composer.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"../lib/utils\"\nimport { BRAND_ICONS } from \"../lib/icons\"\n\nexport type CasePanelEmailComposerRowState = \"default\" | \"confirmed\" | \"unconfirmed\"\n\nexport interface CasePanelEmailComposerRowProps extends React.HTMLAttributes<HTMLDivElement> {\n label: React.ReactNode\n children?: React.ReactNode\n value?: React.ReactNode\n placeholder?: React.ReactNode\n actions?: React.ReactNode\n state?: CasePanelEmailComposerRowState\n}\n\nexport function CasePanelEmailComposerRow({\n label,\n children,\n value,\n placeholder,\n actions,\n state = \"default\",\n className,\n ...props\n}: CasePanelEmailComposerRowProps) {\n const content = children ?? value ?? placeholder\n const rowClassName =\n state === \"unconfirmed\"\n ? \"flex min-h-11 items-stretch border-b border-amber-200/80 bg-amber-50/75 text-sm dark:border-amber-900/50 dark:bg-amber-950/20\"\n : \"flex min-h-11 items-stretch border-b border-border/70 bg-background text-sm\"\n const labelClassName =\n state === \"unconfirmed\"\n ? \"flex w-[60px] shrink-0 items-center border-r border-amber-200/80 px-3 text-[11px] font-semibold uppercase tracking-wide text-amber-700 dark:border-amber-900/50 dark:text-amber-300\"\n : \"flex w-[60px] shrink-0 items-center border-r border-border/70 px-3 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground\"\n const contentClassName =\n state === \"unconfirmed\"\n ? \"flex min-w-0 flex-1 items-center gap-2 px-3 py-2 text-amber-950 dark:text-amber-100\"\n : \"flex min-w-0 flex-1 items-center gap-2 px-3 py-2 text-foreground\"\n const placeholderClassName =\n state === \"unconfirmed\"\n ? \"min-w-0 truncate text-amber-700/80 dark:text-amber-200/80\"\n : \"min-w-0 truncate text-muted-foreground\"\n\n return (\n <div\n data-slot=\"case-panel-email-composer-row\"\n data-state={state}\n className={cn(rowClassName, className)}\n {...props}\n >\n <div data-slot=\"case-panel-email-composer-row-label\" className={labelClassName}>\n {label}\n </div>\n <div data-slot=\"case-panel-email-composer-row-content\" className={contentClassName}>\n {content ? (\n <div className=\"min-w-0 flex-1 truncate\">{content}</div>\n ) : (\n <div className={placeholderClassName}>Add {label}</div>\n )}\n {actions ? <div className=\"flex shrink-0 items-center gap-1.5\">{actions}</div> : null}\n </div>\n </div>\n )\n}\n\nexport interface CasePanelEmailComposerChipProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n children: React.ReactNode\n}\n\nexport function CasePanelEmailComposerChip({\n children,\n className,\n type = \"button\",\n ...props\n}: CasePanelEmailComposerChipProps) {\n return (\n <button\n data-slot=\"case-panel-email-composer-chip\"\n type={type}\n className={cn(\n \"inline-flex h-6 items-center justify-center rounded-full border border-border bg-background px-2.5 text-[11px] font-medium text-muted-foreground shadow-xs transition-colors hover:bg-muted/60 hover:text-foreground disabled:pointer-events-none disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n}\n\nexport interface CasePanelEmailComposerSignatureRowProps extends React.HTMLAttributes<HTMLDivElement> {\n children?: React.ReactNode\n label?: React.ReactNode\n checked?: boolean\n disabled?: boolean\n}\n\nexport function CasePanelEmailComposerSignatureRow({\n children,\n label = \"Include signature\",\n checked = true,\n disabled,\n className,\n ...props\n}: CasePanelEmailComposerSignatureRowProps) {\n return (\n <div\n data-slot=\"case-panel-email-composer-signature-row\"\n className={cn(\n \"flex items-center justify-between border-t border-border/70 bg-muted/20 px-4 py-2.5 text-xs text-muted-foreground\",\n className,\n )}\n {...props}\n >\n {children ?? (\n <div className=\"flex items-center gap-2\">\n <span\n aria-hidden=\"true\"\n data-checked={checked ? \"true\" : \"false\"}\n className={\n checked\n ? \"flex size-4 items-center justify-center rounded border border-foreground bg-foreground text-[10px] leading-none text-background\"\n : \"flex size-4 items-center justify-center rounded border border-border bg-background text-[10px] leading-none text-background\"\n }\n >\n {checked ? \"✓\" : \"\"}\n </span>\n <span className={disabled ? \"text-muted-foreground/60\" : \"text-muted-foreground\"}>{label}</span>\n </div>\n )}\n </div>\n )\n}\n\nexport interface CasePanelEmailComposerSlotHelpers {\n Row: typeof CasePanelEmailComposerRow\n Chip: typeof CasePanelEmailComposerChip\n SignatureRow: typeof CasePanelEmailComposerSignatureRow\n}\n\nexport interface CasePanelEmailComposerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onKeyDown\" | \"title\"> {\n title?: React.ReactNode\n providerLabel?: React.ReactNode\n recipientRows?: React.ReactNode | ((helpers: CasePanelEmailComposerSlotHelpers) => React.ReactNode)\n from?: React.ReactNode\n to?: React.ReactNode\n cc?: React.ReactNode\n bcc?: React.ReactNode\n subject?: React.ReactNode\n toState?: CasePanelEmailComposerRowState\n draftEditor?: React.ReactNode\n toolbar?: React.ReactNode\n sendBarActions?: React.ReactNode\n signatureControl?: React.ReactNode\n disabledReason?: React.ReactNode\n showSendBar?: boolean\n disabled?: boolean\n sendDisabled?: boolean\n sendLabel?: React.ReactNode\n onSendIntent?: () => void\n onContactsIntent?: () => void\n onAccountDetailsIntent?: () => void\n onAddCcIntent?: () => void\n onAddBccIntent?: () => void\n contactsLabel?: React.ReactNode\n accountDetailsLabel?: React.ReactNode\n addCcLabel?: React.ReactNode\n addBccLabel?: React.ReactNode\n showRecipientActionChips?: boolean\n onComposerKeyDown?: React.KeyboardEventHandler<HTMLDivElement>\n}\n\nconst slotHelpers: CasePanelEmailComposerSlotHelpers = {\n Row: CasePanelEmailComposerRow,\n Chip: CasePanelEmailComposerChip,\n SignatureRow: CasePanelEmailComposerSignatureRow,\n}\n\nexport function CasePanelEmailComposer({\n title = \"Draft\",\n providerLabel = \"Gmail\",\n recipientRows,\n from,\n to,\n cc,\n bcc,\n subject,\n toState = \"default\",\n draftEditor,\n toolbar,\n sendBarActions,\n signatureControl,\n disabledReason,\n showSendBar = true,\n disabled = false,\n sendDisabled = false,\n sendLabel = \"Send\",\n onSendIntent,\n onContactsIntent,\n onAccountDetailsIntent,\n onAddCcIntent,\n onAddBccIntent,\n contactsLabel = \"Contacts\",\n accountDetailsLabel = \"Account details\",\n addCcLabel = \"Add Cc\",\n addBccLabel = \"Add Bcc\",\n showRecipientActionChips = true,\n onComposerKeyDown,\n className,\n ...props\n}: CasePanelEmailComposerProps) {\n const disabledReasonId = React.useId()\n const sendIsDisabled = disabled || sendDisabled || Boolean(disabledReason)\n\n const handleSendIntent = React.useCallback(() => {\n if (sendIsDisabled) return\n onSendIntent?.()\n }, [onSendIntent, sendIsDisabled])\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n onComposerKeyDown?.(event)\n if (event.defaultPrevented) return\n if (event.key === \"Enter\" && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n handleSendIntent()\n }\n },\n [handleSendIntent, onComposerKeyDown],\n )\n\n const renderedRecipientRows =\n typeof recipientRows === \"function\"\n ? recipientRows(slotHelpers)\n : recipientRows ?? (\n <>\n <CasePanelEmailComposerRow label=\"From\" value={from} placeholder=\"Sender\" />\n <CasePanelEmailComposerRow label=\"To\" value={to} placeholder=\"Recipient\" state={toState} />\n {cc ? <CasePanelEmailComposerRow label=\"Cc\" value={cc} /> : null}\n {bcc ? <CasePanelEmailComposerRow label=\"Bcc\" value={bcc} /> : null}\n <CasePanelEmailComposerRow label=\"Subject\" value={subject} placeholder=\"Subject\" />\n </>\n )\n\n const showActionChips =\n showRecipientActionChips &&\n (onContactsIntent || onAccountDetailsIntent || onAddCcIntent || onAddBccIntent)\n\n return (\n <div\n data-slot=\"case-panel-email-composer\"\n aria-disabled={disabled ? \"true\" : undefined}\n className={cn(\n \"overflow-hidden rounded-xl border border-border bg-background shadow-sm\",\n className,\n )}\n onKeyDown={handleKeyDown}\n {...props}\n >\n <div data-slot=\"case-panel-email-composer-header\" className=\"flex items-center justify-between border-b border-border/70 bg-muted/20 px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <img\n src={BRAND_ICONS.gmail.icon}\n alt=\"Gmail\"\n className=\"size-4 shrink-0 object-contain\"\n draggable={false}\n />\n <div className=\"min-w-0 truncate text-sm font-semibold text-foreground\">{title}</div>\n </div>\n <div className=\"shrink-0 text-[11px] font-medium text-muted-foreground\">{providerLabel}</div>\n </div>\n\n <div data-slot=\"case-panel-email-composer-card\" className=\"bg-background\">\n <div data-slot=\"case-panel-email-composer-recipient-rows\">{renderedRecipientRows}</div>\n\n {showActionChips ? (\n <div data-slot=\"case-panel-email-composer-chip-row\" className=\"flex flex-wrap items-center gap-1.5 border-b border-border/70 bg-muted/10 px-3 py-2\">\n {onContactsIntent ? (\n <CasePanelEmailComposerChip onClick={onContactsIntent} disabled={disabled}>\n {contactsLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n {onAccountDetailsIntent ? (\n <CasePanelEmailComposerChip onClick={onAccountDetailsIntent} disabled={disabled}>\n {accountDetailsLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n {onAddCcIntent ? (\n <CasePanelEmailComposerChip onClick={onAddCcIntent} disabled={disabled}>\n {addCcLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n {onAddBccIntent ? (\n <CasePanelEmailComposerChip onClick={onAddBccIntent} disabled={disabled}>\n {addBccLabel}\n </CasePanelEmailComposerChip>\n ) : null}\n </div>\n ) : null}\n\n <div data-slot=\"case-panel-email-composer-editor\" className=\"min-h-40 px-4 py-4 text-sm text-foreground\">\n {draftEditor ?? <div className=\"text-muted-foreground\">Draft editor slot</div>}\n </div>\n\n {signatureControl ? (\n <div data-slot=\"case-panel-email-composer-signature-control\">{signatureControl}</div>\n ) : (\n <CasePanelEmailComposerSignatureRow disabled={disabled} />\n )}\n\n <div data-slot=\"case-panel-email-composer-toolbar\" className=\"flex min-h-11 items-center gap-2 border-t border-border/70 bg-muted/10 px-3 py-2\">\n {toolbar ?? <div className=\"text-xs font-medium text-muted-foreground\">Toolbar</div>}\n </div>\n\n {showSendBar ? (\n <div data-slot=\"case-panel-email-composer-send-bar\" className=\"flex items-center justify-between gap-3 border-t border-border/70 bg-background px-3 py-3\">\n <div className=\"min-w-0 flex-1\">\n {disabledReason ? (\n <div id={disabledReasonId} data-slot=\"case-panel-email-composer-disabled-reason\" className=\"truncate text-xs text-muted-foreground\">\n {disabledReason}\n </div>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {sendBarActions}\n <button\n type=\"button\"\n data-slot=\"case-panel-email-composer-send-button\"\n aria-describedby={disabledReason ? disabledReasonId : undefined}\n disabled={sendIsDisabled}\n onClick={handleSendIntent}\n className=\"inline-flex h-8 items-center justify-center rounded-md bg-foreground px-3 text-xs font-semibold text-background shadow-xs transition-colors hover:bg-foreground/90 disabled:pointer-events-none disabled:bg-muted disabled:text-muted-foreground\"\n >\n {sendLabel}\n </button>\n </div>\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["_a"]}
|
|
@@ -12,7 +12,7 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
12
12
|
*/
|
|
13
13
|
type PillStatus = "success" | "warning" | "error" | "neutral" | "info";
|
|
14
14
|
declare const pillVariants: (props?: ({
|
|
15
|
-
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "
|
|
15
|
+
variant?: "error" | "default" | "secondary" | "destructive" | "outline" | "ghost" | "neutral" | "info" | "success" | "warning" | null | undefined;
|
|
16
16
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
17
17
|
interface PillProps extends React.ComponentProps<"span">, VariantProps<typeof pillVariants> {
|
|
18
18
|
}
|
|
@@ -5,7 +5,7 @@ import { Tabs as Tabs$1 } from 'radix-ui';
|
|
|
5
5
|
|
|
6
6
|
declare function Tabs({ className, orientation, ...props }: React.ComponentProps<typeof Tabs$1.Root>): React.JSX.Element;
|
|
7
7
|
declare const tabsListVariants: (props?: ({
|
|
8
|
-
variant?: "
|
|
8
|
+
variant?: "line" | "default" | null | undefined;
|
|
9
9
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
10
10
|
declare function TabsList({ className, variant, ...props }: React.ComponentProps<typeof Tabs$1.List> & VariantProps<typeof tabsListVariants>): React.JSX.Element;
|
|
11
11
|
declare function TabsTrigger({ className, ...props }: React.ComponentProps<typeof Tabs$1.Trigger>): React.JSX.Element;
|
package/package.json
CHANGED
|
@@ -91,7 +91,7 @@ describe("CasePanelActivityTimeline", () => {
|
|
|
91
91
|
defaultExpanded
|
|
92
92
|
onPayloadAction={onPayloadAction}
|
|
93
93
|
events={[
|
|
94
|
-
event({ id: "signal", title: "Signal", payload: { kind: "signal", key: "sig-1", summary: "Churn risk rose", detail: "Usage fell 30%." } }),
|
|
94
|
+
event({ id: "signal", title: "Signal", payload: { kind: "signal", key: "sig-1", summary: "Churn risk rose", detail: "Usage fell 30%.", actionLabel: "Open signal" } }),
|
|
95
95
|
event({ id: "score", title: "Score", payload: { kind: "scoreUpdate", previousScore: 62, nextScore: 81, reason: "Executive engagement improved." } }),
|
|
96
96
|
event({ id: "rec", title: "Recommendation", payload: { kind: "recommendation", recommendation: "Send renewal brief", rationale: "Close date is within 14 days." } }),
|
|
97
97
|
event({ id: "email", title: "Email", payload: { kind: "email", from: "rep@example.com", to: "buyer@example.com", subject: "Renewal plan", preview: "Can we meet tomorrow?" } }),
|
|
@@ -116,6 +116,19 @@ describe("CasePanelActivityTimeline", () => {
|
|
|
116
116
|
expect(onPayloadAction).toHaveBeenCalledWith({ kind: "openSignal", key: "sig-1", eventId: "signal" })
|
|
117
117
|
})
|
|
118
118
|
|
|
119
|
+
it("does not render a signal action without an explicit action label", () => {
|
|
120
|
+
render(
|
|
121
|
+
<CasePanelActivityTimeline
|
|
122
|
+
defaultExpanded
|
|
123
|
+
onPayloadAction={vi.fn()}
|
|
124
|
+
events={[event({ id: "signal", title: "Signal", payload: { kind: "signal", key: "event-id", summary: "Legacy signal event" } })]}
|
|
125
|
+
/>
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
expect(screen.queryByRole("button", { name: "Open signal" })).toBeNull()
|
|
129
|
+
expect(screen.getByText("Legacy signal event")).toBeTruthy()
|
|
130
|
+
})
|
|
131
|
+
|
|
119
132
|
it("renders the Salesforce deep-link slot", () => {
|
|
120
133
|
render(
|
|
121
134
|
<CasePanelActivityTimeline
|
|
@@ -58,7 +58,10 @@ describe("CasePanelDetail structure", () => {
|
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
expect(screen.getByText("@acme")).toBeTruthy()
|
|
61
|
-
|
|
61
|
+
const salesforceLink = screen.getByRole("link", { name: "Open in Salesforce" })
|
|
62
|
+
expect(salesforceLink.getAttribute("href")).toBe("https://example.com/sf")
|
|
63
|
+
expect(salesforceLink.getAttribute("target")).toBe("_blank")
|
|
64
|
+
expect(salesforceLink.getAttribute("rel")).toBe("noopener noreferrer")
|
|
62
65
|
expect(screen.getByRole("link", { name: "Open in Admin" }).getAttribute("href")).toBe("https://example.com/admin")
|
|
63
66
|
})
|
|
64
67
|
|
|
@@ -72,6 +75,19 @@ describe("CasePanelDetail structure", () => {
|
|
|
72
75
|
expect(screen.getByRole("button", { name: "Call sign copied" })).toBeTruthy()
|
|
73
76
|
})
|
|
74
77
|
|
|
78
|
+
it("omits copyable callsign chrome when callsign is missing", () => {
|
|
79
|
+
render(
|
|
80
|
+
<CasePanelIdentitySubline
|
|
81
|
+
callsign={null}
|
|
82
|
+
links={[{ id: "admin", label: "Open in Admin", href: "https://example.com/admin", kind: "text" }]}
|
|
83
|
+
/>,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
expect(screen.queryByText(/^@/)).toBeNull()
|
|
87
|
+
expect(screen.queryByRole("button", { name: "Copy call sign" })).toBeNull()
|
|
88
|
+
expect(screen.getByRole("link", { name: "Open in Admin" })).toBeTruthy()
|
|
89
|
+
})
|
|
90
|
+
|
|
75
91
|
it("renders missing identity links as disabled buttons instead of active links", () => {
|
|
76
92
|
render(
|
|
77
93
|
<CasePanelIdentitySubline
|
|
@@ -115,6 +115,13 @@ describe("CasePanelEmailComposer", () => {
|
|
|
115
115
|
expect(screen.getByRole("button", { name: "Send" })).not.toBeNull()
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
+
it("can hide the default send bar when the consumer owns send controls", () => {
|
|
119
|
+
const { container } = render(<CasePanelEmailComposer showSendBar={false} />)
|
|
120
|
+
|
|
121
|
+
expect(container.querySelector('[data-slot="case-panel-email-composer-send-bar"]')).toBeNull()
|
|
122
|
+
expect(screen.queryByRole("button", { name: "Send" })).toBeNull()
|
|
123
|
+
})
|
|
124
|
+
|
|
118
125
|
it("disables send when disabledReason is provided", () => {
|
|
119
126
|
render(<CasePanelEmailComposer disabledReason="Confirm recipient before sending" />)
|
|
120
127
|
|
|
@@ -301,13 +301,13 @@ function renderPayloadContent(
|
|
|
301
301
|
<div className="space-y-2">
|
|
302
302
|
<p className="text-foreground">{payload.summary}</p>
|
|
303
303
|
{payload.detail ? <p className="text-xs leading-relaxed text-muted-foreground">{payload.detail}</p> : null}
|
|
304
|
-
{onPayloadAction ? (
|
|
304
|
+
{onPayloadAction && payload.actionLabel ? (
|
|
305
305
|
<button
|
|
306
306
|
type="button"
|
|
307
307
|
className="inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground transition-colors hover:text-foreground"
|
|
308
308
|
onClick={() => onPayloadAction({ kind: "openSignal", key: payload.key, eventId })}
|
|
309
309
|
>
|
|
310
|
-
{payload.actionLabel
|
|
310
|
+
{payload.actionLabel}
|
|
311
311
|
</button>
|
|
312
312
|
) : null}
|
|
313
313
|
</div>
|
|
@@ -75,10 +75,12 @@ export interface CasePanelIdentityLink {
|
|
|
75
75
|
icon?: React.ReactNode
|
|
76
76
|
kind?: "icon" | "text"
|
|
77
77
|
disabled?: boolean
|
|
78
|
+
target?: React.HTMLAttributeAnchorTarget
|
|
79
|
+
rel?: string
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
export interface CasePanelIdentitySublineProps {
|
|
81
|
-
callsign
|
|
83
|
+
callsign?: string | null
|
|
82
84
|
links?: CasePanelIdentityLink[]
|
|
83
85
|
onCopyCallsign?: (callsign: string) => void
|
|
84
86
|
copyLabel?: string
|
|
@@ -95,9 +97,15 @@ export function CasePanelIdentitySubline({
|
|
|
95
97
|
className,
|
|
96
98
|
}: CasePanelIdentitySublineProps) {
|
|
97
99
|
const [copied, setCopied] = React.useState(false)
|
|
98
|
-
const
|
|
100
|
+
const trimmedCallsign = callsign?.trim()
|
|
101
|
+
const normalizedCallsign = trimmedCallsign
|
|
102
|
+
? trimmedCallsign.startsWith("@")
|
|
103
|
+
? trimmedCallsign
|
|
104
|
+
: `@${trimmedCallsign}`
|
|
105
|
+
: null
|
|
99
106
|
|
|
100
107
|
const handleCopy = React.useCallback(() => {
|
|
108
|
+
if (!normalizedCallsign) return
|
|
101
109
|
onCopyCallsign?.(normalizedCallsign)
|
|
102
110
|
setCopied(true)
|
|
103
111
|
window.setTimeout(() => setCopied(false), 1400)
|
|
@@ -105,18 +113,22 @@ export function CasePanelIdentitySubline({
|
|
|
105
113
|
|
|
106
114
|
return (
|
|
107
115
|
<div className={cn("mt-[9px] inline-flex flex-wrap items-center gap-[7px] text-[13px] text-muted-foreground", className)}>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
{normalizedCallsign ? (
|
|
117
|
+
<>
|
|
118
|
+
<span className="font-mono font-medium tracking-[0.01em] text-gray-700">{normalizedCallsign}</span>
|
|
119
|
+
<button
|
|
120
|
+
type="button"
|
|
121
|
+
onClick={handleCopy}
|
|
122
|
+
aria-label={copied ? copiedLabel : copyLabel}
|
|
123
|
+
className="inline-flex h-[22px] w-[22px] items-center justify-center rounded-md text-gray-400 transition-colors hover:bg-accent hover:text-foreground"
|
|
124
|
+
>
|
|
125
|
+
{copied ? <Check className="h-[13px] w-[13px] text-emerald-700" aria-hidden="true" /> : <Copy className="h-[13px] w-[13px]" aria-hidden="true" />}
|
|
126
|
+
</button>
|
|
127
|
+
</>
|
|
128
|
+
) : null}
|
|
117
129
|
{links.length > 0 ? (
|
|
118
130
|
<>
|
|
119
|
-
<span aria-hidden="true" className="mx-[3px] h-3.5 w-px bg-gray-200" />
|
|
131
|
+
{normalizedCallsign ? <span aria-hidden="true" className="mx-[3px] h-3.5 w-px bg-gray-200" /> : null}
|
|
120
132
|
<span className="inline-flex items-center gap-1.5">
|
|
121
133
|
{links.map((link) => (
|
|
122
134
|
<CasePanelIdentityLinkButton key={link.id} link={link} />
|
|
@@ -154,7 +166,13 @@ function CasePanelIdentityLinkButton({ link }: { link: CasePanelIdentityLink })
|
|
|
154
166
|
}
|
|
155
167
|
|
|
156
168
|
return (
|
|
157
|
-
<a
|
|
169
|
+
<a
|
|
170
|
+
href={link.href}
|
|
171
|
+
aria-label={link.label}
|
|
172
|
+
target={link.target ?? "_blank"}
|
|
173
|
+
rel={link.rel ?? "noopener noreferrer"}
|
|
174
|
+
className={className}
|
|
175
|
+
>
|
|
158
176
|
{content}
|
|
159
177
|
</a>
|
|
160
178
|
)
|
|
@@ -156,6 +156,7 @@ export interface CasePanelEmailComposerProps extends Omit<React.HTMLAttributes<H
|
|
|
156
156
|
sendBarActions?: React.ReactNode
|
|
157
157
|
signatureControl?: React.ReactNode
|
|
158
158
|
disabledReason?: React.ReactNode
|
|
159
|
+
showSendBar?: boolean
|
|
159
160
|
disabled?: boolean
|
|
160
161
|
sendDisabled?: boolean
|
|
161
162
|
sendLabel?: React.ReactNode
|
|
@@ -193,6 +194,7 @@ export function CasePanelEmailComposer({
|
|
|
193
194
|
sendBarActions,
|
|
194
195
|
signatureControl,
|
|
195
196
|
disabledReason,
|
|
197
|
+
showSendBar = true,
|
|
196
198
|
disabled = false,
|
|
197
199
|
sendDisabled = false,
|
|
198
200
|
sendLabel = "Send",
|
|
@@ -313,28 +315,30 @@ export function CasePanelEmailComposer({
|
|
|
313
315
|
{toolbar ?? <div className="text-xs font-medium text-muted-foreground">Toolbar</div>}
|
|
314
316
|
</div>
|
|
315
317
|
|
|
316
|
-
|
|
317
|
-
<div
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
318
|
+
{showSendBar ? (
|
|
319
|
+
<div data-slot="case-panel-email-composer-send-bar" className="flex items-center justify-between gap-3 border-t border-border/70 bg-background px-3 py-3">
|
|
320
|
+
<div className="min-w-0 flex-1">
|
|
321
|
+
{disabledReason ? (
|
|
322
|
+
<div id={disabledReasonId} data-slot="case-panel-email-composer-disabled-reason" className="truncate text-xs text-muted-foreground">
|
|
323
|
+
{disabledReason}
|
|
324
|
+
</div>
|
|
325
|
+
) : null}
|
|
326
|
+
</div>
|
|
327
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
328
|
+
{sendBarActions}
|
|
329
|
+
<button
|
|
330
|
+
type="button"
|
|
331
|
+
data-slot="case-panel-email-composer-send-button"
|
|
332
|
+
aria-describedby={disabledReason ? disabledReasonId : undefined}
|
|
333
|
+
disabled={sendIsDisabled}
|
|
334
|
+
onClick={handleSendIntent}
|
|
335
|
+
className="inline-flex h-8 items-center justify-center rounded-md bg-foreground px-3 text-xs font-semibold text-background shadow-xs transition-colors hover:bg-foreground/90 disabled:pointer-events-none disabled:bg-muted disabled:text-muted-foreground"
|
|
336
|
+
>
|
|
337
|
+
{sendLabel}
|
|
338
|
+
</button>
|
|
339
|
+
</div>
|
|
336
340
|
</div>
|
|
337
|
-
|
|
341
|
+
) : null}
|
|
338
342
|
</div>
|
|
339
343
|
</div>
|
|
340
344
|
)
|