@dilipod/ui 0.4.24 → 0.4.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/support-chat.d.ts +28 -0
- package/dist/components/support-chat.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +154 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +155 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/date-range-picker.tsx +1 -1
- package/src/components/dialog.tsx +1 -1
- package/src/components/form-field.tsx +1 -1
- package/src/components/impact-metrics-form.tsx +3 -3
- package/src/components/input.tsx +3 -3
- package/src/components/radio-group.tsx +2 -2
- package/src/components/select.tsx +4 -4
- package/src/components/sheet.tsx +1 -1
- package/src/components/slider.tsx +2 -2
- package/src/components/support-chat.tsx +236 -0
- package/src/components/switch.tsx +1 -1
- package/src/components/tabs.tsx +2 -2
- package/src/components/textarea.tsx +3 -3
- package/src/components/toast.tsx +1 -1
- package/src/index.ts +4 -0
- package/src/styles/globals.css +2 -3
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import { cva, type VariantProps } from 'class-variance-authority'
|
|
|
7
7
|
import { cn } from '../lib/utils'
|
|
8
8
|
|
|
9
9
|
const buttonVariants = cva(
|
|
10
|
-
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-semibold
|
|
10
|
+
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-semibold transition-all duration-200 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 cursor-pointer',
|
|
11
11
|
{
|
|
12
12
|
variants: {
|
|
13
13
|
variant: {
|
|
@@ -124,7 +124,7 @@ const DateRangeSelect = React.forwardRef<HTMLSelectElement, DateRangeSelectProps
|
|
|
124
124
|
value={value}
|
|
125
125
|
onChange={(e) => onChange(e.target.value as DateRangePreset)}
|
|
126
126
|
className={cn(
|
|
127
|
-
'h-9 rounded-sm border border-input bg-background px-3 text-sm
|
|
127
|
+
'h-9 rounded-sm border border-input bg-background px-3 text-sm focus:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
128
128
|
className
|
|
129
129
|
)}
|
|
130
130
|
>
|
|
@@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
|
|
|
44
44
|
{...props}
|
|
45
45
|
>
|
|
46
46
|
{children}
|
|
47
|
-
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70
|
|
47
|
+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none data-[state=open]:bg-gray-100">
|
|
48
48
|
<X className="h-4 w-4" />
|
|
49
49
|
<span className="sr-only">Close</span>
|
|
50
50
|
</DialogPrimitive.Close>
|
|
@@ -39,7 +39,7 @@ const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(
|
|
|
39
39
|
'aria-describedby': error ? errorId : helperText ? helperId : undefined,
|
|
40
40
|
className: cn(
|
|
41
41
|
childProps.className,
|
|
42
|
-
error && 'border-red-500
|
|
42
|
+
error && 'border-red-500'
|
|
43
43
|
),
|
|
44
44
|
})
|
|
45
45
|
}
|
|
@@ -205,7 +205,7 @@ export function ImpactMetricsForm({
|
|
|
205
205
|
...prev,
|
|
206
206
|
time_saved_minutes_per_run: parseInt(e.target.value) || 0
|
|
207
207
|
}))}
|
|
208
|
-
className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none
|
|
208
|
+
className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none bg-background"
|
|
209
209
|
min="0"
|
|
210
210
|
/>
|
|
211
211
|
<span className="text-sm text-muted-foreground">min</span>
|
|
@@ -230,7 +230,7 @@ export function ImpactMetricsForm({
|
|
|
230
230
|
...prev,
|
|
231
231
|
hourly_rate_euros: parseFloat(e.target.value) || 0
|
|
232
232
|
}))}
|
|
233
|
-
className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none
|
|
233
|
+
className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none bg-background"
|
|
234
234
|
min="0"
|
|
235
235
|
step="0.5"
|
|
236
236
|
/>
|
|
@@ -255,7 +255,7 @@ export function ImpactMetricsForm({
|
|
|
255
255
|
...prev,
|
|
256
256
|
fte_equivalent: (parseFloat(e.target.value) || 0) / 100
|
|
257
257
|
}))}
|
|
258
|
-
className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none
|
|
258
|
+
className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none bg-background"
|
|
259
259
|
min="0"
|
|
260
260
|
max="1000"
|
|
261
261
|
step="5"
|
package/src/components/input.tsx
CHANGED
|
@@ -15,10 +15,10 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
15
15
|
<input
|
|
16
16
|
type={type}
|
|
17
17
|
className={cn(
|
|
18
|
-
'flex h-10 w-full rounded-sm border bg-white px-3 py-2 text-base text-[var(--black)]
|
|
18
|
+
'flex h-10 w-full rounded-sm border bg-white px-3 py-2 text-base text-[var(--black)] file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-[var(--black)] placeholder:text-gray-500 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm transition-colors',
|
|
19
19
|
error
|
|
20
|
-
? 'border-red-500
|
|
21
|
-
: 'border-gray-300
|
|
20
|
+
? 'border-red-500'
|
|
21
|
+
: 'border-gray-300',
|
|
22
22
|
className
|
|
23
23
|
)}
|
|
24
24
|
ref={ref}
|
|
@@ -27,7 +27,7 @@ const RadioGroupItem = React.forwardRef<
|
|
|
27
27
|
<RadioGroupPrimitive.Item
|
|
28
28
|
ref={ref}
|
|
29
29
|
className={cn(
|
|
30
|
-
'aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:
|
|
30
|
+
'aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
31
31
|
className
|
|
32
32
|
)}
|
|
33
33
|
{...props}
|
|
@@ -91,7 +91,7 @@ const RadioGroupCard = React.forwardRef<
|
|
|
91
91
|
ref={ref}
|
|
92
92
|
id={cardId}
|
|
93
93
|
className={cn(
|
|
94
|
-
'relative flex cursor-pointer rounded-lg border bg-background p-4 transition-all hover:border-[var(--cyan)]/50 focus:outline-none focus-visible:
|
|
94
|
+
'relative flex cursor-pointer rounded-lg border bg-background p-4 transition-all hover:border-[var(--cyan)]/50 focus:outline-none focus-visible:outline-none data-[state=checked]:border-[var(--cyan)] data-[state=checked]:ring-1 data-[state=checked]:ring-[var(--cyan)] disabled:cursor-not-allowed disabled:opacity-50',
|
|
95
95
|
className
|
|
96
96
|
)}
|
|
97
97
|
{...props}
|
|
@@ -15,14 +15,14 @@ const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
15
15
|
<select
|
|
16
16
|
ref={ref}
|
|
17
17
|
className={cn(
|
|
18
|
-
'h-10 w-full rounded-sm border bg-white px-3 py-2 text-base text-[var(--black)]
|
|
18
|
+
'h-10 w-full rounded-sm border bg-white px-3 py-2 text-base text-[var(--black)]',
|
|
19
19
|
'placeholder:text-gray-500',
|
|
20
|
-
'focus-visible:outline-none
|
|
20
|
+
'focus-visible:outline-none',
|
|
21
21
|
'disabled:cursor-not-allowed disabled:opacity-50 md:text-sm transition-colors',
|
|
22
22
|
'appearance-none pr-10',
|
|
23
23
|
error
|
|
24
|
-
? 'border-red-500
|
|
25
|
-
: 'border-gray-300
|
|
24
|
+
? 'border-red-500'
|
|
25
|
+
: 'border-gray-300',
|
|
26
26
|
className
|
|
27
27
|
)}
|
|
28
28
|
aria-invalid={error ? 'true' : undefined}
|
package/src/components/sheet.tsx
CHANGED
|
@@ -68,7 +68,7 @@ const SheetContent = React.forwardRef<
|
|
|
68
68
|
>
|
|
69
69
|
{children}
|
|
70
70
|
{/* @ts-expect-error - Radix Dialog Close accepts className and children at runtime */}
|
|
71
|
-
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70
|
|
71
|
+
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none data-[state=open]:bg-gray-100">
|
|
72
72
|
<X className="h-4 w-4" />
|
|
73
73
|
<span className="sr-only">Close</span>
|
|
74
74
|
</SheetPrimitive.Close>
|
|
@@ -56,8 +56,8 @@ const Slider = React.forwardRef<
|
|
|
56
56
|
</SliderPrimitive.Track>
|
|
57
57
|
<SliderPrimitive.Thumb
|
|
58
58
|
className={cn(
|
|
59
|
-
'block rounded-full border-2 border-[var(--cyan)] bg-white shadow-md
|
|
60
|
-
'focus-visible:outline-none
|
|
59
|
+
'block rounded-full border-2 border-[var(--cyan)] bg-white shadow-md transition-colors',
|
|
60
|
+
'focus-visible:outline-none',
|
|
61
61
|
'disabled:pointer-events-none disabled:opacity-50',
|
|
62
62
|
'hover:border-[var(--cyan-dark,#00bfaa)] cursor-pointer',
|
|
63
63
|
sizeStyles[size].thumb
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useRef, useCallback, ReactNode } from 'react'
|
|
4
|
+
import { Button } from './button'
|
|
5
|
+
import { Textarea } from './textarea'
|
|
6
|
+
import {
|
|
7
|
+
CircleNotch,
|
|
8
|
+
PaperPlaneTilt,
|
|
9
|
+
} from '@phosphor-icons/react'
|
|
10
|
+
|
|
11
|
+
// ============================================
|
|
12
|
+
// Types
|
|
13
|
+
// ============================================
|
|
14
|
+
|
|
15
|
+
export interface SupportChatMessage {
|
|
16
|
+
id: string
|
|
17
|
+
role: 'customer' | 'admin' | 'system'
|
|
18
|
+
content: string
|
|
19
|
+
type: 'message' | 'event' | 'suggestion'
|
|
20
|
+
created_at: string
|
|
21
|
+
metadata?: Record<string, unknown>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SupportChatProps {
|
|
25
|
+
/** Fetch messages from this endpoint */
|
|
26
|
+
messagesEndpoint: string
|
|
27
|
+
/** POST new messages to this endpoint */
|
|
28
|
+
sendEndpoint: string
|
|
29
|
+
/** Role of the current user */
|
|
30
|
+
currentRole: 'customer' | 'admin'
|
|
31
|
+
/** Poll interval in ms (0 to disable). Default: 5000 */
|
|
32
|
+
pollInterval?: number
|
|
33
|
+
/** Placeholder for the input */
|
|
34
|
+
placeholder?: string
|
|
35
|
+
/** Custom empty state */
|
|
36
|
+
emptyMessage?: string | ReactNode
|
|
37
|
+
/** Additional CSS class */
|
|
38
|
+
className?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================
|
|
42
|
+
// Helpers
|
|
43
|
+
// ============================================
|
|
44
|
+
|
|
45
|
+
function formatMessageTime(dateStr: string): string {
|
|
46
|
+
const d = new Date(dateStr)
|
|
47
|
+
const now = new Date()
|
|
48
|
+
const diffMs = now.getTime() - d.getTime()
|
|
49
|
+
const diffMins = Math.floor(diffMs / 60000)
|
|
50
|
+
const diffHours = Math.floor(diffMs / 3600000)
|
|
51
|
+
|
|
52
|
+
if (diffMins < 1) return 'just now'
|
|
53
|
+
if (diffMins < 60) return `${diffMins}m ago`
|
|
54
|
+
if (diffHours < 24) return `${diffHours}h ago`
|
|
55
|
+
|
|
56
|
+
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' })
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================
|
|
60
|
+
// Component
|
|
61
|
+
// ============================================
|
|
62
|
+
|
|
63
|
+
export function SupportChat({
|
|
64
|
+
messagesEndpoint,
|
|
65
|
+
sendEndpoint,
|
|
66
|
+
currentRole,
|
|
67
|
+
pollInterval = 5000,
|
|
68
|
+
placeholder = 'Type a message...',
|
|
69
|
+
emptyMessage = 'No messages yet. Start the conversation!',
|
|
70
|
+
className = '',
|
|
71
|
+
}: SupportChatProps) {
|
|
72
|
+
const [messages, setMessages] = useState<SupportChatMessage[]>([])
|
|
73
|
+
const [loading, setLoading] = useState(true)
|
|
74
|
+
const [sending, setSending] = useState(false)
|
|
75
|
+
const [inputValue, setInputValue] = useState('')
|
|
76
|
+
const scrollRef = useRef<HTMLDivElement>(null)
|
|
77
|
+
const prevMessageCountRef = useRef(0)
|
|
78
|
+
|
|
79
|
+
const fetchMessages = useCallback(async () => {
|
|
80
|
+
try {
|
|
81
|
+
const res = await fetch(messagesEndpoint)
|
|
82
|
+
if (!res.ok) return
|
|
83
|
+
const data = await res.json()
|
|
84
|
+
setMessages(data.messages || [])
|
|
85
|
+
} catch {
|
|
86
|
+
// Silently fail
|
|
87
|
+
} finally {
|
|
88
|
+
setLoading(false)
|
|
89
|
+
}
|
|
90
|
+
}, [messagesEndpoint])
|
|
91
|
+
|
|
92
|
+
// Initial fetch + polling
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
fetchMessages()
|
|
95
|
+
if (pollInterval > 0) {
|
|
96
|
+
const interval = setInterval(fetchMessages, pollInterval)
|
|
97
|
+
return () => clearInterval(interval)
|
|
98
|
+
}
|
|
99
|
+
}, [fetchMessages, pollInterval])
|
|
100
|
+
|
|
101
|
+
// Auto-scroll when new messages arrive
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (messages.length > prevMessageCountRef.current) {
|
|
104
|
+
scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' })
|
|
105
|
+
}
|
|
106
|
+
prevMessageCountRef.current = messages.length
|
|
107
|
+
}, [messages.length])
|
|
108
|
+
|
|
109
|
+
const handleSend = async () => {
|
|
110
|
+
const content = inputValue.trim()
|
|
111
|
+
if (!content || sending) return
|
|
112
|
+
|
|
113
|
+
setSending(true)
|
|
114
|
+
setInputValue('')
|
|
115
|
+
|
|
116
|
+
// Optimistic update
|
|
117
|
+
const optimisticMsg: SupportChatMessage = {
|
|
118
|
+
id: `temp-${Date.now()}`,
|
|
119
|
+
role: currentRole,
|
|
120
|
+
content,
|
|
121
|
+
type: 'message',
|
|
122
|
+
created_at: new Date().toISOString(),
|
|
123
|
+
}
|
|
124
|
+
setMessages((prev) => [...prev, optimisticMsg])
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const res = await fetch(sendEndpoint, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: { 'Content-Type': 'application/json' },
|
|
130
|
+
body: JSON.stringify({ content }),
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
if (res.ok) {
|
|
134
|
+
// Replace optimistic message with real one
|
|
135
|
+
await fetchMessages()
|
|
136
|
+
} else {
|
|
137
|
+
// Remove optimistic message on failure
|
|
138
|
+
setMessages((prev) => prev.filter((m) => m.id !== optimisticMsg.id))
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
setMessages((prev) => prev.filter((m) => m.id !== optimisticMsg.id))
|
|
142
|
+
} finally {
|
|
143
|
+
setSending(false)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
148
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
149
|
+
e.preventDefault()
|
|
150
|
+
handleSend()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (loading) {
|
|
155
|
+
return (
|
|
156
|
+
<div className={`flex items-center justify-center py-8 ${className}`}>
|
|
157
|
+
<CircleNotch className="w-5 h-5 animate-spin text-muted-foreground" />
|
|
158
|
+
</div>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div className={`flex flex-col ${className}`}>
|
|
164
|
+
{/* Messages area */}
|
|
165
|
+
<div ref={scrollRef} className="flex-1 overflow-y-auto space-y-3 mb-4 max-h-[400px] min-h-[200px] px-1">
|
|
166
|
+
{messages.length === 0 ? (
|
|
167
|
+
<div className="flex items-center justify-center h-full text-sm text-muted-foreground py-8">
|
|
168
|
+
{typeof emptyMessage === 'string' ? <p>{emptyMessage}</p> : emptyMessage}
|
|
169
|
+
</div>
|
|
170
|
+
) : (
|
|
171
|
+
messages.map((msg) => {
|
|
172
|
+
// System events — centered, subtle
|
|
173
|
+
if (msg.type === 'event' || msg.role === 'system') {
|
|
174
|
+
return (
|
|
175
|
+
<div key={msg.id} className="flex justify-center">
|
|
176
|
+
<div className="bg-gray-50 border border-gray-100 rounded-full px-3 py-1 text-xs text-muted-foreground max-w-[90%] text-center">
|
|
177
|
+
{msg.content}
|
|
178
|
+
<span className="ml-2 opacity-60">{formatMessageTime(msg.created_at)}</span>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Regular messages
|
|
185
|
+
const isCurrentUser = msg.role === currentRole
|
|
186
|
+
return (
|
|
187
|
+
<div key={msg.id} className={`flex ${isCurrentUser ? 'justify-end' : 'justify-start'}`}>
|
|
188
|
+
<div className={`max-w-[80%] rounded-lg px-3 py-2 ${
|
|
189
|
+
isCurrentUser
|
|
190
|
+
? 'bg-[var(--cyan)] text-white'
|
|
191
|
+
: 'bg-gray-100 text-[var(--black)]'
|
|
192
|
+
}`}>
|
|
193
|
+
{!isCurrentUser && (
|
|
194
|
+
<p className="text-xs font-medium mb-0.5 opacity-70">
|
|
195
|
+
{msg.role === 'admin' ? 'Dilipod Team' : 'You'}
|
|
196
|
+
</p>
|
|
197
|
+
)}
|
|
198
|
+
<p className="text-sm whitespace-pre-wrap">{msg.content}</p>
|
|
199
|
+
<p className={`text-[10px] mt-1 ${isCurrentUser ? 'text-white/60' : 'text-muted-foreground'}`}>
|
|
200
|
+
{formatMessageTime(msg.created_at)}
|
|
201
|
+
</p>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
205
|
+
})
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
{/* Input area */}
|
|
210
|
+
<div className="flex gap-2 border-t border-gray-100 pt-3">
|
|
211
|
+
<Textarea
|
|
212
|
+
value={inputValue}
|
|
213
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
214
|
+
onKeyDown={handleKeyDown}
|
|
215
|
+
placeholder={placeholder}
|
|
216
|
+
rows={1}
|
|
217
|
+
className="resize-none min-h-[36px] py-2 flex-1"
|
|
218
|
+
/>
|
|
219
|
+
<Button
|
|
220
|
+
onClick={handleSend}
|
|
221
|
+
disabled={sending || !inputValue.trim()}
|
|
222
|
+
size="sm"
|
|
223
|
+
className="flex-shrink-0 h-9"
|
|
224
|
+
>
|
|
225
|
+
{sending ? (
|
|
226
|
+
<CircleNotch className="w-4 h-4 animate-spin" />
|
|
227
|
+
) : (
|
|
228
|
+
<PaperPlaneTilt className="w-4 h-4" weight="bold" />
|
|
229
|
+
)}
|
|
230
|
+
</Button>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export default SupportChat
|
|
@@ -32,7 +32,7 @@ const Switch = React.forwardRef<
|
|
|
32
32
|
return (
|
|
33
33
|
<SwitchPrimitive.Root
|
|
34
34
|
className={cn(
|
|
35
|
-
'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none
|
|
35
|
+
'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-[var(--cyan)] data-[state=unchecked]:bg-input',
|
|
36
36
|
sizeStyles[size].root,
|
|
37
37
|
className
|
|
38
38
|
)}
|
package/src/components/tabs.tsx
CHANGED
|
@@ -28,7 +28,7 @@ const TabsTrigger = React.forwardRef<
|
|
|
28
28
|
<TabsPrimitive.Trigger
|
|
29
29
|
ref={ref}
|
|
30
30
|
className={cn(
|
|
31
|
-
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium
|
|
31
|
+
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',
|
|
32
32
|
className
|
|
33
33
|
)}
|
|
34
34
|
{...props}
|
|
@@ -43,7 +43,7 @@ const TabsContent = React.forwardRef<
|
|
|
43
43
|
<TabsPrimitive.Content
|
|
44
44
|
ref={ref}
|
|
45
45
|
className={cn(
|
|
46
|
-
'mt-2
|
|
46
|
+
'mt-2 focus-visible:outline-none',
|
|
47
47
|
className
|
|
48
48
|
)}
|
|
49
49
|
{...props}
|
|
@@ -14,10 +14,10 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
14
14
|
return (
|
|
15
15
|
<textarea
|
|
16
16
|
className={cn(
|
|
17
|
-
'flex min-h-[80px] w-full rounded-sm border bg-white px-3 py-2 text-base text-[var(--black)]
|
|
17
|
+
'flex min-h-[80px] w-full rounded-sm border bg-white px-3 py-2 text-base text-[var(--black)] placeholder:text-gray-500 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm transition-colors resize-none',
|
|
18
18
|
error
|
|
19
|
-
? 'border-red-500
|
|
20
|
-
: 'border-gray-300
|
|
19
|
+
? 'border-red-500'
|
|
20
|
+
: 'border-gray-300',
|
|
21
21
|
className
|
|
22
22
|
)}
|
|
23
23
|
ref={ref}
|
package/src/components/toast.tsx
CHANGED
|
@@ -63,7 +63,7 @@ const ToastAction = React.forwardRef<
|
|
|
63
63
|
<ToastPrimitives.Action
|
|
64
64
|
ref={ref}
|
|
65
65
|
className={cn(
|
|
66
|
-
'inline-flex h-8 shrink-0 items-center justify-center rounded-sm border bg-transparent px-3 text-sm font-medium
|
|
66
|
+
'inline-flex h-8 shrink-0 items-center justify-center rounded-sm border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-gray-100 focus:outline-none disabled:pointer-events-none disabled:opacity-50',
|
|
67
67
|
className
|
|
68
68
|
)}
|
|
69
69
|
{...props}
|
package/src/index.ts
CHANGED
|
@@ -281,6 +281,10 @@ export type { ImpactMetrics, ImpactMetricsFormProps } from './components/impact-
|
|
|
281
281
|
export { ActivityTimeline } from './components/activity-timeline'
|
|
282
282
|
export type { Activity, ActivityTimelineProps } from './components/activity-timeline'
|
|
283
283
|
|
|
284
|
+
// Support Chat Components
|
|
285
|
+
export { SupportChat } from './components/support-chat'
|
|
286
|
+
export type { SupportChatMessage, SupportChatProps } from './components/support-chat'
|
|
287
|
+
|
|
284
288
|
// Workflow Components
|
|
285
289
|
export { WorkflowFlow } from './components/workflow-flow'
|
|
286
290
|
export type { WorkflowFlowProps, N8nWorkflow as WorkflowFlowN8nWorkflow, N8nNode } from './components/workflow-flow'
|
package/src/styles/globals.css
CHANGED
|
@@ -241,10 +241,9 @@ a:not([disabled]), button:not([disabled]) {
|
|
|
241
241
|
cursor: pointer;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
/*
|
|
244
|
+
/* Remove default focus outline */
|
|
245
245
|
*:focus-visible {
|
|
246
|
-
outline:
|
|
247
|
-
outline-offset: 2px;
|
|
246
|
+
outline: none;
|
|
248
247
|
}
|
|
249
248
|
|
|
250
249
|
/* Smooth scroll */
|