@handled-ai/design-system 0.18.38 → 0.18.40
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/charts/chart.d.ts +1 -1
- package/dist/components/draft-feedback-inline.d.ts +1 -1
- package/dist/components/draft-feedback-inline.js +10 -10
- package/dist/components/draft-feedback-inline.js.map +1 -1
- package/dist/components/email-recipient-field.js +107 -166
- package/dist/components/email-recipient-field.js.map +1 -1
- package/dist/components/related-record-action-card.d.ts +19 -0
- package/dist/components/related-record-action-card.js +150 -0
- package/dist/components/related-record-action-card.js.map +1 -0
- package/dist/components/score-feedback.js +6 -6
- package/dist/components/score-feedback.js.map +1 -1
- package/dist/components/suggested-actions.js +17 -5
- package/dist/components/suggested-actions.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/draft-feedback-inline.test.tsx +72 -0
- package/src/components/__tests__/email-recipient-field.test.tsx +116 -1
- package/src/components/__tests__/related-record-action-card.test.tsx +123 -0
- package/src/components/__tests__/suggested-actions-feedback-header.test.tsx +86 -0
- package/src/components/draft-feedback-inline.tsx +13 -13
- package/src/components/email-recipient-field.tsx +55 -101
- package/src/components/related-record-action-card.tsx +169 -0
- package/src/components/score-feedback.tsx +7 -7
- package/src/components/suggested-actions.tsx +19 -5
- package/src/index.ts +1 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
"use client";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { ExternalLink } from "lucide-react";
|
|
6
|
+
import { BRAND_ICONS } from "../lib/icons.js";
|
|
7
|
+
import { cn } from "../lib/utils.js";
|
|
8
|
+
function renderActionIcon(icon, kind) {
|
|
9
|
+
if (icon === "salesforce" || kind === "salesforce") {
|
|
10
|
+
return /* @__PURE__ */ jsx(
|
|
11
|
+
"img",
|
|
12
|
+
{
|
|
13
|
+
src: BRAND_ICONS.salesforce,
|
|
14
|
+
alt: "Salesforce",
|
|
15
|
+
className: "h-4 w-4 object-contain",
|
|
16
|
+
draggable: false
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
if (icon) {
|
|
21
|
+
return icon;
|
|
22
|
+
}
|
|
23
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: kind.slice(0, 1).toUpperCase() });
|
|
24
|
+
}
|
|
25
|
+
function RelatedRecordActionCard({
|
|
26
|
+
kind,
|
|
27
|
+
label,
|
|
28
|
+
subtitle,
|
|
29
|
+
disabledReason,
|
|
30
|
+
href,
|
|
31
|
+
external,
|
|
32
|
+
icon,
|
|
33
|
+
onClick,
|
|
34
|
+
className,
|
|
35
|
+
testId
|
|
36
|
+
}) {
|
|
37
|
+
const isDisabled = Boolean(disabledReason) || !href && !onClick;
|
|
38
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
39
|
+
/* @__PURE__ */ jsx(
|
|
40
|
+
"span",
|
|
41
|
+
{
|
|
42
|
+
"data-slot": "related-record-action-card-icon",
|
|
43
|
+
className: cn(
|
|
44
|
+
"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border text-xs font-semibold transition-colors",
|
|
45
|
+
isDisabled ? "border-border/60 bg-muted/40 text-muted-foreground" : "border-border bg-muted/30 text-muted-foreground group-hover:bg-muted/60 group-active:text-primary"
|
|
46
|
+
),
|
|
47
|
+
children: renderActionIcon(icon, kind)
|
|
48
|
+
}
|
|
49
|
+
),
|
|
50
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
51
|
+
/* @__PURE__ */ jsx(
|
|
52
|
+
"span",
|
|
53
|
+
{
|
|
54
|
+
"data-slot": "related-record-action-card-label",
|
|
55
|
+
className: cn(
|
|
56
|
+
"block truncate text-sm font-medium transition-colors",
|
|
57
|
+
isDisabled ? "text-muted-foreground" : "text-foreground group-active:text-primary"
|
|
58
|
+
),
|
|
59
|
+
children: label
|
|
60
|
+
}
|
|
61
|
+
),
|
|
62
|
+
subtitle && /* @__PURE__ */ jsx(
|
|
63
|
+
"span",
|
|
64
|
+
{
|
|
65
|
+
"data-slot": "related-record-action-card-subtitle",
|
|
66
|
+
className: "mt-0.5 block truncate text-xs text-muted-foreground",
|
|
67
|
+
children: subtitle
|
|
68
|
+
}
|
|
69
|
+
),
|
|
70
|
+
disabledReason && /* @__PURE__ */ jsx(
|
|
71
|
+
"span",
|
|
72
|
+
{
|
|
73
|
+
"data-slot": "related-record-action-card-disabled-reason",
|
|
74
|
+
className: "mt-1 block text-xs text-muted-foreground",
|
|
75
|
+
children: disabledReason
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
] }),
|
|
79
|
+
external && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
80
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "opens in a new tab" }),
|
|
81
|
+
/* @__PURE__ */ jsx(
|
|
82
|
+
ExternalLink,
|
|
83
|
+
{
|
|
84
|
+
"aria-hidden": "true",
|
|
85
|
+
"data-slot": "related-record-action-card-external-icon",
|
|
86
|
+
className: cn(
|
|
87
|
+
"h-3.5 w-3.5 shrink-0 text-muted-foreground transition-colors",
|
|
88
|
+
isDisabled ? "" : "group-active:text-primary"
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
] })
|
|
93
|
+
] });
|
|
94
|
+
if (isDisabled) {
|
|
95
|
+
return /* @__PURE__ */ jsx(
|
|
96
|
+
"div",
|
|
97
|
+
{
|
|
98
|
+
"aria-disabled": "true",
|
|
99
|
+
"data-kind": kind,
|
|
100
|
+
"data-slot": "related-record-action-card",
|
|
101
|
+
"data-testid": testId,
|
|
102
|
+
className: cn(
|
|
103
|
+
"group flex w-full items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors",
|
|
104
|
+
"cursor-not-allowed border-border/60 bg-muted/20 text-muted-foreground opacity-70",
|
|
105
|
+
className
|
|
106
|
+
),
|
|
107
|
+
children: content
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (href) {
|
|
112
|
+
return /* @__PURE__ */ jsx(
|
|
113
|
+
"a",
|
|
114
|
+
{
|
|
115
|
+
href,
|
|
116
|
+
target: external ? "_blank" : void 0,
|
|
117
|
+
rel: external ? "noopener noreferrer" : void 0,
|
|
118
|
+
"data-kind": kind,
|
|
119
|
+
"data-slot": "related-record-action-card",
|
|
120
|
+
"data-testid": testId,
|
|
121
|
+
className: cn(
|
|
122
|
+
"group flex w-full items-center gap-3 rounded-lg border border-border bg-background px-3 py-2 text-left transition-colors",
|
|
123
|
+
"cursor-pointer hover:bg-muted/50 hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:text-primary",
|
|
124
|
+
className
|
|
125
|
+
),
|
|
126
|
+
children: content
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return /* @__PURE__ */ jsx(
|
|
131
|
+
"button",
|
|
132
|
+
{
|
|
133
|
+
type: "button",
|
|
134
|
+
onClick,
|
|
135
|
+
"data-kind": kind,
|
|
136
|
+
"data-slot": "related-record-action-card",
|
|
137
|
+
"data-testid": testId,
|
|
138
|
+
className: cn(
|
|
139
|
+
"group flex w-full items-center gap-3 rounded-lg border border-border bg-background px-3 py-2 text-left transition-colors",
|
|
140
|
+
"cursor-pointer hover:bg-muted/50 hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:text-primary",
|
|
141
|
+
className
|
|
142
|
+
),
|
|
143
|
+
children: content
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
export {
|
|
148
|
+
RelatedRecordActionCard
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=related-record-action-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/related-record-action-card.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ExternalLink } from \"lucide-react\"\n\nimport { BRAND_ICONS } from \"../lib/icons\"\nimport { cn } from \"../lib/utils\"\n\nexport type RelatedRecordActionCardKind = \"case\" | \"account\" | \"opportunity\" | \"salesforce\" | \"generic\"\n\nexport type RelatedRecordActionIcon = \"salesforce\" | React.ReactNode\n\nexport interface RelatedRecordActionCardProps {\n kind: RelatedRecordActionCardKind\n label: string\n subtitle?: string\n disabledReason?: string\n href?: string\n external?: boolean\n icon?: RelatedRecordActionIcon\n onClick?: React.MouseEventHandler<HTMLButtonElement>\n className?: string\n testId?: string\n}\n\nfunction renderActionIcon(icon: RelatedRecordActionIcon | undefined, kind: RelatedRecordActionCardKind) {\n if (icon === \"salesforce\" || kind === \"salesforce\") {\n return (\n <img\n src={BRAND_ICONS.salesforce}\n alt=\"Salesforce\"\n className=\"h-4 w-4 object-contain\"\n draggable={false}\n />\n )\n }\n\n if (icon) {\n return icon\n }\n\n return <span aria-hidden=\"true\">{kind.slice(0, 1).toUpperCase()}</span>\n}\n\nexport function RelatedRecordActionCard({\n kind,\n label,\n subtitle,\n disabledReason,\n href,\n external,\n icon,\n onClick,\n className,\n testId,\n}: RelatedRecordActionCardProps) {\n const isDisabled = Boolean(disabledReason) || (!href && !onClick)\n\n const content = (\n <>\n <span\n data-slot=\"related-record-action-card-icon\"\n className={cn(\n \"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border text-xs font-semibold transition-colors\",\n isDisabled\n ? \"border-border/60 bg-muted/40 text-muted-foreground\"\n : \"border-border bg-muted/30 text-muted-foreground group-hover:bg-muted/60 group-active:text-primary\"\n )}\n >\n {renderActionIcon(icon, kind)}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span\n data-slot=\"related-record-action-card-label\"\n className={cn(\n \"block truncate text-sm font-medium transition-colors\",\n isDisabled ? \"text-muted-foreground\" : \"text-foreground group-active:text-primary\"\n )}\n >\n {label}\n </span>\n {subtitle && (\n <span\n data-slot=\"related-record-action-card-subtitle\"\n className=\"mt-0.5 block truncate text-xs text-muted-foreground\"\n >\n {subtitle}\n </span>\n )}\n {disabledReason && (\n <span\n data-slot=\"related-record-action-card-disabled-reason\"\n className=\"mt-1 block text-xs text-muted-foreground\"\n >\n {disabledReason}\n </span>\n )}\n </span>\n {external && (\n <>\n <span className=\"sr-only\">opens in a new tab</span>\n <ExternalLink\n aria-hidden=\"true\"\n data-slot=\"related-record-action-card-external-icon\"\n className={cn(\n \"h-3.5 w-3.5 shrink-0 text-muted-foreground transition-colors\",\n isDisabled ? \"\" : \"group-active:text-primary\"\n )}\n />\n </>\n )}\n </>\n )\n\n if (isDisabled) {\n return (\n <div\n aria-disabled=\"true\"\n data-kind={kind}\n data-slot=\"related-record-action-card\"\n data-testid={testId}\n className={cn(\n \"group flex w-full items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors\",\n \"cursor-not-allowed border-border/60 bg-muted/20 text-muted-foreground opacity-70\",\n className\n )}\n >\n {content}\n </div>\n )\n }\n\n if (href) {\n return (\n <a\n href={href}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n data-kind={kind}\n data-slot=\"related-record-action-card\"\n data-testid={testId}\n className={cn(\n \"group flex w-full items-center gap-3 rounded-lg border border-border bg-background px-3 py-2 text-left transition-colors\",\n \"cursor-pointer hover:bg-muted/50 hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:text-primary\",\n className\n )}\n >\n {content}\n </a>\n )\n }\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n data-kind={kind}\n data-slot=\"related-record-action-card\"\n data-testid={testId}\n className={cn(\n \"group flex w-full items-center gap-3 rounded-lg border border-border bg-background px-3 py-2 text-left transition-colors\",\n \"cursor-pointer hover:bg-muted/50 hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:text-primary\",\n className\n )}\n >\n {content}\n </button>\n )\n}\n"],"mappings":";AA4BM,SAuEE,UAvEF,KA2CA,YA3CA;AAzBN,SAAS,oBAAoB;AAE7B,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AAmBnB,SAAS,iBAAiB,MAA2C,MAAmC;AACtG,MAAI,SAAS,gBAAgB,SAAS,cAAc;AAClD,WACE;AAAA,MAAC;AAAA;AAAA,QACC,KAAK,YAAY;AAAA,QACjB,KAAI;AAAA,QACJ,WAAU;AAAA,QACV,WAAW;AAAA;AAAA,IACb;AAAA,EAEJ;AAEA,MAAI,MAAM;AACR,WAAO;AAAA,EACT;AAEA,SAAO,oBAAC,UAAK,eAAY,QAAQ,eAAK,MAAM,GAAG,CAAC,EAAE,YAAY,GAAE;AAClE;AAEO,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,aAAa,QAAQ,cAAc,KAAM,CAAC,QAAQ,CAAC;AAEzD,QAAM,UACJ,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,aAAU;AAAA,QACV,WAAW;AAAA,UACT;AAAA,UACA,aACI,uDACA;AAAA,QACN;AAAA,QAEC,2BAAiB,MAAM,IAAI;AAAA;AAAA,IAC9B;AAAA,IACA,qBAAC,UAAK,WAAU,kBACd;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,WAAW;AAAA,YACT;AAAA,YACA,aAAa,0BAA0B;AAAA,UACzC;AAAA,UAEC;AAAA;AAAA,MACH;AAAA,MACC,YACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAED,kBACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,OAEJ;AAAA,IACC,YACC,iCACE;AAAA,0BAAC,UAAK,WAAU,WAAU,gCAAkB;AAAA,MAC5C;AAAA,QAAC;AAAA;AAAA,UACC,eAAY;AAAA,UACZ,aAAU;AAAA,UACV,WAAW;AAAA,YACT;AAAA,YACA,aAAa,KAAK;AAAA,UACpB;AAAA;AAAA,MACF;AAAA,OACF;AAAA,KAEJ;AAGF,MAAI,YAAY;AACd,WACE;AAAA,MAAC;AAAA;AAAA,QACC,iBAAc;AAAA,QACd,aAAW;AAAA,QACX,aAAU;AAAA,QACV,eAAa;AAAA,QACb,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QAEC;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,MAAI,MAAM;AACR,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,QAAQ,WAAW,WAAW;AAAA,QAC9B,KAAK,WAAW,wBAAwB;AAAA,QACxC,aAAW;AAAA,QACX,aAAU;AAAA,QACV,eAAa;AAAA,QACb,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QAEC;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA,aAAW;AAAA,MACX,aAAU;AAAA,MACV,eAAa;AAAA,MACb,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
|
@@ -102,7 +102,7 @@ function Trigger({ className }) {
|
|
|
102
102
|
className
|
|
103
103
|
),
|
|
104
104
|
children: [
|
|
105
|
-
/* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-
|
|
105
|
+
/* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-foreground" }),
|
|
106
106
|
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: label })
|
|
107
107
|
]
|
|
108
108
|
}
|
|
@@ -116,9 +116,9 @@ function Trigger({ className }) {
|
|
|
116
116
|
onClick: () => handleThumbClick("up"),
|
|
117
117
|
className: cn(
|
|
118
118
|
"p-1.5 rounded transition-colors",
|
|
119
|
-
thumbState === "up" ? "bg-
|
|
119
|
+
thumbState === "up" ? "bg-muted text-foreground" : "hover:bg-muted text-muted-foreground hover:text-foreground"
|
|
120
120
|
),
|
|
121
|
-
children: /* @__PURE__ */ jsx(ThumbsUp, { className: "w-3.5 h-3.5"
|
|
121
|
+
children: /* @__PURE__ */ jsx(ThumbsUp, { className: "w-3.5 h-3.5" })
|
|
122
122
|
}
|
|
123
123
|
),
|
|
124
124
|
/* @__PURE__ */ jsx(
|
|
@@ -128,9 +128,9 @@ function Trigger({ className }) {
|
|
|
128
128
|
onClick: () => handleThumbClick("down"),
|
|
129
129
|
className: cn(
|
|
130
130
|
"p-1.5 rounded transition-colors",
|
|
131
|
-
thumbState === "down" ? "bg-
|
|
131
|
+
thumbState === "down" ? "bg-muted text-foreground" : "hover:bg-muted text-muted-foreground hover:text-foreground"
|
|
132
132
|
),
|
|
133
|
-
children: /* @__PURE__ */ jsx(ThumbsDown, { className: "w-3.5 h-3.5"
|
|
133
|
+
children: /* @__PURE__ */ jsx(ThumbsDown, { className: "w-3.5 h-3.5" })
|
|
134
134
|
}
|
|
135
135
|
)
|
|
136
136
|
] });
|
|
@@ -158,7 +158,7 @@ function Panel({ className }) {
|
|
|
158
158
|
onClick: () => togglePill(pill),
|
|
159
159
|
className: cn(
|
|
160
160
|
"px-2.5 py-1 rounded-full text-[11px] font-medium border transition-colors",
|
|
161
|
-
selectedPills.includes(pill) ? thumbState === "up" ? "bg-
|
|
161
|
+
selectedPills.includes(pill) ? thumbState === "up" ? "bg-muted text-foreground border-border" : "bg-red-50 text-red-700 border-red-200 dark:bg-red-950/30 dark:text-red-300 dark:border-red-800" : "bg-background text-muted-foreground border-border hover:bg-muted/50 hover:text-foreground"
|
|
162
162
|
),
|
|
163
163
|
children: pill
|
|
164
164
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/score-feedback.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ThumbsUp, ThumbsDown, Check } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst positivePills = [\n \"Right timing\",\n \"Accurate data\",\n \"Good prospect fit\",\n \"Actionable\",\n]\n\nconst negativePills = [\n \"Bad timing\",\n \"Inaccurate data\",\n \"Wrong prospect\",\n \"Already handled\",\n \"Not actionable\",\n \"Other\",\n]\n\ninterface SubmittedScoreFeedback {\n type: \"up\" | \"down\"\n pills: string[]\n detail: string\n}\n\ninterface ScoreFeedbackState {\n thumbState: \"up\" | \"down\" | null\n selectedPills: string[]\n detailText: string\n notedType: \"up\" | \"down\" | null\n submittedFeedback: SubmittedScoreFeedback | null\n otherSelected: boolean\n hasRequiredInput: boolean\n handleThumbClick: (type: \"up\" | \"down\") => void\n togglePill: (pill: string) => void\n setDetailText: (text: string) => void\n handleSubmit: () => void\n editSubmitted: () => void\n}\n\nconst ScoreFeedbackCtx = React.createContext<ScoreFeedbackState | null>(null)\n\nfunction useScoreFeedback() {\n const ctx = React.useContext(ScoreFeedbackCtx)\n if (!ctx) throw new Error(\"Must be used within ScoreFeedback.Root\")\n return ctx\n}\n\ninterface RootProps {\n children: React.ReactNode\n onSubmitFeedback?: (type: \"up\" | \"down\", pills: string[], detail: string) => void\n initialFeedback?: { type: \"up\" | \"down\"; pills: string[]; detail: string } | null\n}\n\nfunction Root({ children, onSubmitFeedback, initialFeedback }: RootProps) {\n const [thumbState, setThumbState] = React.useState<\"up\" | \"down\" | null>(null)\n const [selectedPills, setSelectedPills] = React.useState<string[]>([])\n const [detailText, setDetailTextState] = React.useState(\"\")\n const [notedType, setNotedType] = React.useState<\"up\" | \"down\" | null>(null)\n const [submittedFeedback, setSubmittedFeedback] = React.useState<SubmittedScoreFeedback | null>(\n initialFeedback ?? null\n )\n\n // Sync submitted feedback when initialFeedback prop changes (e.g. async\n // detail load). Skip when the user has an in-progress edit (thumbState set).\n React.useEffect(() => {\n if (thumbState !== null) return\n setSubmittedFeedback(initialFeedback ?? null)\n }, [initialFeedback]) // eslint-disable-line react-hooks/exhaustive-deps -- intentionally omits thumbState to read it as a guard, not a trigger\n\n const otherSelected = selectedPills.includes(\"Other\")\n\n const hasRequiredInput =\n thumbState === \"down\"\n ? selectedPills.length > 0 && (!otherSelected || detailText.trim().length > 0)\n : selectedPills.length > 0 || detailText.trim().length > 0\n\n const togglePill = React.useCallback((pill: string) => {\n setSelectedPills((prev) =>\n prev.includes(pill) ? prev.filter((p) => p !== pill) : [...prev, pill]\n )\n }, [])\n\n const handleThumbClick = React.useCallback((type: \"up\" | \"down\") => {\n setThumbState((prev) => (prev === type ? null : type))\n setSelectedPills([])\n setDetailTextState(\"\")\n }, [])\n\n const handleSubmit = React.useCallback(() => {\n if (!thumbState) return\n onSubmitFeedback?.(thumbState, selectedPills, detailText)\n setSubmittedFeedback({ type: thumbState, pills: [...selectedPills], detail: detailText.trim() })\n setNotedType(thumbState)\n setThumbState(null)\n setSelectedPills([])\n setDetailTextState(\"\")\n setTimeout(() => setNotedType(null), 3000)\n }, [thumbState, selectedPills, detailText, onSubmitFeedback])\n\n const editSubmitted = React.useCallback(() => {\n if (!submittedFeedback) return\n setThumbState(submittedFeedback.type)\n setSelectedPills([...submittedFeedback.pills])\n setDetailTextState(submittedFeedback.detail)\n setNotedType(null)\n }, [submittedFeedback])\n\n return (\n <ScoreFeedbackCtx.Provider\n value={{\n thumbState,\n selectedPills,\n detailText,\n notedType,\n submittedFeedback,\n otherSelected,\n hasRequiredInput,\n handleThumbClick,\n togglePill,\n setDetailText: setDetailTextState,\n handleSubmit,\n editSubmitted,\n }}\n >\n {children}\n </ScoreFeedbackCtx.Provider>\n )\n}\n\nfunction Trigger({ className }: { className?: string }) {\n const { thumbState, notedType, submittedFeedback, handleThumbClick, editSubmitted } = useScoreFeedback()\n\n if (notedType || (submittedFeedback && !thumbState)) {\n const label = notedType\n ? notedType === \"up\" ? \"Noted\" : \"Recorded\"\n : submittedFeedback?.type === \"up\" ? \"Noted\" : \"Recorded\"\n\n return (\n <button\n type=\"button\"\n onClick={submittedFeedback ? editSubmitted : undefined}\n className={cn(\n \"flex items-center gap-1 shrink-0 rounded px-1.5 py-1 transition-colors\",\n submittedFeedback ? \"cursor-pointer hover:bg-muted/50\" : \"cursor-default\",\n className,\n )}\n >\n <Check className=\"w-3 h-3 text-emerald-500\" />\n <span className=\"text-[11px] text-muted-foreground\">{label}</span>\n </button>\n )\n }\n\n return (\n <div className={cn(\"flex gap-0.5 shrink-0\", className)}>\n <button\n type=\"button\"\n onClick={() => handleThumbClick(\"up\")}\n className={cn(\n \"p-1.5 rounded transition-colors\",\n thumbState === \"up\"\n ? \"bg-emerald-100 text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400\"\n : \"hover:bg-muted text-muted-foreground hover:text-foreground\"\n )}\n >\n <ThumbsUp className=\"w-3.5 h-3.5\" fill={thumbState === \"up\" ? \"currentColor\" : \"none\"} />\n </button>\n <button\n type=\"button\"\n onClick={() => handleThumbClick(\"down\")}\n className={cn(\n \"p-1.5 rounded transition-colors\",\n thumbState === \"down\"\n ? \"bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400\"\n : \"hover:bg-muted text-muted-foreground hover:text-foreground\"\n )}\n >\n <ThumbsDown className=\"w-3.5 h-3.5\" fill={thumbState === \"down\" ? \"currentColor\" : \"none\"} />\n </button>\n </div>\n )\n}\n\nfunction Panel({ className }: { className?: string }) {\n const {\n thumbState,\n selectedPills,\n detailText,\n otherSelected,\n hasRequiredInput,\n togglePill,\n setDetailText,\n handleSubmit,\n } = useScoreFeedback()\n\n if (!thumbState) return null\n\n return (\n <div className={cn(\"overflow-hidden\", className)}>\n <div className=\"mt-4 pt-4 pb-1 space-y-3 border-t border-border/60\">\n <span className=\"text-[11px] font-bold text-muted-foreground/70 uppercase tracking-wider\">\n How's this score?\n </span>\n <div>\n <span className=\"text-xs text-muted-foreground mb-2 block font-medium\">\n {thumbState === \"up\" ? \"What was useful?\" : \"What\\u2019s the issue?\"}\n </span>\n <div className=\"flex flex-wrap gap-1.5\">\n {(thumbState === \"up\" ? positivePills : negativePills).map((pill) => (\n <button\n key={pill}\n type=\"button\"\n onClick={() => togglePill(pill)}\n className={cn(\n \"px-2.5 py-1 rounded-full text-[11px] font-medium border transition-colors\",\n selectedPills.includes(pill)\n ? thumbState === \"up\"\n ? \"bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-300 dark:border-emerald-800\"\n : \"bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-300 dark:border-red-800\"\n : \"bg-background text-muted-foreground border-border hover:bg-muted/50 hover:text-foreground\"\n )}\n >\n {pill}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <input\n type=\"text\"\n value={detailText}\n onChange={(e) => setDetailText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && hasRequiredInput) handleSubmit()\n }}\n placeholder={\n thumbState === \"up\"\n ? \"Tell us more (optional)\"\n : \"e.g., The risk factors are outdated\"\n }\n className=\"w-full text-xs bg-background border border-border rounded-md px-2.5 py-2 text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n {otherSelected && detailText.trim().length === 0 && (\n <span className=\"text-[10px] text-red-500\">\n Please describe when “Other” is selected\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 pt-1\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!hasRequiredInput}\n className={cn(\n \"flex-1 py-1.5 rounded-md text-xs font-semibold transition-colors\",\n hasRequiredInput\n ? \"bg-foreground text-background hover:bg-foreground/90\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\"\n )}\n >\n Submit\n </button>\n </div>\n </div>\n </div>\n )\n}\n\nexport const ScoreFeedback = { Root, Trigger, Panel }\nexport { useScoreFeedback }\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/score-feedback.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ThumbsUp, ThumbsDown, Check } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst positivePills = [\n \"Right timing\",\n \"Accurate data\",\n \"Good prospect fit\",\n \"Actionable\",\n]\n\nconst negativePills = [\n \"Bad timing\",\n \"Inaccurate data\",\n \"Wrong prospect\",\n \"Already handled\",\n \"Not actionable\",\n \"Other\",\n]\n\ninterface SubmittedScoreFeedback {\n type: \"up\" | \"down\"\n pills: string[]\n detail: string\n}\n\ninterface ScoreFeedbackState {\n thumbState: \"up\" | \"down\" | null\n selectedPills: string[]\n detailText: string\n notedType: \"up\" | \"down\" | null\n submittedFeedback: SubmittedScoreFeedback | null\n otherSelected: boolean\n hasRequiredInput: boolean\n handleThumbClick: (type: \"up\" | \"down\") => void\n togglePill: (pill: string) => void\n setDetailText: (text: string) => void\n handleSubmit: () => void\n editSubmitted: () => void\n}\n\nconst ScoreFeedbackCtx = React.createContext<ScoreFeedbackState | null>(null)\n\nfunction useScoreFeedback() {\n const ctx = React.useContext(ScoreFeedbackCtx)\n if (!ctx) throw new Error(\"Must be used within ScoreFeedback.Root\")\n return ctx\n}\n\ninterface RootProps {\n children: React.ReactNode\n onSubmitFeedback?: (type: \"up\" | \"down\", pills: string[], detail: string) => void\n initialFeedback?: { type: \"up\" | \"down\"; pills: string[]; detail: string } | null\n}\n\nfunction Root({ children, onSubmitFeedback, initialFeedback }: RootProps) {\n const [thumbState, setThumbState] = React.useState<\"up\" | \"down\" | null>(null)\n const [selectedPills, setSelectedPills] = React.useState<string[]>([])\n const [detailText, setDetailTextState] = React.useState(\"\")\n const [notedType, setNotedType] = React.useState<\"up\" | \"down\" | null>(null)\n const [submittedFeedback, setSubmittedFeedback] = React.useState<SubmittedScoreFeedback | null>(\n initialFeedback ?? null\n )\n\n // Sync submitted feedback when initialFeedback prop changes (e.g. async\n // detail load). Skip when the user has an in-progress edit (thumbState set).\n React.useEffect(() => {\n if (thumbState !== null) return\n setSubmittedFeedback(initialFeedback ?? null)\n }, [initialFeedback]) // eslint-disable-line react-hooks/exhaustive-deps -- intentionally omits thumbState to read it as a guard, not a trigger\n\n const otherSelected = selectedPills.includes(\"Other\")\n\n const hasRequiredInput =\n thumbState === \"down\"\n ? selectedPills.length > 0 && (!otherSelected || detailText.trim().length > 0)\n : selectedPills.length > 0 || detailText.trim().length > 0\n\n const togglePill = React.useCallback((pill: string) => {\n setSelectedPills((prev) =>\n prev.includes(pill) ? prev.filter((p) => p !== pill) : [...prev, pill]\n )\n }, [])\n\n const handleThumbClick = React.useCallback((type: \"up\" | \"down\") => {\n setThumbState((prev) => (prev === type ? null : type))\n setSelectedPills([])\n setDetailTextState(\"\")\n }, [])\n\n const handleSubmit = React.useCallback(() => {\n if (!thumbState) return\n onSubmitFeedback?.(thumbState, selectedPills, detailText)\n setSubmittedFeedback({ type: thumbState, pills: [...selectedPills], detail: detailText.trim() })\n setNotedType(thumbState)\n setThumbState(null)\n setSelectedPills([])\n setDetailTextState(\"\")\n setTimeout(() => setNotedType(null), 3000)\n }, [thumbState, selectedPills, detailText, onSubmitFeedback])\n\n const editSubmitted = React.useCallback(() => {\n if (!submittedFeedback) return\n setThumbState(submittedFeedback.type)\n setSelectedPills([...submittedFeedback.pills])\n setDetailTextState(submittedFeedback.detail)\n setNotedType(null)\n }, [submittedFeedback])\n\n return (\n <ScoreFeedbackCtx.Provider\n value={{\n thumbState,\n selectedPills,\n detailText,\n notedType,\n submittedFeedback,\n otherSelected,\n hasRequiredInput,\n handleThumbClick,\n togglePill,\n setDetailText: setDetailTextState,\n handleSubmit,\n editSubmitted,\n }}\n >\n {children}\n </ScoreFeedbackCtx.Provider>\n )\n}\n\nfunction Trigger({ className }: { className?: string }) {\n const { thumbState, notedType, submittedFeedback, handleThumbClick, editSubmitted } = useScoreFeedback()\n\n if (notedType || (submittedFeedback && !thumbState)) {\n const label = notedType\n ? notedType === \"up\" ? \"Noted\" : \"Recorded\"\n : submittedFeedback?.type === \"up\" ? \"Noted\" : \"Recorded\"\n\n return (\n <button\n type=\"button\"\n onClick={submittedFeedback ? editSubmitted : undefined}\n className={cn(\n \"flex items-center gap-1 shrink-0 rounded px-1.5 py-1 transition-colors\",\n submittedFeedback ? \"cursor-pointer hover:bg-muted/50\" : \"cursor-default\",\n className,\n )}\n >\n <Check className=\"w-3 h-3 text-foreground\" />\n <span className=\"text-[11px] text-muted-foreground\">{label}</span>\n </button>\n )\n }\n\n return (\n <div className={cn(\"flex gap-0.5 shrink-0\", className)}>\n <button\n type=\"button\"\n onClick={() => handleThumbClick(\"up\")}\n className={cn(\n \"p-1.5 rounded transition-colors\",\n thumbState === \"up\"\n ? \"bg-muted text-foreground\"\n : \"hover:bg-muted text-muted-foreground hover:text-foreground\"\n )}\n >\n <ThumbsUp className=\"w-3.5 h-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleThumbClick(\"down\")}\n className={cn(\n \"p-1.5 rounded transition-colors\",\n thumbState === \"down\"\n ? \"bg-muted text-foreground\"\n : \"hover:bg-muted text-muted-foreground hover:text-foreground\"\n )}\n >\n <ThumbsDown className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n )\n}\n\nfunction Panel({ className }: { className?: string }) {\n const {\n thumbState,\n selectedPills,\n detailText,\n otherSelected,\n hasRequiredInput,\n togglePill,\n setDetailText,\n handleSubmit,\n } = useScoreFeedback()\n\n if (!thumbState) return null\n\n return (\n <div className={cn(\"overflow-hidden\", className)}>\n <div className=\"mt-4 pt-4 pb-1 space-y-3 border-t border-border/60\">\n <span className=\"text-[11px] font-bold text-muted-foreground/70 uppercase tracking-wider\">\n How's this score?\n </span>\n <div>\n <span className=\"text-xs text-muted-foreground mb-2 block font-medium\">\n {thumbState === \"up\" ? \"What was useful?\" : \"What\\u2019s the issue?\"}\n </span>\n <div className=\"flex flex-wrap gap-1.5\">\n {(thumbState === \"up\" ? positivePills : negativePills).map((pill) => (\n <button\n key={pill}\n type=\"button\"\n onClick={() => togglePill(pill)}\n className={cn(\n \"px-2.5 py-1 rounded-full text-[11px] font-medium border transition-colors\",\n selectedPills.includes(pill)\n ? thumbState === \"up\"\n ? \"bg-muted text-foreground border-border\"\n : \"bg-red-50 text-red-700 border-red-200 dark:bg-red-950/30 dark:text-red-300 dark:border-red-800\"\n : \"bg-background text-muted-foreground border-border hover:bg-muted/50 hover:text-foreground\"\n )}\n >\n {pill}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <input\n type=\"text\"\n value={detailText}\n onChange={(e) => setDetailText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && hasRequiredInput) handleSubmit()\n }}\n placeholder={\n thumbState === \"up\"\n ? \"Tell us more (optional)\"\n : \"e.g., The risk factors are outdated\"\n }\n className=\"w-full text-xs bg-background border border-border rounded-md px-2.5 py-2 text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n {otherSelected && detailText.trim().length === 0 && (\n <span className=\"text-[10px] text-red-500\">\n Please describe when “Other” is selected\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 pt-1\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!hasRequiredInput}\n className={cn(\n \"flex-1 py-1.5 rounded-md text-xs font-semibold transition-colors\",\n hasRequiredInput\n ? \"bg-foreground text-background hover:bg-foreground/90\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\"\n )}\n >\n Submit\n </button>\n </div>\n </div>\n </div>\n )\n}\n\nexport const ScoreFeedback = { Root, Trigger, Panel }\nexport { useScoreFeedback }\n"],"mappings":"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","names":[]}
|
|
@@ -652,6 +652,16 @@ function SuggestedActionCard({
|
|
|
652
652
|
);
|
|
653
653
|
const [showAiEdit, setShowAiEdit] = React.useState(false);
|
|
654
654
|
const [feedbackOpen, setFeedbackOpen] = React.useState(false);
|
|
655
|
+
const [feedbackDirection, setFeedbackDirection] = React.useState(null);
|
|
656
|
+
const handleThumbClick = (dir) => {
|
|
657
|
+
if (feedbackOpen && feedbackDirection === dir) {
|
|
658
|
+
setFeedbackOpen(false);
|
|
659
|
+
setFeedbackDirection(null);
|
|
660
|
+
} else {
|
|
661
|
+
setFeedbackDirection(dir);
|
|
662
|
+
setFeedbackOpen(true);
|
|
663
|
+
}
|
|
664
|
+
};
|
|
655
665
|
const [followUpEnabled, setFollowUpEnabled] = React.useState((_f = (_e = action.followUp) == null ? void 0 : _e.enabled) != null ? _f : false);
|
|
656
666
|
const [threadExpanded, setThreadExpanded] = React.useState(false);
|
|
657
667
|
const [expandedMessageId, setExpandedMessageId] = React.useState(null);
|
|
@@ -726,16 +736,16 @@ function SuggestedActionCard({
|
|
|
726
736
|
/* @__PURE__ */ jsx(
|
|
727
737
|
"button",
|
|
728
738
|
{
|
|
729
|
-
onClick: () =>
|
|
730
|
-
className: `p-1.5 rounded transition-colors ${feedbackOpen
|
|
739
|
+
onClick: () => handleThumbClick("up"),
|
|
740
|
+
className: `p-1.5 rounded transition-colors ${feedbackOpen && feedbackDirection === "up" ? "bg-muted text-foreground" : "hover:bg-muted text-muted-foreground hover:text-foreground"}`,
|
|
731
741
|
children: /* @__PURE__ */ jsx(ThumbsUp, { className: "w-3.5 h-3.5" })
|
|
732
742
|
}
|
|
733
743
|
),
|
|
734
744
|
/* @__PURE__ */ jsx(
|
|
735
745
|
"button",
|
|
736
746
|
{
|
|
737
|
-
onClick: () =>
|
|
738
|
-
className:
|
|
747
|
+
onClick: () => handleThumbClick("down"),
|
|
748
|
+
className: `p-1.5 rounded transition-colors ${feedbackOpen && feedbackDirection === "down" ? "bg-muted text-foreground" : "hover:bg-muted text-muted-foreground hover:text-foreground"}`,
|
|
739
749
|
children: /* @__PURE__ */ jsx(ThumbsDown, { className: "w-3.5 h-3.5" })
|
|
740
750
|
}
|
|
741
751
|
),
|
|
@@ -755,6 +765,7 @@ function SuggestedActionCard({
|
|
|
755
765
|
feedbackOpen && /* @__PURE__ */ jsx("div", { className: "px-5 py-3 border-b border-border/40 animate-in fade-in slide-in-from-top-2 duration-200", children: /* @__PURE__ */ jsx(
|
|
756
766
|
DraftFeedbackInline,
|
|
757
767
|
{
|
|
768
|
+
initialDirection: feedbackDirection,
|
|
758
769
|
onRegenerateRequest: (pills, detail) => {
|
|
759
770
|
onFeedback == null ? void 0 : onFeedback("down", pills, detail);
|
|
760
771
|
},
|
|
@@ -765,7 +776,8 @@ function SuggestedActionCard({
|
|
|
765
776
|
onFeedback == null ? void 0 : onFeedback("down", pills, detail);
|
|
766
777
|
onDismiss == null ? void 0 : onDismiss(action.id);
|
|
767
778
|
}
|
|
768
|
-
}
|
|
779
|
+
},
|
|
780
|
+
`feedback-${feedbackDirection}`
|
|
769
781
|
) }),
|
|
770
782
|
isThreadReply && action.replyTo && /* @__PURE__ */ jsxs("div", { className: "border-b border-border/40", children: [
|
|
771
783
|
action.threadMessages && action.threadMessages.length > 1 && /* @__PURE__ */ jsx("div", { className: "px-5 py-2 border-b border-border/40", children: /* @__PURE__ */ jsxs(
|