@handled-ai/design-system 0.20.9 → 0.20.10
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/comment-composer.js +69 -49
- package/dist/components/comment-composer.js.map +1 -1
- package/dist/components/conversation-panel.js +84 -127
- package/dist/components/conversation-panel.js.map +1 -1
- package/dist/components/timeline-activity.d.ts +0 -1
- package/dist/components/timeline-activity.js +24 -55
- package/dist/components/timeline-activity.js.map +1 -1
- package/dist/prototype/prototype-inbox-view.js +107 -62
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/comment-composer.test.tsx +7 -3
- package/src/components/__tests__/conversation-panel.test.tsx +11 -75
- package/src/components/__tests__/timeline-activity.test.tsx +0 -36
- package/src/components/comment-composer.tsx +26 -14
- package/src/components/conversation-panel.tsx +29 -83
- package/src/components/timeline-activity.tsx +10 -43
- package/src/prototype/__tests__/detail-view-case-panel-v2.test.tsx +3 -3
- package/src/prototype/__tests__/detail-view-timeline-system-events.test.tsx +17 -14
- package/src/prototype/prototype-inbox-view.tsx +72 -44
|
@@ -34,64 +34,84 @@ function CommentComposer({
|
|
|
34
34
|
"data-slot": "comment-composer",
|
|
35
35
|
"data-open": open ? "true" : void 0,
|
|
36
36
|
className: cn(
|
|
37
|
-
"
|
|
38
|
-
open && "ring-ring/30 ring-2",
|
|
37
|
+
"flex items-start gap-4 rounded-xl transition-colors",
|
|
39
38
|
className
|
|
40
39
|
),
|
|
41
40
|
children: [
|
|
42
|
-
/* @__PURE__ */ jsxs(Avatar, { size: "sm", className: "mt-
|
|
41
|
+
/* @__PURE__ */ jsxs(Avatar, { size: "sm", className: "mt-1", children: [
|
|
43
42
|
(author == null ? void 0 : author.avatarUrl) ? /* @__PURE__ */ jsx(AvatarImage, { src: author.avatarUrl, alt: (_a = author.name) != null ? _a : "You" }) : null,
|
|
44
|
-
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-
|
|
43
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-slate-700 text-[10px] font-semibold uppercase text-white dark:bg-slate-200 dark:text-slate-900", children: getInitials({ name: author == null ? void 0 : author.name, email: author == null ? void 0 : author.email }) })
|
|
45
44
|
] }),
|
|
46
|
-
/* @__PURE__ */ jsxs(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
rows: open ? 3 : 1,
|
|
56
|
-
className: cn(
|
|
57
|
-
"resize-none border-0 bg-transparent px-1 py-0.5 text-sm leading-snug shadow-none focus-visible:ring-0",
|
|
58
|
-
!open && "min-h-0"
|
|
59
|
-
),
|
|
60
|
-
onKeyDown: (e) => {
|
|
61
|
-
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
62
|
-
e.preventDefault();
|
|
63
|
-
post();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
),
|
|
68
|
-
open ? /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-center justify-between gap-2", children: [
|
|
69
|
-
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground inline-flex items-center gap-1 text-[11px]", children: [
|
|
70
|
-
/* @__PURE__ */ jsx(Lock, { size: 12 }),
|
|
71
|
-
" ",
|
|
72
|
-
hint
|
|
73
|
-
] }),
|
|
74
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
45
|
+
/* @__PURE__ */ jsxs(
|
|
46
|
+
"div",
|
|
47
|
+
{
|
|
48
|
+
"data-slot": "comment-composer-shell",
|
|
49
|
+
className: cn(
|
|
50
|
+
"min-w-0 flex-1 rounded-xl border border-border bg-background transition-[box-shadow,border-color]",
|
|
51
|
+
open ? "overflow-hidden shadow-sm" : "shadow-none"
|
|
52
|
+
),
|
|
53
|
+
children: [
|
|
75
54
|
/* @__PURE__ */ jsx(
|
|
76
|
-
|
|
55
|
+
Textarea,
|
|
77
56
|
{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
57
|
+
"data-slot": "comment-composer-input",
|
|
58
|
+
value: text,
|
|
59
|
+
onChange: (e) => setText(e.target.value),
|
|
60
|
+
onFocus: () => setFocused(true),
|
|
61
|
+
placeholder,
|
|
62
|
+
rows: open ? 4 : 1,
|
|
63
|
+
className: cn(
|
|
64
|
+
"resize-none rounded-none border-0 bg-transparent px-5 py-4 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0",
|
|
65
|
+
open ? "min-h-32" : "min-h-14"
|
|
66
|
+
),
|
|
67
|
+
onKeyDown: (e) => {
|
|
68
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
post();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
86
73
|
}
|
|
87
74
|
),
|
|
88
|
-
/* @__PURE__ */ jsxs(
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
75
|
+
open ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-border bg-muted/10 px-5 py-4", children: [
|
|
76
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground", children: [
|
|
77
|
+
/* @__PURE__ */ jsx(Lock, { size: 16, strokeWidth: 1.75 }),
|
|
78
|
+
" ",
|
|
79
|
+
hint
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-3", children: [
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
Button,
|
|
84
|
+
{
|
|
85
|
+
type: "button",
|
|
86
|
+
variant: "ghost",
|
|
87
|
+
size: "sm",
|
|
88
|
+
className: "px-2 text-sm font-medium text-muted-foreground hover:bg-transparent hover:text-foreground",
|
|
89
|
+
onClick: () => {
|
|
90
|
+
setText("");
|
|
91
|
+
setFocused(false);
|
|
92
|
+
},
|
|
93
|
+
children: "Cancel"
|
|
94
|
+
}
|
|
95
|
+
),
|
|
96
|
+
/* @__PURE__ */ jsxs(
|
|
97
|
+
Button,
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
size: "sm",
|
|
101
|
+
disabled: !canPost,
|
|
102
|
+
onClick: post,
|
|
103
|
+
className: "rounded-lg bg-foreground px-4 text-sm font-semibold text-background shadow-none hover:bg-foreground/90",
|
|
104
|
+
children: [
|
|
105
|
+
"Comment",
|
|
106
|
+
/* @__PURE__ */ jsx("kbd", { className: "ml-1 rounded px-1 text-[10px] text-background/70", children: "\u2318\u21B5" })
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
] })
|
|
111
|
+
] }) : null
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
)
|
|
95
115
|
]
|
|
96
116
|
}
|
|
97
117
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/comment-composer.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * comment-composer.tsx — an internal-note composer for the case activity\n * timeline. Posting a comment prepends an `operatorNote` event to the log\n * (wired by the consumer). Collapses to a single line; expands on focus or\n * when it has text. ⌘↵ / Ctrl↵ posts.\n *\n * Presentational: `onPost` does the work (the consumer persists the note and\n * adds it to the timeline). Reuses Avatar / Button / Textarea primitives.\n */\n\nimport * as React from \"react\"\nimport { Lock } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { Button } from \"./button\"\nimport { Textarea } from \"./textarea\"\n\nexport interface CommentComposerProps {\n /** Called with the trimmed note text when the operator posts. */\n onPost: (text: string) => void\n /** Current operator (for the avatar). */\n author?: { name?: string; email?: string; avatarUrl?: string | null }\n placeholder?: string\n /** Hint shown in the footer; defaults to the internal-note reassurance. */\n hint?: string\n className?: string\n}\n\nfunction CommentComposer({\n onPost,\n author,\n placeholder = \"Add a comment or internal note…\",\n hint = \"Internal note: only your team sees this\",\n className,\n}: CommentComposerProps) {\n const [text, setText] = React.useState(\"\")\n const [focused, setFocused] = React.useState(false)\n const open = focused || text.length > 0\n const canPost = text.trim().length > 0\n\n const post = () => {\n const value = text.trim()\n if (!value) return\n onPost(value)\n setText(\"\")\n setFocused(false)\n }\n\n return (\n <div\n data-slot=\"comment-composer\"\n data-open={open ? \"true\" : undefined}\n className={cn(\n \"
|
|
1
|
+
{"version":3,"sources":["../../src/components/comment-composer.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * comment-composer.tsx — an internal-note composer for the case activity\n * timeline. Posting a comment prepends an `operatorNote` event to the log\n * (wired by the consumer). Collapses to a single line; expands on focus or\n * when it has text. ⌘↵ / Ctrl↵ posts.\n *\n * Presentational: `onPost` does the work (the consumer persists the note and\n * adds it to the timeline). Reuses Avatar / Button / Textarea primitives.\n */\n\nimport * as React from \"react\"\nimport { Lock } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { getInitials } from \"../lib/user-display\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"./avatar\"\nimport { Button } from \"./button\"\nimport { Textarea } from \"./textarea\"\n\nexport interface CommentComposerProps {\n /** Called with the trimmed note text when the operator posts. */\n onPost: (text: string) => void\n /** Current operator (for the avatar). */\n author?: { name?: string; email?: string; avatarUrl?: string | null }\n placeholder?: string\n /** Hint shown in the footer; defaults to the internal-note reassurance. */\n hint?: string\n className?: string\n}\n\nfunction CommentComposer({\n onPost,\n author,\n placeholder = \"Add a comment or internal note…\",\n hint = \"Internal note: only your team sees this\",\n className,\n}: CommentComposerProps) {\n const [text, setText] = React.useState(\"\")\n const [focused, setFocused] = React.useState(false)\n const open = focused || text.length > 0\n const canPost = text.trim().length > 0\n\n const post = () => {\n const value = text.trim()\n if (!value) return\n onPost(value)\n setText(\"\")\n setFocused(false)\n }\n\n return (\n <div\n data-slot=\"comment-composer\"\n data-open={open ? \"true\" : undefined}\n className={cn(\n \"flex items-start gap-4 rounded-xl transition-colors\",\n className\n )}\n >\n <Avatar size=\"sm\" className=\"mt-1\">\n {author?.avatarUrl ? <AvatarImage src={author.avatarUrl} alt={author.name ?? \"You\"} /> : null}\n <AvatarFallback className=\"bg-slate-700 text-[10px] font-semibold uppercase text-white dark:bg-slate-200 dark:text-slate-900\">\n {getInitials({ name: author?.name, email: author?.email })}\n </AvatarFallback>\n </Avatar>\n\n <div\n data-slot=\"comment-composer-shell\"\n className={cn(\n \"min-w-0 flex-1 rounded-xl border border-border bg-background transition-[box-shadow,border-color]\",\n open ? \"overflow-hidden shadow-sm\" : \"shadow-none\"\n )}\n >\n <Textarea\n data-slot=\"comment-composer-input\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n onFocus={() => setFocused(true)}\n placeholder={placeholder}\n rows={open ? 4 : 1}\n className={cn(\n \"resize-none rounded-none border-0 bg-transparent px-5 py-4 text-[15px] leading-6 shadow-none outline-none placeholder:text-muted-foreground/60 focus-visible:ring-0 focus-visible:ring-offset-0\",\n open ? \"min-h-32\" : \"min-h-14\"\n )}\n onKeyDown={(e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n e.preventDefault()\n post()\n }\n }}\n />\n\n {open ? (\n <div className=\"flex items-center justify-between gap-3 border-t border-border bg-muted/10 px-5 py-4\">\n <span className=\"inline-flex items-center gap-2 text-sm text-muted-foreground\">\n <Lock size={16} strokeWidth={1.75} /> {hint}\n </span>\n <span className=\"flex items-center gap-3\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"px-2 text-sm font-medium text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={() => {\n setText(\"\")\n setFocused(false)\n }}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={!canPost}\n onClick={post}\n className=\"rounded-lg bg-foreground px-4 text-sm font-semibold text-background shadow-none hover:bg-foreground/90\"\n >\n Comment\n <kbd className=\"ml-1 rounded px-1 text-[10px] text-background/70\">⌘↵</kbd>\n </Button>\n </span>\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport { CommentComposer }\n"],"mappings":";AA6DM,SACuB,KADvB;AAjDN,YAAY,WAAW;AACvB,SAAS,YAAY;AAErB,SAAS,UAAU;AACnB,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,gBAAgB,mBAAmB;AACpD,SAAS,cAAc;AACvB,SAAS,gBAAgB;AAazB,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,OAAO;AAAA,EACP;AACF,GAAyB;AAtCzB;AAuCE,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,EAAE;AACzC,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,OAAO,WAAW,KAAK,SAAS;AACtC,QAAM,UAAU,KAAK,KAAK,EAAE,SAAS;AAErC,QAAM,OAAO,MAAM;AACjB,UAAM,QAAQ,KAAK,KAAK;AACxB,QAAI,CAAC,MAAO;AACZ,WAAO,KAAK;AACZ,YAAQ,EAAE;AACV,eAAW,KAAK;AAAA,EAClB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,aAAW,OAAO,SAAS;AAAA,MAC3B,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,UAAO,MAAK,MAAK,WAAU,QACzB;AAAA,4CAAQ,aAAY,oBAAC,eAAY,KAAK,OAAO,WAAW,MAAK,YAAO,SAAP,YAAe,OAAO,IAAK;AAAA,UACzF,oBAAC,kBAAe,WAAU,qGACvB,sBAAY,EAAE,MAAM,iCAAQ,MAAM,OAAO,iCAAQ,MAAM,CAAC,GAC3D;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,aAAU;AAAA,YACV,WAAW;AAAA,cACT;AAAA,cACA,OAAO,8BAA8B;AAAA,YACvC;AAAA,YAEA;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,kBACvC,SAAS,MAAM,WAAW,IAAI;AAAA,kBAC9B;AAAA,kBACA,MAAM,OAAO,IAAI;AAAA,kBACjB,WAAW;AAAA,oBACT;AAAA,oBACA,OAAO,aAAa;AAAA,kBACtB;AAAA,kBACA,WAAW,CAAC,MAAM;AAChB,yBAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAAS;AACjD,wBAAE,eAAe;AACjB,2BAAK;AAAA,oBACP;AAAA,kBACF;AAAA;AAAA,cACF;AAAA,cAEC,OACC,qBAAC,SAAI,WAAU,wFACb;AAAA,qCAAC,UAAK,WAAU,gEACd;AAAA,sCAAC,QAAK,MAAM,IAAI,aAAa,MAAM;AAAA,kBAAE;AAAA,kBAAE;AAAA,mBACzC;AAAA,gBACA,qBAAC,UAAK,WAAU,2BACd;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,SAAS,MAAM;AACb,gCAAQ,EAAE;AACV,mCAAW,KAAK;AAAA,sBAClB;AAAA,sBACD;AAAA;AAAA,kBAED;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,MAAK;AAAA,sBACL,UAAU,CAAC;AAAA,sBACX,SAAS;AAAA,sBACT,WAAU;AAAA,sBACX;AAAA;AAAA,wBAEC,oBAAC,SAAI,WAAU,oDAAmD,0BAAE;AAAA;AAAA;AAAA,kBACtE;AAAA,mBACF;AAAA,iBACF,IACE;AAAA;AAAA;AAAA,QACN;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -145,30 +145,17 @@ function PersonAvatar({ person, size = "sm" }) {
|
|
|
145
145
|
] });
|
|
146
146
|
}
|
|
147
147
|
const STATUS_PILL = {
|
|
148
|
-
responded: { label: "
|
|
148
|
+
responded: { label: "New reply", cls: "bg-status-active-bg text-status-active-fg border-status-active-border" },
|
|
149
149
|
draft: { label: "Draft", cls: "bg-background text-foreground/80 border-border" },
|
|
150
|
-
awaiting: { label: "
|
|
150
|
+
awaiting: { label: "Awaiting", cls: "bg-status-pending-bg text-status-pending-fg border-status-pending-border" },
|
|
151
151
|
viewing: { label: "Viewing", cls: "bg-muted text-muted-foreground border-border" }
|
|
152
152
|
};
|
|
153
153
|
const STATUS_DOT = {
|
|
154
|
-
responded: "bg-status-
|
|
154
|
+
responded: "bg-status-active-fg",
|
|
155
155
|
draft: "bg-status-pending-fg",
|
|
156
|
-
awaiting: "bg-status-
|
|
156
|
+
awaiting: "bg-status-pending-fg",
|
|
157
157
|
viewing: "bg-muted-foreground/50"
|
|
158
158
|
};
|
|
159
|
-
const THREAD_ROW_ACCENT = {
|
|
160
|
-
responded: "border-l-4 border-l-status-warning-border bg-status-warning-bg/25",
|
|
161
|
-
awaiting: "border-l-4 border-l-status-info-border bg-status-info-bg/25",
|
|
162
|
-
draft: "border-l-4 border-l-status-pending-border bg-status-pending-bg/20",
|
|
163
|
-
viewing: ""
|
|
164
|
-
};
|
|
165
|
-
const RECEIPT_CHIP = {
|
|
166
|
-
new: "border-status-warning-border bg-status-warning-bg text-status-warning-fg",
|
|
167
|
-
read: "border-status-info-border bg-status-info-bg text-status-info-fg",
|
|
168
|
-
opened: "border-status-info-border bg-status-info-bg text-status-info-fg",
|
|
169
|
-
sent: "border-status-info-border bg-status-info-bg text-status-info-fg",
|
|
170
|
-
draft: "border-status-pending-border bg-status-pending-bg text-status-pending-fg"
|
|
171
|
-
};
|
|
172
159
|
function effectiveStatus(t) {
|
|
173
160
|
return t.canReply === false ? "viewing" : t.status;
|
|
174
161
|
}
|
|
@@ -271,8 +258,8 @@ function MessageView({
|
|
|
271
258
|
] })
|
|
272
259
|
] }),
|
|
273
260
|
/* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2", children: [
|
|
274
|
-
message.receipt ? /* @__PURE__ */ jsxs("span", { className:
|
|
275
|
-
message.receipt.kind === "new" ? /* @__PURE__ */ jsx(CornerUpLeft, { size: 11 }) : message.receipt.kind === "read"
|
|
261
|
+
message.receipt ? /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground inline-flex items-center gap-1 text-[11px]", children: [
|
|
262
|
+
message.receipt.kind === "new" ? /* @__PURE__ */ jsx(CornerUpLeft, { size: 11 }) : message.receipt.kind === "read" ? /* @__PURE__ */ jsx(CheckCheck, { size: 11 }) : message.receipt.kind === "draft" ? /* @__PURE__ */ jsx(FilePenLine, { size: 11 }) : /* @__PURE__ */ jsx(MailOpen, { size: 11 }),
|
|
276
263
|
message.receipt.label
|
|
277
264
|
] }) : null,
|
|
278
265
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 text-xs", children: message.date }),
|
|
@@ -595,13 +582,13 @@ function ThreadBody({
|
|
|
595
582
|
}, [sortedMessages]);
|
|
596
583
|
const toggle = (id) => setExpanded((e) => __spreadProps(__spreadValues({}, e), { [id]: !e[id] }));
|
|
597
584
|
return /* @__PURE__ */ jsxs("div", { "data-slot": "conv-thread-body", className: "space-y-2", children: [
|
|
598
|
-
canReply && thread.paused ? /* @__PURE__ */ jsxs("div", { className: "border-status-
|
|
585
|
+
canReply && thread.paused ? /* @__PURE__ */ jsxs("div", { className: "border-status-pending-border bg-status-pending-bg text-status-pending-fg flex items-start gap-2 rounded-md border p-2.5 text-[12px]", children: [
|
|
599
586
|
/* @__PURE__ */ jsx(Pause, { size: 13, className: "mt-0.5 shrink-0" }),
|
|
600
587
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
601
|
-
/* @__PURE__ */ jsx("b", { children: "
|
|
602
|
-
"
|
|
588
|
+
/* @__PURE__ */ jsx("b", { children: "Follow-up actions stopped." }),
|
|
589
|
+
" Your ",
|
|
603
590
|
thread.paused.playbook,
|
|
604
|
-
" won\u2019t send automatically while this conversation is live. Continue it in ",
|
|
591
|
+
" next steps won\u2019t send automatically while this conversation is live. Continue it in ",
|
|
605
592
|
tenantName != null ? tenantName : "the app",
|
|
606
593
|
" or Gmail."
|
|
607
594
|
] })
|
|
@@ -707,56 +694,47 @@ function ThreadRow({
|
|
|
707
694
|
const who = (last == null ? void 0 : last.direction) === "outbound" && sameEmail(lastSender == null ? void 0 : lastSender.email, meDisplay == null ? void 0 : meDisplay.email) ? "You" : firstName((_a = lastSender == null ? void 0 : lastSender.name) != null ? _a : "");
|
|
708
695
|
const lastSnippet = last ? messageBodySnippet(last, 120) : "";
|
|
709
696
|
const pill = STATUS_PILL[status];
|
|
710
|
-
return /* @__PURE__ */ jsxs(
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
"
|
|
720
|
-
{
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
children: [
|
|
726
|
-
/* @__PURE__ */ jsx("
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
onSendReply,
|
|
752
|
-
onCreateGmailDraft,
|
|
753
|
-
onPreviewReply,
|
|
754
|
-
onOpenInGmail
|
|
755
|
-
}
|
|
756
|
-
) }) : null
|
|
757
|
-
]
|
|
758
|
-
}
|
|
759
|
-
);
|
|
697
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": "conv-thread", "data-open": open ? "true" : void 0, className: "border-border border-b last:border-b-0", children: [
|
|
698
|
+
/* @__PURE__ */ jsxs(
|
|
699
|
+
"button",
|
|
700
|
+
{
|
|
701
|
+
type: "button",
|
|
702
|
+
onClick: onToggleOpen,
|
|
703
|
+
"aria-expanded": open,
|
|
704
|
+
className: "flex w-full items-center gap-2.5 px-3 py-2.5 text-left hover:bg-muted/30",
|
|
705
|
+
children: [
|
|
706
|
+
/* @__PURE__ */ jsx("span", { className: cn("size-2 shrink-0 rounded-full", STATUS_DOT[status]), "aria-hidden": true }),
|
|
707
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
708
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
709
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold", children: thread.subject }),
|
|
710
|
+
/* @__PURE__ */ jsx("span", { className: cn("shrink-0 rounded-md border px-1.5 py-px text-[10px] font-medium leading-4", pill.cls), children: pill.label })
|
|
711
|
+
] }),
|
|
712
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground block truncate text-xs", children: [
|
|
713
|
+
/* @__PURE__ */ jsx("b", { className: "text-foreground/80", children: displayParticipant(thread.contact).name }),
|
|
714
|
+
" \xB7 ",
|
|
715
|
+
who,
|
|
716
|
+
": ",
|
|
717
|
+
lastSnippet
|
|
718
|
+
] })
|
|
719
|
+
] }),
|
|
720
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 shrink-0 text-xs", children: thread.lastWhen }),
|
|
721
|
+
open ? /* @__PURE__ */ jsx(ChevronUp, { size: 15, className: "text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 15, className: "text-muted-foreground shrink-0" })
|
|
722
|
+
]
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
open ? /* @__PURE__ */ jsx("div", { className: "px-3 pb-3", children: /* @__PURE__ */ jsx(
|
|
726
|
+
ThreadBody,
|
|
727
|
+
{
|
|
728
|
+
thread,
|
|
729
|
+
me,
|
|
730
|
+
tenantName,
|
|
731
|
+
onSendReply,
|
|
732
|
+
onCreateGmailDraft,
|
|
733
|
+
onPreviewReply,
|
|
734
|
+
onOpenInGmail
|
|
735
|
+
}
|
|
736
|
+
) }) : null
|
|
737
|
+
] });
|
|
760
738
|
}
|
|
761
739
|
function ConversationPanel({
|
|
762
740
|
threads,
|
|
@@ -773,82 +751,61 @@ function ConversationPanel({
|
|
|
773
751
|
const draft = threads.filter((t) => effectiveStatus(t) === "draft").length;
|
|
774
752
|
const awaiting = threads.filter((t) => effectiveStatus(t) === "awaiting").length;
|
|
775
753
|
const anyPaused = threads.some((t) => t.paused);
|
|
776
|
-
const hubGmailThread = threads.find((t) => canOpenInGmail(t, onOpenInGmail));
|
|
777
|
-
const firstAwaiting = threads.find((t) => effectiveStatus(t) === "awaiting");
|
|
778
754
|
const [hubOpen, setHubOpen] = React.useState(true);
|
|
779
755
|
const [openId, setOpenId] = React.useState(() => {
|
|
780
756
|
if (defaultOpenThreadId) return defaultOpenThreadId;
|
|
781
|
-
const firstActionable = threads.find((t) => ["responded", "draft"
|
|
757
|
+
const firstActionable = threads.find((t) => ["responded", "draft"].includes(t.status) && t.canReply !== false);
|
|
782
758
|
return firstActionable ? firstActionable.threadId : null;
|
|
783
759
|
});
|
|
784
760
|
if (!threads.length) return null;
|
|
785
|
-
const badge = responded > 0 ? { label: "Email response detected", dot: "bg-status-
|
|
786
|
-
const headTitle = responded > 0 ? `${responded} ${responded === 1 ? "reply needs" : "replies need"} your response` : draft > 0 ? `Draft ready on ${draft} ${draft === 1 ? "thread" : "threads"}` : awaiting > 0 ?
|
|
787
|
-
const panelState = responded > 0 ? "responded" : draft > 0 ? "draft" : awaiting > 0 ? "awaiting" : "viewing";
|
|
761
|
+
const badge = responded > 0 ? { label: "Email response detected", dot: "bg-status-active-fg", ring: "bg-status-active-fg/30" } : draft > 0 ? { label: "Draft ready", dot: "bg-status-pending-fg", ring: "bg-status-pending-fg/30" } : awaiting > 0 ? { label: "Awaiting response", dot: "bg-status-pending-fg", ring: "bg-status-pending-fg/30" } : { label: "Conversations", dot: "bg-muted-foreground/50", ring: "bg-muted-foreground/20" };
|
|
762
|
+
const headTitle = responded > 0 ? `${responded} ${responded === 1 ? "reply needs" : "replies need"} your response` : draft > 0 ? `Draft ready on ${draft} ${draft === 1 ? "thread" : "threads"}` : awaiting > 0 ? `Awaiting response on ${awaiting} ${awaiting === 1 ? "thread" : "threads"}` : `${threads.length} email ${threads.length === 1 ? "thread" : "threads"}`;
|
|
788
763
|
return /* @__PURE__ */ jsxs(
|
|
789
764
|
"section",
|
|
790
765
|
{
|
|
791
766
|
"data-slot": "conversation-panel",
|
|
792
767
|
"data-responded": responded > 0 ? "true" : void 0,
|
|
793
|
-
"
|
|
794
|
-
className: cn(
|
|
795
|
-
"bg-background overflow-hidden rounded-xl border",
|
|
796
|
-
panelState === "responded" ? "border-status-warning-border" : panelState === "awaiting" ? "border-status-info-border" : "border-border",
|
|
797
|
-
className
|
|
798
|
-
),
|
|
768
|
+
className: cn("border-border bg-background overflow-hidden rounded-xl border", className),
|
|
799
769
|
children: [
|
|
800
770
|
/* @__PURE__ */ jsxs(
|
|
801
|
-
"
|
|
771
|
+
"button",
|
|
802
772
|
{
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
),
|
|
773
|
+
type: "button",
|
|
774
|
+
onClick: () => setHubOpen((v) => !v),
|
|
775
|
+
"aria-expanded": hubOpen,
|
|
776
|
+
className: "flex w-full items-center gap-3 px-3 py-2.5 text-left",
|
|
808
777
|
children: [
|
|
809
778
|
/* @__PURE__ */ jsxs(
|
|
810
|
-
"
|
|
779
|
+
"span",
|
|
811
780
|
{
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
781
|
+
"data-slot": "conversation-badge",
|
|
782
|
+
className: cn(
|
|
783
|
+
"inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[11px] font-semibold",
|
|
784
|
+
responded > 0 ? "bg-status-active-bg text-status-active-fg" : draft > 0 || awaiting > 0 ? "bg-status-pending-bg text-status-pending-fg" : "bg-muted text-muted-foreground"
|
|
785
|
+
),
|
|
816
786
|
children: [
|
|
817
|
-
/* @__PURE__ */ jsxs(
|
|
818
|
-
"span",
|
|
819
|
-
{
|
|
820
|
-
"data-slot": "conversation-badge",
|
|
821
|
-
className: cn(
|
|
822
|
-
"inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[11px] font-semibold",
|
|
823
|
-
responded > 0 ? "bg-status-warning-bg text-status-warning-fg" : awaiting > 0 ? "bg-status-info-bg text-status-info-fg" : draft > 0 ? "bg-status-pending-bg text-status-pending-fg" : "bg-muted text-muted-foreground"
|
|
824
|
-
),
|
|
825
|
-
children: [
|
|
826
|
-
/* @__PURE__ */ jsxs("span", { className: "relative inline-flex size-2", children: [
|
|
827
|
-
/* @__PURE__ */ jsx("span", { className: cn("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", badge.ring) }),
|
|
828
|
-
/* @__PURE__ */ jsx("span", { className: cn("relative inline-flex size-2 rounded-full", badge.dot) })
|
|
829
|
-
] }),
|
|
830
|
-
badge.label
|
|
831
|
-
]
|
|
832
|
-
}
|
|
833
|
-
),
|
|
834
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
835
|
-
/* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold leading-tight", children: headTitle }),
|
|
836
|
-
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mt-0.5 block truncate text-xs", children: [
|
|
837
|
-
threads.length,
|
|
838
|
-
" ",
|
|
839
|
-
threads.length === 1 ? "thread" : "threads",
|
|
840
|
-
" on this action",
|
|
841
|
-
anyPaused ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
842
|
-
" \xB7 ",
|
|
843
|
-
/* @__PURE__ */ jsx("b", { children: "playbook stopped" })
|
|
844
|
-
] }) : null
|
|
845
|
-
] })
|
|
787
|
+
/* @__PURE__ */ jsxs("span", { className: "relative inline-flex size-2", children: [
|
|
788
|
+
/* @__PURE__ */ jsx("span", { className: cn("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", badge.ring) }),
|
|
789
|
+
/* @__PURE__ */ jsx("span", { className: cn("relative inline-flex size-2 rounded-full", badge.dot) })
|
|
846
790
|
] }),
|
|
847
|
-
|
|
791
|
+
badge.label
|
|
848
792
|
]
|
|
849
793
|
}
|
|
850
794
|
),
|
|
851
|
-
|
|
795
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
796
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold leading-tight", children: headTitle }),
|
|
797
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground mt-0.5 block truncate text-xs", children: [
|
|
798
|
+
threads.length,
|
|
799
|
+
" ",
|
|
800
|
+
threads.length === 1 ? "thread" : "threads",
|
|
801
|
+
" on this action",
|
|
802
|
+
anyPaused ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
803
|
+
" \xB7 ",
|
|
804
|
+
/* @__PURE__ */ jsx("b", { children: "playbook stopped" })
|
|
805
|
+
] }) : null
|
|
806
|
+
] })
|
|
807
|
+
] }),
|
|
808
|
+
hubOpen ? /* @__PURE__ */ jsx(ChevronUp, { size: 16, className: "text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 16, className: "text-muted-foreground shrink-0" })
|
|
852
809
|
]
|
|
853
810
|
}
|
|
854
811
|
),
|