@handled-ai/design-system 0.18.37 → 0.18.39
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/badge.d.ts +1 -1
- package/dist/components/button.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 +3 -0
- package/dist/components/email-recipient-field.js.map +1 -1
- package/dist/components/pill.d.ts +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/components/tabs.d.ts +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -5
- package/src/components/__tests__/draft-feedback-inline.test.tsx +72 -0
- 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 +5 -0
- 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 +5 -4
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { ExternalLink } from "lucide-react"
|
|
5
|
+
|
|
6
|
+
import { BRAND_ICONS } from "../lib/icons"
|
|
7
|
+
import { cn } from "../lib/utils"
|
|
8
|
+
|
|
9
|
+
export type RelatedRecordActionCardKind = "case" | "account" | "opportunity" | "salesforce" | "generic"
|
|
10
|
+
|
|
11
|
+
export type RelatedRecordActionIcon = "salesforce" | React.ReactNode
|
|
12
|
+
|
|
13
|
+
export interface RelatedRecordActionCardProps {
|
|
14
|
+
kind: RelatedRecordActionCardKind
|
|
15
|
+
label: string
|
|
16
|
+
subtitle?: string
|
|
17
|
+
disabledReason?: string
|
|
18
|
+
href?: string
|
|
19
|
+
external?: boolean
|
|
20
|
+
icon?: RelatedRecordActionIcon
|
|
21
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>
|
|
22
|
+
className?: string
|
|
23
|
+
testId?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderActionIcon(icon: RelatedRecordActionIcon | undefined, kind: RelatedRecordActionCardKind) {
|
|
27
|
+
if (icon === "salesforce" || kind === "salesforce") {
|
|
28
|
+
return (
|
|
29
|
+
<img
|
|
30
|
+
src={BRAND_ICONS.salesforce}
|
|
31
|
+
alt="Salesforce"
|
|
32
|
+
className="h-4 w-4 object-contain"
|
|
33
|
+
draggable={false}
|
|
34
|
+
/>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (icon) {
|
|
39
|
+
return icon
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return <span aria-hidden="true">{kind.slice(0, 1).toUpperCase()}</span>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function RelatedRecordActionCard({
|
|
46
|
+
kind,
|
|
47
|
+
label,
|
|
48
|
+
subtitle,
|
|
49
|
+
disabledReason,
|
|
50
|
+
href,
|
|
51
|
+
external,
|
|
52
|
+
icon,
|
|
53
|
+
onClick,
|
|
54
|
+
className,
|
|
55
|
+
testId,
|
|
56
|
+
}: RelatedRecordActionCardProps) {
|
|
57
|
+
const isDisabled = Boolean(disabledReason) || (!href && !onClick)
|
|
58
|
+
|
|
59
|
+
const content = (
|
|
60
|
+
<>
|
|
61
|
+
<span
|
|
62
|
+
data-slot="related-record-action-card-icon"
|
|
63
|
+
className={cn(
|
|
64
|
+
"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border text-xs font-semibold transition-colors",
|
|
65
|
+
isDisabled
|
|
66
|
+
? "border-border/60 bg-muted/40 text-muted-foreground"
|
|
67
|
+
: "border-border bg-muted/30 text-muted-foreground group-hover:bg-muted/60 group-active:text-primary"
|
|
68
|
+
)}
|
|
69
|
+
>
|
|
70
|
+
{renderActionIcon(icon, kind)}
|
|
71
|
+
</span>
|
|
72
|
+
<span className="min-w-0 flex-1">
|
|
73
|
+
<span
|
|
74
|
+
data-slot="related-record-action-card-label"
|
|
75
|
+
className={cn(
|
|
76
|
+
"block truncate text-sm font-medium transition-colors",
|
|
77
|
+
isDisabled ? "text-muted-foreground" : "text-foreground group-active:text-primary"
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
{label}
|
|
81
|
+
</span>
|
|
82
|
+
{subtitle && (
|
|
83
|
+
<span
|
|
84
|
+
data-slot="related-record-action-card-subtitle"
|
|
85
|
+
className="mt-0.5 block truncate text-xs text-muted-foreground"
|
|
86
|
+
>
|
|
87
|
+
{subtitle}
|
|
88
|
+
</span>
|
|
89
|
+
)}
|
|
90
|
+
{disabledReason && (
|
|
91
|
+
<span
|
|
92
|
+
data-slot="related-record-action-card-disabled-reason"
|
|
93
|
+
className="mt-1 block text-xs text-muted-foreground"
|
|
94
|
+
>
|
|
95
|
+
{disabledReason}
|
|
96
|
+
</span>
|
|
97
|
+
)}
|
|
98
|
+
</span>
|
|
99
|
+
{external && (
|
|
100
|
+
<>
|
|
101
|
+
<span className="sr-only">opens in a new tab</span>
|
|
102
|
+
<ExternalLink
|
|
103
|
+
aria-hidden="true"
|
|
104
|
+
data-slot="related-record-action-card-external-icon"
|
|
105
|
+
className={cn(
|
|
106
|
+
"h-3.5 w-3.5 shrink-0 text-muted-foreground transition-colors",
|
|
107
|
+
isDisabled ? "" : "group-active:text-primary"
|
|
108
|
+
)}
|
|
109
|
+
/>
|
|
110
|
+
</>
|
|
111
|
+
)}
|
|
112
|
+
</>
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if (isDisabled) {
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
aria-disabled="true"
|
|
119
|
+
data-kind={kind}
|
|
120
|
+
data-slot="related-record-action-card"
|
|
121
|
+
data-testid={testId}
|
|
122
|
+
className={cn(
|
|
123
|
+
"group flex w-full items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors",
|
|
124
|
+
"cursor-not-allowed border-border/60 bg-muted/20 text-muted-foreground opacity-70",
|
|
125
|
+
className
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
{content}
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (href) {
|
|
134
|
+
return (
|
|
135
|
+
<a
|
|
136
|
+
href={href}
|
|
137
|
+
target={external ? "_blank" : undefined}
|
|
138
|
+
rel={external ? "noopener noreferrer" : undefined}
|
|
139
|
+
data-kind={kind}
|
|
140
|
+
data-slot="related-record-action-card"
|
|
141
|
+
data-testid={testId}
|
|
142
|
+
className={cn(
|
|
143
|
+
"group flex w-full items-center gap-3 rounded-lg border border-border bg-background px-3 py-2 text-left transition-colors",
|
|
144
|
+
"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",
|
|
145
|
+
className
|
|
146
|
+
)}
|
|
147
|
+
>
|
|
148
|
+
{content}
|
|
149
|
+
</a>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
onClick={onClick}
|
|
157
|
+
data-kind={kind}
|
|
158
|
+
data-slot="related-record-action-card"
|
|
159
|
+
data-testid={testId}
|
|
160
|
+
className={cn(
|
|
161
|
+
"group flex w-full items-center gap-3 rounded-lg border border-border bg-background px-3 py-2 text-left transition-colors",
|
|
162
|
+
"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",
|
|
163
|
+
className
|
|
164
|
+
)}
|
|
165
|
+
>
|
|
166
|
+
{content}
|
|
167
|
+
</button>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
@@ -149,7 +149,7 @@ function Trigger({ className }: { className?: string }) {
|
|
|
149
149
|
className,
|
|
150
150
|
)}
|
|
151
151
|
>
|
|
152
|
-
<Check className="w-3 h-3 text-
|
|
152
|
+
<Check className="w-3 h-3 text-foreground" />
|
|
153
153
|
<span className="text-[11px] text-muted-foreground">{label}</span>
|
|
154
154
|
</button>
|
|
155
155
|
)
|
|
@@ -163,11 +163,11 @@ function Trigger({ className }: { className?: string }) {
|
|
|
163
163
|
className={cn(
|
|
164
164
|
"p-1.5 rounded transition-colors",
|
|
165
165
|
thumbState === "up"
|
|
166
|
-
? "bg-
|
|
166
|
+
? "bg-muted text-foreground"
|
|
167
167
|
: "hover:bg-muted text-muted-foreground hover:text-foreground"
|
|
168
168
|
)}
|
|
169
169
|
>
|
|
170
|
-
<ThumbsUp className="w-3.5 h-3.5"
|
|
170
|
+
<ThumbsUp className="w-3.5 h-3.5" />
|
|
171
171
|
</button>
|
|
172
172
|
<button
|
|
173
173
|
type="button"
|
|
@@ -175,11 +175,11 @@ function Trigger({ className }: { className?: string }) {
|
|
|
175
175
|
className={cn(
|
|
176
176
|
"p-1.5 rounded transition-colors",
|
|
177
177
|
thumbState === "down"
|
|
178
|
-
? "bg-
|
|
178
|
+
? "bg-muted text-foreground"
|
|
179
179
|
: "hover:bg-muted text-muted-foreground hover:text-foreground"
|
|
180
180
|
)}
|
|
181
181
|
>
|
|
182
|
-
<ThumbsDown className="w-3.5 h-3.5"
|
|
182
|
+
<ThumbsDown className="w-3.5 h-3.5" />
|
|
183
183
|
</button>
|
|
184
184
|
</div>
|
|
185
185
|
)
|
|
@@ -219,8 +219,8 @@ function Panel({ className }: { className?: string }) {
|
|
|
219
219
|
"px-2.5 py-1 rounded-full text-[11px] font-medium border transition-colors",
|
|
220
220
|
selectedPills.includes(pill)
|
|
221
221
|
? thumbState === "up"
|
|
222
|
-
? "bg-
|
|
223
|
-
: "bg-red-
|
|
222
|
+
? "bg-muted text-foreground border-border"
|
|
223
|
+
: "bg-red-50 text-red-700 border-red-200 dark:bg-red-950/30 dark:text-red-300 dark:border-red-800"
|
|
224
224
|
: "bg-background text-muted-foreground border-border hover:bg-muted/50 hover:text-foreground"
|
|
225
225
|
)}
|
|
226
226
|
>
|
|
@@ -925,6 +925,14 @@ function SuggestedActionCard({
|
|
|
925
925
|
)
|
|
926
926
|
const [showAiEdit, setShowAiEdit] = React.useState(false)
|
|
927
927
|
const [feedbackOpen, setFeedbackOpen] = React.useState(false)
|
|
928
|
+
const [feedbackDirection, setFeedbackDirection] = React.useState<"up" | "down" | null>(null)
|
|
929
|
+
const handleThumbClick = (dir: "up" | "down") => {
|
|
930
|
+
if (feedbackOpen && feedbackDirection === dir) {
|
|
931
|
+
setFeedbackOpen(false); setFeedbackDirection(null)
|
|
932
|
+
} else {
|
|
933
|
+
setFeedbackDirection(dir); setFeedbackOpen(true)
|
|
934
|
+
}
|
|
935
|
+
}
|
|
928
936
|
const [followUpEnabled, setFollowUpEnabled] = React.useState(action.followUp?.enabled ?? false)
|
|
929
937
|
const [threadExpanded, setThreadExpanded] = React.useState(false)
|
|
930
938
|
const [expandedMessageId, setExpandedMessageId] = React.useState<string | null>(null)
|
|
@@ -1016,18 +1024,22 @@ function SuggestedActionCard({
|
|
|
1016
1024
|
</div>
|
|
1017
1025
|
<div className="flex items-center gap-1.5">
|
|
1018
1026
|
<button
|
|
1019
|
-
onClick={() =>
|
|
1027
|
+
onClick={() => handleThumbClick("up")}
|
|
1020
1028
|
className={`p-1.5 rounded transition-colors ${
|
|
1021
|
-
feedbackOpen
|
|
1022
|
-
? "bg-
|
|
1029
|
+
feedbackOpen && feedbackDirection === "up"
|
|
1030
|
+
? "bg-muted text-foreground"
|
|
1023
1031
|
: "hover:bg-muted text-muted-foreground hover:text-foreground"
|
|
1024
1032
|
}`}
|
|
1025
1033
|
>
|
|
1026
1034
|
<ThumbsUp className="w-3.5 h-3.5" />
|
|
1027
1035
|
</button>
|
|
1028
1036
|
<button
|
|
1029
|
-
onClick={() =>
|
|
1030
|
-
className=
|
|
1037
|
+
onClick={() => handleThumbClick("down")}
|
|
1038
|
+
className={`p-1.5 rounded transition-colors ${
|
|
1039
|
+
feedbackOpen && feedbackDirection === "down"
|
|
1040
|
+
? "bg-muted text-foreground"
|
|
1041
|
+
: "hover:bg-muted text-muted-foreground hover:text-foreground"
|
|
1042
|
+
}`}
|
|
1031
1043
|
>
|
|
1032
1044
|
<ThumbsDown className="w-3.5 h-3.5" />
|
|
1033
1045
|
</button>
|
|
@@ -1047,6 +1059,8 @@ function SuggestedActionCard({
|
|
|
1047
1059
|
{feedbackOpen && (
|
|
1048
1060
|
<div className="px-5 py-3 border-b border-border/40 animate-in fade-in slide-in-from-top-2 duration-200">
|
|
1049
1061
|
<DraftFeedbackInline
|
|
1062
|
+
key={`feedback-${feedbackDirection}`}
|
|
1063
|
+
initialDirection={feedbackDirection}
|
|
1050
1064
|
onRegenerateRequest={(pills, detail) => {
|
|
1051
1065
|
onFeedback?.("down", pills, detail)
|
|
1052
1066
|
}}
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,10 @@ export * from "./components/badge"
|
|
|
21
21
|
export * from "./components/button"
|
|
22
22
|
export * from "./components/card"
|
|
23
23
|
export * from "./components/case-panel-email-composer"
|
|
24
|
+
export * from "./components/email-composer-row"
|
|
25
|
+
export * from "./components/email-preview-card"
|
|
26
|
+
export * from "./components/email-recipient-field"
|
|
27
|
+
export * from "./components/email-send-bar"
|
|
24
28
|
export * from "./components/case-panel-activity-timeline"
|
|
25
29
|
export * from "./components/case-panel-detail"
|
|
26
30
|
export * from "./components/case-panel-why"
|
|
@@ -40,12 +44,9 @@ export * from "./components/detail-view"
|
|
|
40
44
|
export * from "./components/detail-drawer"
|
|
41
45
|
export * from "./components/dialog"
|
|
42
46
|
export * from "./components/dropdown-menu"
|
|
43
|
-
export * from "./components/email-composer-row"
|
|
44
|
-
export * from "./components/email-preview-card"
|
|
45
|
-
export * from "./components/email-recipient-field"
|
|
46
|
-
export * from "./components/email-send-bar"
|
|
47
47
|
export * from "./components/empty-state"
|
|
48
48
|
export * from "./components/entity-panel"
|
|
49
|
+
export * from "./components/related-record-action-card"
|
|
49
50
|
export { FeedbackFooter, FeedbackChipGroup, FeedbackInput, FeedbackActions, InlineFeedbackControl } from "./components/feedback-primitives"
|
|
50
51
|
export type { FeedbackFooterProps, FeedbackChipTree, FeedbackChipGroupProps, FeedbackInputProps, FeedbackActionsProps, FeedbackSubmitData, PersistedFeedbackData, InlineFeedbackControlProps } from "./components/feedback-primitives"
|
|
51
52
|
export { SignalPriorityPopover } from "./components/signal-priority-popover"
|