myoperator-ui 0.0.210-beta.0 → 0.0.210-beta.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/index.js +437 -516
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2194,172 +2194,6 @@ export const ReadableField = React.forwardRef<HTMLDivElement, ReadableFieldProps
|
|
|
2194
2194
|
);
|
|
2195
2195
|
|
|
2196
2196
|
ReadableField.displayName = "ReadableField";
|
|
2197
|
-
`, prefix)
|
|
2198
|
-
}
|
|
2199
|
-
]
|
|
2200
|
-
},
|
|
2201
|
-
"textarea": {
|
|
2202
|
-
name: "textarea",
|
|
2203
|
-
description: "A multi-line text area with label, helper text, error state, and character count support",
|
|
2204
|
-
category: "form",
|
|
2205
|
-
dependencies: [
|
|
2206
|
-
"class-variance-authority",
|
|
2207
|
-
"clsx",
|
|
2208
|
-
"tailwind-merge"
|
|
2209
|
-
],
|
|
2210
|
-
files: [
|
|
2211
|
-
{
|
|
2212
|
-
name: "textarea.tsx",
|
|
2213
|
-
content: prefixTailwindClasses(`import * as React from "react";
|
|
2214
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
2215
|
-
|
|
2216
|
-
import { cn } from "../../lib/utils";
|
|
2217
|
-
|
|
2218
|
-
const textAreaVariants = cva(
|
|
2219
|
-
"w-full rounded bg-semantic-bg-primary px-4 py-3 text-base text-semantic-text-primary outline-none transition-all resize-y placeholder:text-semantic-text-placeholder disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)]",
|
|
2220
|
-
{
|
|
2221
|
-
variants: {
|
|
2222
|
-
state: {
|
|
2223
|
-
default:
|
|
2224
|
-
"border border-semantic-border-input focus:outline-none focus:border-semantic-border-input-focus/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
2225
|
-
error:
|
|
2226
|
-
"border border-semantic-error-primary/40 focus:outline-none focus:border-semantic-error-primary/60 focus:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
|
|
2227
|
-
},
|
|
2228
|
-
},
|
|
2229
|
-
defaultVariants: {
|
|
2230
|
-
state: "default",
|
|
2231
|
-
},
|
|
2232
|
-
}
|
|
2233
|
-
);
|
|
2234
|
-
|
|
2235
|
-
export interface TextAreaProps
|
|
2236
|
-
extends React.ComponentProps<"textarea">,
|
|
2237
|
-
VariantProps<typeof textAreaVariants> {
|
|
2238
|
-
/** Label text displayed above the textarea */
|
|
2239
|
-
label?: string;
|
|
2240
|
-
/** Shows red asterisk next to label when true */
|
|
2241
|
-
required?: boolean;
|
|
2242
|
-
/** Helper text displayed below the textarea */
|
|
2243
|
-
helperText?: string;
|
|
2244
|
-
/** Error message - shows error state with red styling */
|
|
2245
|
-
error?: string;
|
|
2246
|
-
/** Shows character count when maxLength is set */
|
|
2247
|
-
showCount?: boolean;
|
|
2248
|
-
/** Additional class for the wrapper container */
|
|
2249
|
-
wrapperClassName?: string;
|
|
2250
|
-
/** Additional class for the label */
|
|
2251
|
-
labelClassName?: string;
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
2255
|
-
(
|
|
2256
|
-
{
|
|
2257
|
-
className,
|
|
2258
|
-
wrapperClassName,
|
|
2259
|
-
labelClassName,
|
|
2260
|
-
state,
|
|
2261
|
-
label,
|
|
2262
|
-
required,
|
|
2263
|
-
helperText,
|
|
2264
|
-
error,
|
|
2265
|
-
showCount,
|
|
2266
|
-
maxLength,
|
|
2267
|
-
value,
|
|
2268
|
-
defaultValue,
|
|
2269
|
-
onChange,
|
|
2270
|
-
disabled,
|
|
2271
|
-
id,
|
|
2272
|
-
rows = 4,
|
|
2273
|
-
...props
|
|
2274
|
-
},
|
|
2275
|
-
ref
|
|
2276
|
-
) => {
|
|
2277
|
-
const [internalValue, setInternalValue] = React.useState(
|
|
2278
|
-
defaultValue ?? ""
|
|
2279
|
-
);
|
|
2280
|
-
|
|
2281
|
-
const isControlled = value !== undefined;
|
|
2282
|
-
const currentValue = isControlled ? value : internalValue;
|
|
2283
|
-
const derivedState = error ? "error" : (state ?? "default");
|
|
2284
|
-
|
|
2285
|
-
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
2286
|
-
if (!isControlled) setInternalValue(e.target.value);
|
|
2287
|
-
onChange?.(e);
|
|
2288
|
-
};
|
|
2289
|
-
|
|
2290
|
-
const generatedId = React.useId();
|
|
2291
|
-
const textAreaId = id || generatedId;
|
|
2292
|
-
const helperId = \`\${textAreaId}-helper\`;
|
|
2293
|
-
const errorId = \`\${textAreaId}-error\`;
|
|
2294
|
-
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
2295
|
-
const charCount = String(currentValue).length;
|
|
2296
|
-
|
|
2297
|
-
return (
|
|
2298
|
-
<div className={cn("flex flex-col gap-1.5", wrapperClassName)}>
|
|
2299
|
-
{label && (
|
|
2300
|
-
<label
|
|
2301
|
-
htmlFor={textAreaId}
|
|
2302
|
-
className={cn(
|
|
2303
|
-
"text-sm font-semibold text-semantic-text-secondary tracking-[0.014px]",
|
|
2304
|
-
labelClassName
|
|
2305
|
-
)}
|
|
2306
|
-
>
|
|
2307
|
-
{label}
|
|
2308
|
-
{required && (
|
|
2309
|
-
<span className="text-semantic-error-primary ml-0.5">*</span>
|
|
2310
|
-
)}
|
|
2311
|
-
</label>
|
|
2312
|
-
)}
|
|
2313
|
-
|
|
2314
|
-
<textarea
|
|
2315
|
-
ref={ref}
|
|
2316
|
-
id={textAreaId}
|
|
2317
|
-
rows={rows}
|
|
2318
|
-
className={cn(textAreaVariants({ state: derivedState, className }))}
|
|
2319
|
-
disabled={disabled}
|
|
2320
|
-
maxLength={maxLength}
|
|
2321
|
-
value={isControlled ? value : undefined}
|
|
2322
|
-
defaultValue={!isControlled ? defaultValue : undefined}
|
|
2323
|
-
onChange={handleChange}
|
|
2324
|
-
aria-invalid={!!error}
|
|
2325
|
-
aria-describedby={ariaDescribedBy}
|
|
2326
|
-
{...props}
|
|
2327
|
-
/>
|
|
2328
|
-
|
|
2329
|
-
{(error || helperText || (showCount && maxLength)) && (
|
|
2330
|
-
<div className="flex justify-between items-start gap-2">
|
|
2331
|
-
{error ? (
|
|
2332
|
-
<span id={errorId} className="text-xs text-semantic-error-primary">
|
|
2333
|
-
{error}
|
|
2334
|
-
</span>
|
|
2335
|
-
) : helperText ? (
|
|
2336
|
-
<span id={helperId} className="text-xs text-semantic-text-muted">
|
|
2337
|
-
{helperText}
|
|
2338
|
-
</span>
|
|
2339
|
-
) : (
|
|
2340
|
-
<span />
|
|
2341
|
-
)}
|
|
2342
|
-
{showCount && maxLength && (
|
|
2343
|
-
<span
|
|
2344
|
-
className={cn(
|
|
2345
|
-
"text-xs",
|
|
2346
|
-
charCount > maxLength
|
|
2347
|
-
? "text-semantic-error-primary"
|
|
2348
|
-
: "text-semantic-text-muted"
|
|
2349
|
-
)}
|
|
2350
|
-
>
|
|
2351
|
-
{charCount}/{maxLength}
|
|
2352
|
-
</span>
|
|
2353
|
-
)}
|
|
2354
|
-
</div>
|
|
2355
|
-
)}
|
|
2356
|
-
</div>
|
|
2357
|
-
);
|
|
2358
|
-
}
|
|
2359
|
-
);
|
|
2360
|
-
TextArea.displayName = "TextArea";
|
|
2361
|
-
|
|
2362
|
-
export { TextArea, textAreaVariants };
|
|
2363
2197
|
`, prefix)
|
|
2364
2198
|
}
|
|
2365
2199
|
]
|
|
@@ -13770,17 +13604,10 @@ export type {
|
|
|
13770
13604
|
{
|
|
13771
13605
|
name: "ivr-bot-config.tsx",
|
|
13772
13606
|
content: prefixTailwindClasses(`import * as React from "react";
|
|
13773
|
-
import { Info } from "lucide-react";
|
|
13774
13607
|
import { cn } from "../../../lib/utils";
|
|
13775
13608
|
import { Button } from "../button";
|
|
13776
13609
|
import { Badge } from "../badge";
|
|
13777
13610
|
import { PageHeader } from "../page-header";
|
|
13778
|
-
import {
|
|
13779
|
-
Accordion,
|
|
13780
|
-
AccordionItem,
|
|
13781
|
-
AccordionTrigger,
|
|
13782
|
-
AccordionContent,
|
|
13783
|
-
} from "../accordion";
|
|
13784
13611
|
import { BotIdentityCard } from "./bot-identity-card";
|
|
13785
13612
|
import { BotBehaviorCard } from "./bot-behavior-card";
|
|
13786
13613
|
import { KnowledgeBaseCard } from "./knowledge-base-card";
|
|
@@ -13794,106 +13621,8 @@ import type {
|
|
|
13794
13621
|
IvrBotConfigData,
|
|
13795
13622
|
CreateFunctionData,
|
|
13796
13623
|
} from "./types";
|
|
13624
|
+
import { FallbackPromptsCard } from "./fallback-prompts-card";
|
|
13797
13625
|
|
|
13798
|
-
// \u2500\u2500\u2500 Styled Textarea (still used by FallbackPromptsAccordion) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
13799
|
-
function StyledTextarea({
|
|
13800
|
-
placeholder,
|
|
13801
|
-
value,
|
|
13802
|
-
rows = 3,
|
|
13803
|
-
onChange,
|
|
13804
|
-
disabled,
|
|
13805
|
-
className,
|
|
13806
|
-
}: {
|
|
13807
|
-
placeholder?: string;
|
|
13808
|
-
value?: string;
|
|
13809
|
-
rows?: number;
|
|
13810
|
-
onChange?: (v: string) => void;
|
|
13811
|
-
disabled?: boolean;
|
|
13812
|
-
className?: string;
|
|
13813
|
-
}) {
|
|
13814
|
-
return (
|
|
13815
|
-
<textarea
|
|
13816
|
-
value={value ?? ""}
|
|
13817
|
-
rows={rows}
|
|
13818
|
-
onChange={(e) => onChange?.(e.target.value)}
|
|
13819
|
-
placeholder={placeholder}
|
|
13820
|
-
disabled={disabled}
|
|
13821
|
-
className={cn(
|
|
13822
|
-
"w-full px-4 py-2.5 text-base rounded border resize-none",
|
|
13823
|
-
"border-semantic-border-input bg-semantic-bg-primary",
|
|
13824
|
-
"text-semantic-text-primary placeholder:text-semantic-text-muted",
|
|
13825
|
-
"outline-none hover:border-semantic-border-input-focus",
|
|
13826
|
-
"focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
13827
|
-
disabled && "opacity-50 cursor-not-allowed",
|
|
13828
|
-
className
|
|
13829
|
-
)}
|
|
13830
|
-
/>
|
|
13831
|
-
);
|
|
13832
|
-
}
|
|
13833
|
-
|
|
13834
|
-
// \u2500\u2500\u2500 Field wrapper (still used by FallbackPromptsAccordion) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
13835
|
-
function Field({
|
|
13836
|
-
label,
|
|
13837
|
-
children,
|
|
13838
|
-
}: {
|
|
13839
|
-
label: string;
|
|
13840
|
-
children: React.ReactNode;
|
|
13841
|
-
}) {
|
|
13842
|
-
return (
|
|
13843
|
-
<div className="flex flex-col gap-1.5">
|
|
13844
|
-
<label className="text-sm font-semibold text-semantic-text-secondary tracking-[0.014px]">
|
|
13845
|
-
{label}
|
|
13846
|
-
</label>
|
|
13847
|
-
{children}
|
|
13848
|
-
</div>
|
|
13849
|
-
);
|
|
13850
|
-
}
|
|
13851
|
-
|
|
13852
|
-
// \u2500\u2500\u2500 Fallback Prompts (accordion) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
13853
|
-
function FallbackPromptsAccordion({
|
|
13854
|
-
data,
|
|
13855
|
-
onChange,
|
|
13856
|
-
disabled,
|
|
13857
|
-
}: {
|
|
13858
|
-
data: Partial<IvrBotConfigData>;
|
|
13859
|
-
onChange: (patch: Partial<IvrBotConfigData>) => void;
|
|
13860
|
-
disabled?: boolean;
|
|
13861
|
-
}) {
|
|
13862
|
-
return (
|
|
13863
|
-
<div className="bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden">
|
|
13864
|
-
<Accordion type="single">
|
|
13865
|
-
<AccordionItem value="fallback">
|
|
13866
|
-
<AccordionTrigger className="px-4 py-4 border-b border-semantic-border-layout hover:no-underline sm:px-6 sm:py-5">
|
|
13867
|
-
<span className="flex items-center gap-1.5 text-base font-semibold text-semantic-text-primary">
|
|
13868
|
-
Fallback Prompts
|
|
13869
|
-
<Info className="size-3.5 text-semantic-text-muted shrink-0" />
|
|
13870
|
-
</span>
|
|
13871
|
-
</AccordionTrigger>
|
|
13872
|
-
<AccordionContent>
|
|
13873
|
-
<div className="px-4 pt-4 pb-2 flex flex-col gap-6 sm:px-6 sm:pt-6">
|
|
13874
|
-
<Field label="Agent Busy Prompt">
|
|
13875
|
-
<StyledTextarea
|
|
13876
|
-
value={data.agentBusyPrompt ?? ""}
|
|
13877
|
-
onChange={(v) => onChange({ agentBusyPrompt: v })}
|
|
13878
|
-
placeholder="Executives are busy at the moment, we will connect you soon."
|
|
13879
|
-
disabled={disabled}
|
|
13880
|
-
/>
|
|
13881
|
-
</Field>
|
|
13882
|
-
<Field label="No Extension Found">
|
|
13883
|
-
<StyledTextarea
|
|
13884
|
-
value={data.noExtensionPrompt ?? ""}
|
|
13885
|
-
onChange={(v) => onChange({ noExtensionPrompt: v })}
|
|
13886
|
-
placeholder="Sorry, the requested extension is currently unavailable. Let me help you directly."
|
|
13887
|
-
disabled={disabled}
|
|
13888
|
-
/>
|
|
13889
|
-
</Field>
|
|
13890
|
-
</div>
|
|
13891
|
-
</AccordionContent>
|
|
13892
|
-
</AccordionItem>
|
|
13893
|
-
</Accordion>
|
|
13894
|
-
</div>
|
|
13895
|
-
);
|
|
13896
|
-
}
|
|
13897
13626
|
|
|
13898
13627
|
// \u2500\u2500\u2500 Default data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
13899
13628
|
const DEFAULT_DATA: IvrBotConfigData = {
|
|
@@ -13937,8 +13666,14 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
|
|
|
13937
13666
|
onDeleteFunction,
|
|
13938
13667
|
onTestApi,
|
|
13939
13668
|
functionsInfoTooltip,
|
|
13669
|
+
knowledgeBaseInfoTooltip,
|
|
13940
13670
|
functionPromptMinLength,
|
|
13941
13671
|
functionPromptMaxLength,
|
|
13672
|
+
functionEditData,
|
|
13673
|
+
systemPromptMaxLength,
|
|
13674
|
+
onSystemPromptBlur,
|
|
13675
|
+
onAgentBusyPromptBlur,
|
|
13676
|
+
onNoExtensionFoundPromptBlur,
|
|
13942
13677
|
onBack,
|
|
13943
13678
|
onPlayVoice,
|
|
13944
13679
|
onPauseVoice,
|
|
@@ -13963,6 +13698,7 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
|
|
|
13963
13698
|
...initialData,
|
|
13964
13699
|
});
|
|
13965
13700
|
const [createFnOpen, setCreateFnOpen] = React.useState(false);
|
|
13701
|
+
const [editFnOpen, setEditFnOpen] = React.useState(false);
|
|
13966
13702
|
const [uploadOpen, setUploadOpen] = React.useState(false);
|
|
13967
13703
|
|
|
13968
13704
|
const update = (patch: Partial<IvrBotConfigData>) =>
|
|
@@ -13974,6 +13710,16 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
|
|
|
13974
13710
|
onCreateFunction?.(fnData);
|
|
13975
13711
|
};
|
|
13976
13712
|
|
|
13713
|
+
const handleEditFunction = (id: string) => {
|
|
13714
|
+
onEditFunction?.(id);
|
|
13715
|
+
setEditFnOpen(true);
|
|
13716
|
+
};
|
|
13717
|
+
|
|
13718
|
+
const handleEditFunctionSubmit = (fnData: CreateFunctionData) => {
|
|
13719
|
+
onCreateFunction?.(fnData);
|
|
13720
|
+
setEditFnOpen(false);
|
|
13721
|
+
};
|
|
13722
|
+
|
|
13977
13723
|
return (
|
|
13978
13724
|
<div ref={ref} className={cn("flex flex-col min-h-screen bg-semantic-bg-primary", className)}>
|
|
13979
13725
|
{/* Page header */}
|
|
@@ -14030,10 +13776,26 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
|
|
|
14030
13776
|
<BotBehaviorCard
|
|
14031
13777
|
data={data}
|
|
14032
13778
|
onChange={update}
|
|
13779
|
+
onSystemPromptBlur={onSystemPromptBlur}
|
|
14033
13780
|
sessionVariables={sessionVariables}
|
|
13781
|
+
maxLength={systemPromptMaxLength}
|
|
13782
|
+
disabled={disabled}
|
|
13783
|
+
/>
|
|
13784
|
+
<FallbackPromptsCard
|
|
13785
|
+
data={{
|
|
13786
|
+
agentBusyPrompt: data.agentBusyPrompt,
|
|
13787
|
+
noExtensionFoundPrompt: data.noExtensionPrompt,
|
|
13788
|
+
}}
|
|
13789
|
+
onChange={(patch) =>
|
|
13790
|
+
update({
|
|
13791
|
+
...(patch.agentBusyPrompt !== undefined && { agentBusyPrompt: patch.agentBusyPrompt }),
|
|
13792
|
+
...(patch.noExtensionFoundPrompt !== undefined && { noExtensionPrompt: patch.noExtensionFoundPrompt }),
|
|
13793
|
+
})
|
|
13794
|
+
}
|
|
13795
|
+
onAgentBusyPromptBlur={onAgentBusyPromptBlur}
|
|
13796
|
+
onNoExtensionFoundPromptBlur={onNoExtensionFoundPromptBlur}
|
|
14034
13797
|
disabled={disabled}
|
|
14035
13798
|
/>
|
|
14036
|
-
<FallbackPromptsAccordion data={data} onChange={update} disabled={disabled} />
|
|
14037
13799
|
</div>
|
|
14038
13800
|
|
|
14039
13801
|
{/* Right column \u2014 gray panel extending full height */}
|
|
@@ -14042,6 +13804,7 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
|
|
|
14042
13804
|
files={data.knowledgeBaseFiles}
|
|
14043
13805
|
onAdd={() => setUploadOpen(true)}
|
|
14044
13806
|
onDownload={onDownloadKnowledgeFile}
|
|
13807
|
+
infoTooltip={knowledgeBaseInfoTooltip}
|
|
14045
13808
|
disabled={disabled}
|
|
14046
13809
|
onDelete={(id) => {
|
|
14047
13810
|
update({
|
|
@@ -14055,7 +13818,7 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
|
|
|
14055
13818
|
<FunctionsCard
|
|
14056
13819
|
functions={data.functions}
|
|
14057
13820
|
onAddFunction={() => setCreateFnOpen(true)}
|
|
14058
|
-
onEditFunction={
|
|
13821
|
+
onEditFunction={handleEditFunction}
|
|
14059
13822
|
infoTooltip={functionsInfoTooltip}
|
|
14060
13823
|
disabled={disabled}
|
|
14061
13824
|
onDeleteFunction={(id) => {
|
|
@@ -14093,6 +13856,18 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
|
|
|
14093
13856
|
promptMaxLength={functionPromptMaxLength}
|
|
14094
13857
|
/>
|
|
14095
13858
|
|
|
13859
|
+
{/* Edit Function Modal */}
|
|
13860
|
+
<CreateFunctionModal
|
|
13861
|
+
open={editFnOpen}
|
|
13862
|
+
onOpenChange={setEditFnOpen}
|
|
13863
|
+
onSubmit={handleEditFunctionSubmit}
|
|
13864
|
+
onTestApi={onTestApi}
|
|
13865
|
+
initialData={functionEditData}
|
|
13866
|
+
isEditing
|
|
13867
|
+
promptMinLength={functionPromptMinLength}
|
|
13868
|
+
promptMaxLength={functionPromptMaxLength}
|
|
13869
|
+
/>
|
|
13870
|
+
|
|
14096
13871
|
{/* File Upload Modal */}
|
|
14097
13872
|
<FileUploadModal
|
|
14098
13873
|
open={uploadOpen}
|
|
@@ -14134,6 +13909,24 @@ const METHODS_WITH_BODY: HttpMethod[] = ["POST", "PUT", "PATCH"];
|
|
|
14134
13909
|
const FUNCTION_NAME_MAX = 30;
|
|
14135
13910
|
const BODY_MAX = 4000;
|
|
14136
13911
|
|
|
13912
|
+
// Query parameter validation (aligned with apiIntegrationSchema.queryParams)
|
|
13913
|
+
const QUERY_PARAM_KEY_MAX = 512;
|
|
13914
|
+
const QUERY_PARAM_VALUE_MAX = 2048;
|
|
13915
|
+
const QUERY_PARAM_KEY_PATTERN = /^[a-zA-Z0-9_.\\-~]+$/;
|
|
13916
|
+
|
|
13917
|
+
function validateQueryParamKey(key: string): string | undefined {
|
|
13918
|
+
if (!key.trim()) return "Query param key is required";
|
|
13919
|
+
if (key.length > QUERY_PARAM_KEY_MAX) return "key cannot exceed 512 characters.";
|
|
13920
|
+
if (!QUERY_PARAM_KEY_PATTERN.test(key)) return "Invalid query parameter key.";
|
|
13921
|
+
return undefined;
|
|
13922
|
+
}
|
|
13923
|
+
|
|
13924
|
+
function validateQueryParamValue(value: string): string | undefined {
|
|
13925
|
+
if (!value.trim()) return "Query param value is required";
|
|
13926
|
+
if (value.length > QUERY_PARAM_VALUE_MAX) return "value cannot exceed 2048 characters.";
|
|
13927
|
+
return undefined;
|
|
13928
|
+
}
|
|
13929
|
+
|
|
14137
13930
|
function generateId() {
|
|
14138
13931
|
return Math.random().toString(36).slice(2, 9);
|
|
14139
13932
|
}
|
|
@@ -14156,14 +13949,18 @@ const textareaCls = cn(
|
|
|
14156
13949
|
);
|
|
14157
13950
|
|
|
14158
13951
|
// \u2500\u2500 KeyValueTable \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
13952
|
+
type RowErrors = { key?: string; value?: string };
|
|
13953
|
+
|
|
14159
13954
|
function KeyValueTable({
|
|
14160
13955
|
rows,
|
|
14161
13956
|
onChange,
|
|
14162
13957
|
label,
|
|
13958
|
+
getRowErrors,
|
|
14163
13959
|
}: {
|
|
14164
13960
|
rows: KeyValuePair[];
|
|
14165
13961
|
onChange: (rows: KeyValuePair[]) => void;
|
|
14166
13962
|
label: string;
|
|
13963
|
+
getRowErrors?: (row: KeyValuePair) => RowErrors;
|
|
14167
13964
|
}) {
|
|
14168
13965
|
const update = (id: string, patch: Partial<KeyValuePair>) =>
|
|
14169
13966
|
onChange(rows.map((r) => (r.id === id ? { ...r, ...patch } : r)));
|
|
@@ -14173,31 +13970,39 @@ function KeyValueTable({
|
|
|
14173
13970
|
const add = () =>
|
|
14174
13971
|
onChange([...rows, { id: generateId(), key: "", value: "" }]);
|
|
14175
13972
|
|
|
13973
|
+
const getErrors = (row: KeyValuePair): RowErrors =>
|
|
13974
|
+
getRowErrors?.(row) ?? {};
|
|
13975
|
+
|
|
13976
|
+
// Reusable delete row action \u2014 same placement and styling as KeyValueRow / knowledge-base-card
|
|
13977
|
+
const deleteRowButtonClass =
|
|
13978
|
+
"text-semantic-text-muted hover:text-semantic-error-primary hover:bg-semantic-error-surface transition-colors shrink-0";
|
|
13979
|
+
|
|
14176
13980
|
return (
|
|
14177
13981
|
<div className="flex flex-col gap-1.5">
|
|
14178
13982
|
<span className="text-xs text-semantic-text-muted">{label}</span>
|
|
14179
13983
|
<div className="border border-semantic-border-layout rounded overflow-hidden">
|
|
14180
|
-
{/* Column headers \u2014 desktop only */}
|
|
13984
|
+
{/* Column headers \u2014 desktop only; border-r on Key cell defines column boundary */}
|
|
14181
13985
|
<div className="hidden sm:flex bg-semantic-bg-ui border-b border-semantic-border-layout">
|
|
14182
|
-
<div className="flex-1 px-3 py-2 text-xs font-semibold text-semantic-text-muted border-r border-semantic-border-layout">
|
|
13986
|
+
<div className="flex-1 min-w-0 px-3 py-2 text-xs font-semibold text-semantic-text-muted border-r border-semantic-border-layout">
|
|
14183
13987
|
Key
|
|
14184
13988
|
</div>
|
|
14185
|
-
<div className="flex-[2] px-3 py-2 text-xs font-semibold text-semantic-text-muted">
|
|
13989
|
+
<div className="flex-[2] min-w-0 px-3 py-2 text-xs font-semibold text-semantic-text-muted">
|
|
14186
13990
|
Value
|
|
14187
13991
|
</div>
|
|
14188
|
-
<div className="w-10 shrink-0" />
|
|
13992
|
+
<div className="w-10 shrink-0" aria-hidden="true" />
|
|
14189
13993
|
</div>
|
|
14190
13994
|
|
|
14191
|
-
{/* Filled rows */}
|
|
14192
|
-
{rows.map((row) =>
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
13995
|
+
{/* Filled rows \u2014 same flex ratio (flex-1 / flex-2 / w-10) so middle border aligns with header */}
|
|
13996
|
+
{rows.map((row) => {
|
|
13997
|
+
const errors = getErrors(row);
|
|
13998
|
+
return (
|
|
13999
|
+
<div
|
|
14000
|
+
key={row.id}
|
|
14001
|
+
className="border-b border-semantic-border-layout last:border-b-0 flex items-center min-h-0"
|
|
14002
|
+
>
|
|
14003
|
+
{/* Key column \u2014 border-r on column (not input) so it aligns with header */}
|
|
14004
|
+
<div className="flex-1 flex flex-col min-w-0 sm:border-r sm:border-semantic-border-layout">
|
|
14005
|
+
<span className="sm:hidden px-3 pt-2.5 pb-0.5 text-[10px] font-semibold text-semantic-text-muted uppercase tracking-wide">
|
|
14201
14006
|
Key
|
|
14202
14007
|
</span>
|
|
14203
14008
|
<input
|
|
@@ -14205,61 +14010,62 @@ function KeyValueTable({
|
|
|
14205
14010
|
value={row.key}
|
|
14206
14011
|
onChange={(e) => update(row.id, { key: e.target.value })}
|
|
14207
14012
|
placeholder="Key"
|
|
14208
|
-
|
|
14013
|
+
maxLength={getRowErrors ? QUERY_PARAM_KEY_MAX : undefined}
|
|
14014
|
+
className={cn(
|
|
14015
|
+
"w-full px-3 py-2.5 text-base text-semantic-text-primary placeholder:text-semantic-text-muted bg-semantic-bg-primary outline-none focus:bg-semantic-bg-hover",
|
|
14016
|
+
errors.key && "border-semantic-error-primary"
|
|
14017
|
+
)}
|
|
14018
|
+
aria-invalid={Boolean(errors.key)}
|
|
14019
|
+
aria-describedby={errors.key ? \`err-key-\${row.id}\` : undefined}
|
|
14209
14020
|
/>
|
|
14021
|
+
{errors.key && (
|
|
14022
|
+
<p id={\`err-key-\${row.id}\`} className="m-0 px-3 pt-0.5 text-xs text-semantic-error-primary">
|
|
14023
|
+
{errors.key}
|
|
14024
|
+
</p>
|
|
14025
|
+
)}
|
|
14210
14026
|
</div>
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14218
|
-
|
|
14219
|
-
|
|
14220
|
-
|
|
14221
|
-
|
|
14222
|
-
|
|
14223
|
-
|
|
14224
|
-
|
|
14225
|
-
|
|
14027
|
+
|
|
14028
|
+
{/* Value column */}
|
|
14029
|
+
<div className="flex-[2] flex flex-col min-w-0">
|
|
14030
|
+
<span className="sm:hidden px-3 pt-2.5 pb-0.5 text-[10px] font-semibold text-semantic-text-muted uppercase tracking-wide">
|
|
14031
|
+
Value
|
|
14032
|
+
</span>
|
|
14033
|
+
<input
|
|
14034
|
+
type="text"
|
|
14035
|
+
value={row.value}
|
|
14036
|
+
onChange={(e) => update(row.id, { value: e.target.value })}
|
|
14037
|
+
placeholder="Type {{ to add variables"
|
|
14038
|
+
maxLength={getRowErrors ? QUERY_PARAM_VALUE_MAX : undefined}
|
|
14039
|
+
className={cn(
|
|
14040
|
+
"w-full px-3 py-2.5 text-base text-semantic-text-primary placeholder:text-semantic-text-muted bg-semantic-bg-primary outline-none focus:bg-semantic-bg-hover",
|
|
14041
|
+
errors.value && "border-semantic-error-primary"
|
|
14042
|
+
)}
|
|
14043
|
+
aria-invalid={Boolean(errors.value)}
|
|
14044
|
+
aria-describedby={errors.value ? \`err-value-\${row.id}\` : undefined}
|
|
14045
|
+
/>
|
|
14046
|
+
{errors.value && (
|
|
14047
|
+
<p id={\`err-value-\${row.id}\`} className="m-0 px-3 pt-0.5 text-xs text-semantic-error-primary">
|
|
14048
|
+
{errors.value}
|
|
14049
|
+
</p>
|
|
14050
|
+
)}
|
|
14051
|
+
</div>
|
|
14052
|
+
|
|
14053
|
+
{/* Action column \u2014 delete aligned with row (same as KeyValueRow / knowledge-base-card) */}
|
|
14054
|
+
<div className="w-10 sm:w-10 flex items-center justify-center shrink-0 self-stretch border-l border-semantic-border-layout sm:border-l-0">
|
|
14055
|
+
<Button
|
|
14226
14056
|
type="button"
|
|
14057
|
+
variant="ghost"
|
|
14058
|
+
size="icon"
|
|
14227
14059
|
onClick={() => remove(row.id)}
|
|
14228
|
-
className="
|
|
14060
|
+
className={cn("rounded-md", deleteRowButtonClass)}
|
|
14229
14061
|
aria-label="Delete row"
|
|
14230
14062
|
>
|
|
14231
|
-
<Trash2 className="size-
|
|
14232
|
-
</
|
|
14063
|
+
<Trash2 className="size-4" />
|
|
14064
|
+
</Button>
|
|
14233
14065
|
</div>
|
|
14234
14066
|
</div>
|
|
14235
|
-
|
|
14236
|
-
|
|
14237
|
-
<div className="hidden sm:flex">
|
|
14238
|
-
<input
|
|
14239
|
-
type="text"
|
|
14240
|
-
value={row.key}
|
|
14241
|
-
onChange={(e) => update(row.id, { key: e.target.value })}
|
|
14242
|
-
placeholder="Key"
|
|
14243
|
-
className="flex-1 px-3 py-2.5 text-base text-semantic-text-primary placeholder:text-semantic-text-muted bg-semantic-bg-primary border-r border-semantic-border-layout outline-none focus:bg-semantic-bg-hover"
|
|
14244
|
-
/>
|
|
14245
|
-
<input
|
|
14246
|
-
type="text"
|
|
14247
|
-
value={row.value}
|
|
14248
|
-
onChange={(e) => update(row.id, { value: e.target.value })}
|
|
14249
|
-
placeholder="Type {{ to add variables"
|
|
14250
|
-
className="flex-[2] px-3 py-2.5 text-base text-semantic-text-primary placeholder:text-semantic-text-muted bg-semantic-bg-primary outline-none focus:bg-semantic-bg-hover"
|
|
14251
|
-
/>
|
|
14252
|
-
<button
|
|
14253
|
-
type="button"
|
|
14254
|
-
onClick={() => remove(row.id)}
|
|
14255
|
-
className="w-10 flex items-center justify-center text-semantic-text-muted hover:text-semantic-error-primary hover:bg-semantic-error-surface transition-colors shrink-0"
|
|
14256
|
-
aria-label="Delete row"
|
|
14257
|
-
>
|
|
14258
|
-
<Trash2 className="size-3.5" />
|
|
14259
|
-
</button>
|
|
14260
|
-
</div>
|
|
14261
|
-
</div>
|
|
14262
|
-
))}
|
|
14067
|
+
);
|
|
14068
|
+
})}
|
|
14263
14069
|
|
|
14264
14070
|
{/* Add row \u2014 always visible */}
|
|
14265
14071
|
<button
|
|
@@ -14286,6 +14092,8 @@ export const CreateFunctionModal = React.forwardRef<
|
|
|
14286
14092
|
onOpenChange,
|
|
14287
14093
|
onSubmit,
|
|
14288
14094
|
onTestApi,
|
|
14095
|
+
initialData,
|
|
14096
|
+
isEditing = false,
|
|
14289
14097
|
promptMinLength = 100,
|
|
14290
14098
|
promptMaxLength = 5000,
|
|
14291
14099
|
initialStep = 1,
|
|
@@ -14296,31 +14104,52 @@ export const CreateFunctionModal = React.forwardRef<
|
|
|
14296
14104
|
) => {
|
|
14297
14105
|
const [step, setStep] = React.useState<1 | 2>(initialStep);
|
|
14298
14106
|
|
|
14299
|
-
const [name, setName] = React.useState("");
|
|
14300
|
-
const [prompt, setPrompt] = React.useState("");
|
|
14107
|
+
const [name, setName] = React.useState(initialData?.name ?? "");
|
|
14108
|
+
const [prompt, setPrompt] = React.useState(initialData?.prompt ?? "");
|
|
14301
14109
|
|
|
14302
|
-
const [method, setMethod] = React.useState<HttpMethod>("GET");
|
|
14303
|
-
const [url, setUrl] = React.useState("");
|
|
14110
|
+
const [method, setMethod] = React.useState<HttpMethod>(initialData?.method ?? "GET");
|
|
14111
|
+
const [url, setUrl] = React.useState(initialData?.url ?? "");
|
|
14304
14112
|
const [activeTab, setActiveTab] =
|
|
14305
14113
|
React.useState<FunctionTabType>(initialTab);
|
|
14306
|
-
const [headers, setHeaders] = React.useState<KeyValuePair[]>([]);
|
|
14307
|
-
const [queryParams, setQueryParams] = React.useState<KeyValuePair[]>([]);
|
|
14308
|
-
const [body, setBody] = React.useState("");
|
|
14114
|
+
const [headers, setHeaders] = React.useState<KeyValuePair[]>(initialData?.headers ?? []);
|
|
14115
|
+
const [queryParams, setQueryParams] = React.useState<KeyValuePair[]>(initialData?.queryParams ?? []);
|
|
14116
|
+
const [body, setBody] = React.useState(initialData?.body ?? "");
|
|
14309
14117
|
const [apiResponse, setApiResponse] = React.useState("");
|
|
14310
14118
|
const [isTesting, setIsTesting] = React.useState(false);
|
|
14119
|
+
const [step2SubmitAttempted, setStep2SubmitAttempted] = React.useState(false);
|
|
14120
|
+
|
|
14121
|
+
// Sync form state from initialData each time the modal opens
|
|
14122
|
+
React.useEffect(() => {
|
|
14123
|
+
if (open) {
|
|
14124
|
+
setStep(initialStep);
|
|
14125
|
+
setName(initialData?.name ?? "");
|
|
14126
|
+
setPrompt(initialData?.prompt ?? "");
|
|
14127
|
+
setMethod(initialData?.method ?? "GET");
|
|
14128
|
+
setUrl(initialData?.url ?? "");
|
|
14129
|
+
setActiveTab(initialTab);
|
|
14130
|
+
setHeaders(initialData?.headers ?? []);
|
|
14131
|
+
setQueryParams(initialData?.queryParams ?? []);
|
|
14132
|
+
setBody(initialData?.body ?? "");
|
|
14133
|
+
setApiResponse("");
|
|
14134
|
+
setStep2SubmitAttempted(false);
|
|
14135
|
+
}
|
|
14136
|
+
// Re-run only when modal opens; intentionally exclude deep deps to avoid mid-session resets
|
|
14137
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
14138
|
+
}, [open]);
|
|
14311
14139
|
|
|
14312
14140
|
const reset = React.useCallback(() => {
|
|
14313
14141
|
setStep(initialStep);
|
|
14314
|
-
setName("");
|
|
14315
|
-
setPrompt("");
|
|
14316
|
-
setMethod("GET");
|
|
14317
|
-
setUrl("");
|
|
14142
|
+
setName(initialData?.name ?? "");
|
|
14143
|
+
setPrompt(initialData?.prompt ?? "");
|
|
14144
|
+
setMethod(initialData?.method ?? "GET");
|
|
14145
|
+
setUrl(initialData?.url ?? "");
|
|
14318
14146
|
setActiveTab(initialTab);
|
|
14319
|
-
setHeaders([]);
|
|
14320
|
-
setQueryParams([]);
|
|
14321
|
-
setBody("");
|
|
14147
|
+
setHeaders(initialData?.headers ?? []);
|
|
14148
|
+
setQueryParams(initialData?.queryParams ?? []);
|
|
14149
|
+
setBody(initialData?.body ?? "");
|
|
14322
14150
|
setApiResponse("");
|
|
14323
|
-
|
|
14151
|
+
setStep2SubmitAttempted(false);
|
|
14152
|
+
}, [initialData, initialStep, initialTab]);
|
|
14324
14153
|
|
|
14325
14154
|
const handleClose = React.useCallback(() => {
|
|
14326
14155
|
reset();
|
|
@@ -14340,7 +14169,21 @@ export const CreateFunctionModal = React.forwardRef<
|
|
|
14340
14169
|
if (name.trim() && prompt.trim().length >= promptMinLength) setStep(2);
|
|
14341
14170
|
};
|
|
14342
14171
|
|
|
14172
|
+
const queryParamsHaveErrors = (rows: KeyValuePair[]): boolean =>
|
|
14173
|
+
rows.some((row) => {
|
|
14174
|
+
const hasInput = row.key.trim() !== "" || row.value.trim() !== "";
|
|
14175
|
+
if (!hasInput) return false;
|
|
14176
|
+
return (
|
|
14177
|
+
validateQueryParamKey(row.key) !== undefined ||
|
|
14178
|
+
validateQueryParamValue(row.value) !== undefined
|
|
14179
|
+
);
|
|
14180
|
+
});
|
|
14181
|
+
|
|
14343
14182
|
const handleSubmit = () => {
|
|
14183
|
+
if (step === 2) {
|
|
14184
|
+
setStep2SubmitAttempted(true);
|
|
14185
|
+
if (queryParamsHaveErrors(queryParams)) return;
|
|
14186
|
+
}
|
|
14344
14187
|
const data: CreateFunctionData = {
|
|
14345
14188
|
name: name.trim(),
|
|
14346
14189
|
prompt: prompt.trim(),
|
|
@@ -14400,7 +14243,7 @@ export const CreateFunctionModal = React.forwardRef<
|
|
|
14400
14243
|
{/* \u2500\u2500 Header \u2500\u2500 */}
|
|
14401
14244
|
<div className="flex items-center justify-between px-4 py-4 border-b border-semantic-border-layout shrink-0 sm:px-6">
|
|
14402
14245
|
<DialogTitle className="text-base font-semibold text-semantic-text-primary">
|
|
14403
|
-
Create Function
|
|
14246
|
+
{isEditing ? "Edit Function" : "Create Function"}
|
|
14404
14247
|
</DialogTitle>
|
|
14405
14248
|
<button
|
|
14406
14249
|
type="button"
|
|
@@ -14558,6 +14401,14 @@ export const CreateFunctionModal = React.forwardRef<
|
|
|
14558
14401
|
rows={queryParams}
|
|
14559
14402
|
onChange={setQueryParams}
|
|
14560
14403
|
label="Query parameter"
|
|
14404
|
+
getRowErrors={(row) => {
|
|
14405
|
+
const hasInput = row.key.trim() !== "" || row.value.trim() !== "";
|
|
14406
|
+
if (!hasInput && !step2SubmitAttempted) return {};
|
|
14407
|
+
return {
|
|
14408
|
+
key: validateQueryParamKey(row.key),
|
|
14409
|
+
value: validateQueryParamValue(row.value),
|
|
14410
|
+
};
|
|
14411
|
+
}}
|
|
14561
14412
|
/>
|
|
14562
14413
|
)}
|
|
14563
14414
|
{activeTab === "body" && (
|
|
@@ -15020,8 +14871,12 @@ export interface BotBehaviorCardProps {
|
|
|
15020
14871
|
data: Partial<BotBehaviorData>;
|
|
15021
14872
|
/** Callback when any field changes */
|
|
15022
14873
|
onChange: (patch: Partial<BotBehaviorData>) => void;
|
|
14874
|
+
/** Called when the system prompt textarea loses focus */
|
|
14875
|
+
onSystemPromptBlur?: (value: string) => void;
|
|
15023
14876
|
/** Session variables shown as insertable chips */
|
|
15024
14877
|
sessionVariables?: string[];
|
|
14878
|
+
/** Maximum character length for the system prompt textarea (default: 25000) */
|
|
14879
|
+
maxLength?: number;
|
|
15025
14880
|
/** Disables all fields in the card (view mode) */
|
|
15026
14881
|
disabled?: boolean;
|
|
15027
14882
|
/** Additional className for the card */
|
|
@@ -15069,6 +14924,7 @@ function StyledTextarea({
|
|
|
15069
14924
|
value,
|
|
15070
14925
|
rows = 3,
|
|
15071
14926
|
onChange,
|
|
14927
|
+
onBlur,
|
|
15072
14928
|
disabled,
|
|
15073
14929
|
className,
|
|
15074
14930
|
}: {
|
|
@@ -15076,6 +14932,7 @@ function StyledTextarea({
|
|
|
15076
14932
|
value?: string;
|
|
15077
14933
|
rows?: number;
|
|
15078
14934
|
onChange?: (v: string) => void;
|
|
14935
|
+
onBlur?: (v: string) => void;
|
|
15079
14936
|
disabled?: boolean;
|
|
15080
14937
|
className?: string;
|
|
15081
14938
|
}) {
|
|
@@ -15084,6 +14941,7 @@ function StyledTextarea({
|
|
|
15084
14941
|
value={value ?? ""}
|
|
15085
14942
|
rows={rows}
|
|
15086
14943
|
onChange={(e) => onChange?.(e.target.value)}
|
|
14944
|
+
onBlur={(e) => onBlur?.(e.target.value)}
|
|
15087
14945
|
placeholder={placeholder}
|
|
15088
14946
|
disabled={disabled}
|
|
15089
14947
|
className={cn(
|
|
@@ -15106,14 +14964,16 @@ const BotBehaviorCard = React.forwardRef<HTMLDivElement, BotBehaviorCardProps>(
|
|
|
15106
14964
|
{
|
|
15107
14965
|
data,
|
|
15108
14966
|
onChange,
|
|
14967
|
+
onSystemPromptBlur,
|
|
15109
14968
|
sessionVariables = DEFAULT_SESSION_VARIABLES,
|
|
14969
|
+
maxLength = 25000,
|
|
15110
14970
|
disabled,
|
|
15111
14971
|
className,
|
|
15112
14972
|
},
|
|
15113
14973
|
ref
|
|
15114
14974
|
) => {
|
|
15115
14975
|
const prompt = data.systemPrompt ?? "";
|
|
15116
|
-
const MAX =
|
|
14976
|
+
const MAX = maxLength;
|
|
15117
14977
|
|
|
15118
14978
|
const insertVariable = (variable: string) => {
|
|
15119
14979
|
onChange({ systemPrompt: prompt + variable });
|
|
@@ -15133,6 +14993,7 @@ const BotBehaviorCard = React.forwardRef<HTMLDivElement, BotBehaviorCardProps>(
|
|
|
15133
14993
|
onChange={(v) => {
|
|
15134
14994
|
if (v.length <= MAX) onChange({ systemPrompt: v });
|
|
15135
14995
|
}}
|
|
14996
|
+
onBlur={onSystemPromptBlur}
|
|
15136
14997
|
placeholder="You are a helpful assistant. Always start by greeting the user politely: 'Hello! Welcome. How can I assist you today?'"
|
|
15137
14998
|
disabled={disabled}
|
|
15138
14999
|
className="pb-8"
|
|
@@ -15178,6 +15039,13 @@ export { BotBehaviorCard };
|
|
|
15178
15039
|
import { Download, Trash2, Plus, Info } from "lucide-react";
|
|
15179
15040
|
import { cn } from "../../../lib/utils";
|
|
15180
15041
|
import { Badge } from "../badge";
|
|
15042
|
+
import {
|
|
15043
|
+
Tooltip,
|
|
15044
|
+
TooltipContent,
|
|
15045
|
+
TooltipProvider,
|
|
15046
|
+
TooltipTrigger,
|
|
15047
|
+
} from "../tooltip";
|
|
15048
|
+
import { BOT_KNOWLEDGE_STATUS } from "./types";
|
|
15181
15049
|
import type { KnowledgeBaseFile } from "./types";
|
|
15182
15050
|
|
|
15183
15051
|
// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -15191,6 +15059,8 @@ export interface KnowledgeBaseCardProps {
|
|
|
15191
15059
|
onDownload?: (id: string) => void;
|
|
15192
15060
|
/** Called when user clicks the delete button on a file */
|
|
15193
15061
|
onDelete?: (id: string) => void;
|
|
15062
|
+
/** Hover text shown on the info icon next to the "Knowledge Base" title */
|
|
15063
|
+
infoTooltip?: string;
|
|
15194
15064
|
/** Disables all interactive elements in the card (view mode) */
|
|
15195
15065
|
disabled?: boolean;
|
|
15196
15066
|
/** Additional className */
|
|
@@ -15199,13 +15069,13 @@ export interface KnowledgeBaseCardProps {
|
|
|
15199
15069
|
|
|
15200
15070
|
// \u2500\u2500\u2500 Status config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
15201
15071
|
|
|
15202
|
-
type BadgeVariant = "active" | "destructive";
|
|
15203
|
-
const STATUS_CONFIG: Record<
|
|
15204
|
-
{
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15072
|
+
type BadgeVariant = "default" | "active" | "destructive";
|
|
15073
|
+
const STATUS_CONFIG: Record<BOT_KNOWLEDGE_STATUS, { label: string; variant: BadgeVariant }> = {
|
|
15074
|
+
[BOT_KNOWLEDGE_STATUS.PENDING]: { label: "Pending", variant: "default" },
|
|
15075
|
+
[BOT_KNOWLEDGE_STATUS.READY]: { label: "Ready", variant: "active" },
|
|
15076
|
+
[BOT_KNOWLEDGE_STATUS.PROCESSING]: { label: "Processing", variant: "active" },
|
|
15077
|
+
[BOT_KNOWLEDGE_STATUS.FAILED]: { label: "Failed", variant: "destructive" },
|
|
15078
|
+
};
|
|
15209
15079
|
|
|
15210
15080
|
// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
15211
15081
|
|
|
@@ -15216,6 +15086,7 @@ const KnowledgeBaseCard = React.forwardRef<HTMLDivElement, KnowledgeBaseCardProp
|
|
|
15216
15086
|
onAdd,
|
|
15217
15087
|
onDownload,
|
|
15218
15088
|
onDelete,
|
|
15089
|
+
infoTooltip,
|
|
15219
15090
|
disabled,
|
|
15220
15091
|
className,
|
|
15221
15092
|
},
|
|
@@ -15235,7 +15106,18 @@ const KnowledgeBaseCard = React.forwardRef<HTMLDivElement, KnowledgeBaseCardProp
|
|
|
15235
15106
|
<h2 className="m-0 text-base font-semibold text-semantic-text-primary">
|
|
15236
15107
|
Knowledge Base
|
|
15237
15108
|
</h2>
|
|
15238
|
-
|
|
15109
|
+
{infoTooltip ? (
|
|
15110
|
+
<TooltipProvider delayDuration={200}>
|
|
15111
|
+
<Tooltip>
|
|
15112
|
+
<TooltipTrigger asChild>
|
|
15113
|
+
<Info className="size-3.5 text-semantic-text-muted shrink-0 cursor-help" />
|
|
15114
|
+
</TooltipTrigger>
|
|
15115
|
+
<TooltipContent>{infoTooltip}</TooltipContent>
|
|
15116
|
+
</Tooltip>
|
|
15117
|
+
</TooltipProvider>
|
|
15118
|
+
) : (
|
|
15119
|
+
<Info className="size-3.5 text-semantic-text-muted shrink-0" />
|
|
15120
|
+
)}
|
|
15239
15121
|
</div>
|
|
15240
15122
|
<button
|
|
15241
15123
|
type="button"
|
|
@@ -15256,7 +15138,7 @@ const KnowledgeBaseCard = React.forwardRef<HTMLDivElement, KnowledgeBaseCardProp
|
|
|
15256
15138
|
) : (
|
|
15257
15139
|
<div className="flex flex-col divide-y divide-semantic-border-layout">
|
|
15258
15140
|
{files.map((file) => {
|
|
15259
|
-
const status = STATUS_CONFIG[file.status] ?? STATUS_CONFIG.
|
|
15141
|
+
const status = STATUS_CONFIG[file.status] ?? STATUS_CONFIG[BOT_KNOWLEDGE_STATUS.PENDING];
|
|
15260
15142
|
return (
|
|
15261
15143
|
<div
|
|
15262
15144
|
key={file.id}
|
|
@@ -15790,6 +15672,164 @@ const AdvancedSettingsCard = React.forwardRef<HTMLDivElement, AdvancedSettingsCa
|
|
|
15790
15672
|
AdvancedSettingsCard.displayName = "AdvancedSettingsCard";
|
|
15791
15673
|
|
|
15792
15674
|
export { AdvancedSettingsCard };
|
|
15675
|
+
`, prefix)
|
|
15676
|
+
},
|
|
15677
|
+
{
|
|
15678
|
+
name: "fallback-prompts-card.tsx",
|
|
15679
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
15680
|
+
import { Info } from "lucide-react";
|
|
15681
|
+
import { cn } from "../../../lib/utils";
|
|
15682
|
+
import {
|
|
15683
|
+
Accordion,
|
|
15684
|
+
AccordionItem,
|
|
15685
|
+
AccordionTrigger,
|
|
15686
|
+
AccordionContent,
|
|
15687
|
+
} from "../accordion";
|
|
15688
|
+
|
|
15689
|
+
// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
15690
|
+
|
|
15691
|
+
export interface FallbackPromptsData {
|
|
15692
|
+
agentBusyPrompt: string;
|
|
15693
|
+
noExtensionFoundPrompt: string;
|
|
15694
|
+
}
|
|
15695
|
+
|
|
15696
|
+
export interface FallbackPromptsCardProps {
|
|
15697
|
+
/** Current prompt text values */
|
|
15698
|
+
data: Partial<FallbackPromptsData>;
|
|
15699
|
+
/** Callback when any field changes */
|
|
15700
|
+
onChange: (patch: Partial<FallbackPromptsData>) => void;
|
|
15701
|
+
/** Called when the Agent Busy Prompt textarea loses focus */
|
|
15702
|
+
onAgentBusyPromptBlur?: (value: string) => void;
|
|
15703
|
+
/** Called when the No Extension Found textarea loses focus */
|
|
15704
|
+
onNoExtensionFoundPromptBlur?: (value: string) => void;
|
|
15705
|
+
/** Maximum character length for each prompt field (default: 25000) */
|
|
15706
|
+
maxLength?: number;
|
|
15707
|
+
/** Disables all fields in the card (view mode) */
|
|
15708
|
+
disabled?: boolean;
|
|
15709
|
+
/** Opens the accordion by default (default: false) */
|
|
15710
|
+
defaultOpen?: boolean;
|
|
15711
|
+
/** Additional className */
|
|
15712
|
+
className?: string;
|
|
15713
|
+
}
|
|
15714
|
+
|
|
15715
|
+
// \u2500\u2500\u2500 Internal helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
15716
|
+
|
|
15717
|
+
function PromptField({
|
|
15718
|
+
label,
|
|
15719
|
+
value,
|
|
15720
|
+
placeholder,
|
|
15721
|
+
maxLength,
|
|
15722
|
+
disabled,
|
|
15723
|
+
onChange,
|
|
15724
|
+
onBlur,
|
|
15725
|
+
rows = 2,
|
|
15726
|
+
}: {
|
|
15727
|
+
label: string;
|
|
15728
|
+
value: string;
|
|
15729
|
+
placeholder?: string;
|
|
15730
|
+
maxLength: number;
|
|
15731
|
+
disabled?: boolean;
|
|
15732
|
+
onChange: (value: string) => void;
|
|
15733
|
+
onBlur?: (value: string) => void;
|
|
15734
|
+
rows?: number;
|
|
15735
|
+
}) {
|
|
15736
|
+
return (
|
|
15737
|
+
<div className="flex flex-col gap-1.5">
|
|
15738
|
+
<label className="text-sm font-semibold text-semantic-text-secondary tracking-[0.014px]">
|
|
15739
|
+
{label}
|
|
15740
|
+
</label>
|
|
15741
|
+
<textarea
|
|
15742
|
+
value={value}
|
|
15743
|
+
placeholder={placeholder}
|
|
15744
|
+
maxLength={maxLength}
|
|
15745
|
+
disabled={disabled}
|
|
15746
|
+
rows={rows}
|
|
15747
|
+
onChange={(e) => onChange(e.target.value)}
|
|
15748
|
+
onBlur={(e) => onBlur?.(e.target.value)}
|
|
15749
|
+
className={cn(
|
|
15750
|
+
"w-full resize-none rounded border border-semantic-border-layout bg-semantic-bg-primary px-3 py-2.5 text-base text-semantic-text-primary placeholder:text-semantic-text-muted outline-none transition-all",
|
|
15751
|
+
"focus:outline-none focus:border-semantic-border-input-focus/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
15752
|
+
disabled && "cursor-not-allowed opacity-50"
|
|
15753
|
+
)}
|
|
15754
|
+
/>
|
|
15755
|
+
<p className="m-0 text-xs text-semantic-text-muted text-right">
|
|
15756
|
+
{value.length}/{maxLength}
|
|
15757
|
+
</p>
|
|
15758
|
+
</div>
|
|
15759
|
+
);
|
|
15760
|
+
}
|
|
15761
|
+
|
|
15762
|
+
// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
15763
|
+
|
|
15764
|
+
const FallbackPromptsCard = React.forwardRef<
|
|
15765
|
+
HTMLDivElement,
|
|
15766
|
+
FallbackPromptsCardProps
|
|
15767
|
+
>(
|
|
15768
|
+
(
|
|
15769
|
+
{
|
|
15770
|
+
data,
|
|
15771
|
+
onChange,
|
|
15772
|
+
onAgentBusyPromptBlur,
|
|
15773
|
+
onNoExtensionFoundPromptBlur,
|
|
15774
|
+
maxLength = 25000,
|
|
15775
|
+
disabled,
|
|
15776
|
+
defaultOpen = false,
|
|
15777
|
+
className,
|
|
15778
|
+
},
|
|
15779
|
+
ref
|
|
15780
|
+
) => {
|
|
15781
|
+
return (
|
|
15782
|
+
<div
|
|
15783
|
+
ref={ref}
|
|
15784
|
+
className={cn(
|
|
15785
|
+
"bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden",
|
|
15786
|
+
className
|
|
15787
|
+
)}
|
|
15788
|
+
>
|
|
15789
|
+
<Accordion
|
|
15790
|
+
type="single"
|
|
15791
|
+
defaultValue={defaultOpen ? ["fallback"] : []}
|
|
15792
|
+
>
|
|
15793
|
+
<AccordionItem value="fallback">
|
|
15794
|
+
<AccordionTrigger className="px-4 py-4 border-b border-semantic-border-layout hover:no-underline sm:px-6 sm:py-5">
|
|
15795
|
+
<span className="flex items-center gap-1.5 text-base font-semibold text-semantic-text-primary">
|
|
15796
|
+
Fallback Prompts
|
|
15797
|
+
<Info className="size-3.5 text-semantic-text-muted shrink-0" />
|
|
15798
|
+
</span>
|
|
15799
|
+
</AccordionTrigger>
|
|
15800
|
+
<AccordionContent>
|
|
15801
|
+
<div className="flex flex-col gap-6 px-4 pt-4 sm:px-6 sm:pt-5">
|
|
15802
|
+
<PromptField
|
|
15803
|
+
label="Agent Busy Prompt"
|
|
15804
|
+
value={data.agentBusyPrompt ?? ""}
|
|
15805
|
+
placeholder="Executives are busy at the moment, we will connect you soon."
|
|
15806
|
+
maxLength={maxLength}
|
|
15807
|
+
disabled={disabled}
|
|
15808
|
+
onChange={(v) => onChange({ agentBusyPrompt: v })}
|
|
15809
|
+
onBlur={onAgentBusyPromptBlur}
|
|
15810
|
+
rows={2}
|
|
15811
|
+
/>
|
|
15812
|
+
<PromptField
|
|
15813
|
+
label="No Extension Found"
|
|
15814
|
+
value={data.noExtensionFoundPrompt ?? ""}
|
|
15815
|
+
placeholder="Sorry, the requested extension is currently unavailable. Let me help you directly."
|
|
15816
|
+
maxLength={maxLength}
|
|
15817
|
+
disabled={disabled}
|
|
15818
|
+
onChange={(v) => onChange({ noExtensionFoundPrompt: v })}
|
|
15819
|
+
onBlur={onNoExtensionFoundPromptBlur}
|
|
15820
|
+
rows={4}
|
|
15821
|
+
/>
|
|
15822
|
+
</div>
|
|
15823
|
+
</AccordionContent>
|
|
15824
|
+
</AccordionItem>
|
|
15825
|
+
</Accordion>
|
|
15826
|
+
</div>
|
|
15827
|
+
);
|
|
15828
|
+
}
|
|
15829
|
+
);
|
|
15830
|
+
FallbackPromptsCard.displayName = "FallbackPromptsCard";
|
|
15831
|
+
|
|
15832
|
+
export { FallbackPromptsCard };
|
|
15793
15833
|
`, prefix)
|
|
15794
15834
|
},
|
|
15795
15835
|
{
|
|
@@ -15800,7 +15840,16 @@ export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
|
15800
15840
|
|
|
15801
15841
|
export type FunctionTabType = "header" | "queryParams" | "body";
|
|
15802
15842
|
|
|
15803
|
-
export
|
|
15843
|
+
export const BOT_KNOWLEDGE_STATUS = {
|
|
15844
|
+
PENDING: "pending",
|
|
15845
|
+
READY: "ready",
|
|
15846
|
+
PROCESSING: "processing",
|
|
15847
|
+
FAILED: "failed",
|
|
15848
|
+
} as const;
|
|
15849
|
+
|
|
15850
|
+
export type BOT_KNOWLEDGE_STATUS = typeof BOT_KNOWLEDGE_STATUS[keyof typeof BOT_KNOWLEDGE_STATUS];
|
|
15851
|
+
|
|
15852
|
+
export type KnowledgeFileStatus = BOT_KNOWLEDGE_STATUS;
|
|
15804
15853
|
|
|
15805
15854
|
export interface KeyValuePair {
|
|
15806
15855
|
id: string;
|
|
@@ -15844,6 +15893,10 @@ export interface CreateFunctionModalProps {
|
|
|
15844
15893
|
onOpenChange: (open: boolean) => void;
|
|
15845
15894
|
onSubmit?: (data: CreateFunctionData) => void;
|
|
15846
15895
|
onTestApi?: (step2: CreateFunctionStep2Data) => Promise<string>;
|
|
15896
|
+
/** Pre-fills all fields \u2014 use when opening the modal to edit an existing function */
|
|
15897
|
+
initialData?: Partial<CreateFunctionData>;
|
|
15898
|
+
/** When true, changes the modal title to "Edit Function" */
|
|
15899
|
+
isEditing?: boolean;
|
|
15847
15900
|
/** Minimum character length for the prompt field (default: 100) */
|
|
15848
15901
|
promptMinLength?: number;
|
|
15849
15902
|
/** Maximum character length for the prompt field (default: 5000) */
|
|
@@ -15895,17 +15948,32 @@ export interface IvrBotConfigProps {
|
|
|
15895
15948
|
onDownloadKnowledgeFile?: (fileId: string) => void;
|
|
15896
15949
|
onDeleteKnowledgeFile?: (fileId: string) => void;
|
|
15897
15950
|
onCreateFunction?: (data: CreateFunctionData) => void;
|
|
15898
|
-
/** Called when user edits a custom function */
|
|
15951
|
+
/** Called when user edits a custom function. Receives the function id. */
|
|
15899
15952
|
onEditFunction?: (id: string) => void;
|
|
15900
15953
|
/** Called when user deletes a custom function */
|
|
15901
15954
|
onDeleteFunction?: (id: string) => void;
|
|
15902
15955
|
onTestApi?: (step2: CreateFunctionStep2Data) => Promise<string>;
|
|
15903
15956
|
/** Hover text for the info icon in the Functions card header */
|
|
15904
15957
|
functionsInfoTooltip?: string;
|
|
15958
|
+
/** Hover text for the info icon in the Knowledge Base card header */
|
|
15959
|
+
knowledgeBaseInfoTooltip?: string;
|
|
15905
15960
|
/** Minimum character length for the function prompt (default: 100) */
|
|
15906
15961
|
functionPromptMinLength?: number;
|
|
15907
15962
|
/** Maximum character length for the function prompt (default: 5000) */
|
|
15908
15963
|
functionPromptMaxLength?: number;
|
|
15964
|
+
/**
|
|
15965
|
+
* Pre-filled data shown when the edit function modal opens.
|
|
15966
|
+
* Pass when your app fetches full function data after onEditFunction fires.
|
|
15967
|
+
*/
|
|
15968
|
+
functionEditData?: Partial<CreateFunctionData>;
|
|
15969
|
+
/** Max character length for the "How It Behaves" system prompt (default: 25000) */
|
|
15970
|
+
systemPromptMaxLength?: number;
|
|
15971
|
+
/** Called when the system prompt textarea loses focus */
|
|
15972
|
+
onSystemPromptBlur?: (value: string) => void;
|
|
15973
|
+
/** Called when the Agent Busy Prompt textarea loses focus */
|
|
15974
|
+
onAgentBusyPromptBlur?: (value: string) => void;
|
|
15975
|
+
/** Called when the No Extension Found textarea loses focus */
|
|
15976
|
+
onNoExtensionFoundPromptBlur?: (value: string) => void;
|
|
15909
15977
|
onBack?: () => void;
|
|
15910
15978
|
/** Called when the play icon is clicked on a voice option */
|
|
15911
15979
|
onPlayVoice?: (voiceValue: string) => void;
|
|
@@ -15952,6 +16020,12 @@ export { KnowledgeBaseCard } from "./knowledge-base-card";
|
|
|
15952
16020
|
export { FunctionsCard } from "./functions-card";
|
|
15953
16021
|
export { FrustrationHandoverCard } from "./frustration-handover-card";
|
|
15954
16022
|
export { AdvancedSettingsCard } from "./advanced-settings-card";
|
|
16023
|
+
export { FallbackPromptsCard } from "./fallback-prompts-card";
|
|
16024
|
+
export type {
|
|
16025
|
+
FallbackPromptsData,
|
|
16026
|
+
FallbackPromptsCardProps,
|
|
16027
|
+
} from "./fallback-prompts-card";
|
|
16028
|
+
export { BOT_KNOWLEDGE_STATUS } from "./types";
|
|
15955
16029
|
export { CreateFunctionModal } from "./create-function-modal";
|
|
15956
16030
|
export { FileUploadModal } from "../file-upload-modal";
|
|
15957
16031
|
export { IvrBotConfig } from "./ivr-bot-config";
|
|
@@ -16631,159 +16705,6 @@ export interface WalletTopupProps {
|
|
|
16631
16705
|
name: "index.ts",
|
|
16632
16706
|
content: prefixTailwindClasses(`export { WalletTopup } from "./wallet-topup";
|
|
16633
16707
|
export type { WalletTopupProps, AmountOption } from "./types";
|
|
16634
|
-
`, prefix)
|
|
16635
|
-
}
|
|
16636
|
-
]
|
|
16637
|
-
},
|
|
16638
|
-
"fallback-prompts-panel": {
|
|
16639
|
-
name: "fallback-prompts-panel",
|
|
16640
|
-
description: "An accordion card for configuring IVR bot fallback prompt messages (agent busy and no extension found)",
|
|
16641
|
-
category: "custom",
|
|
16642
|
-
dependencies: [
|
|
16643
|
-
"clsx",
|
|
16644
|
-
"tailwind-merge",
|
|
16645
|
-
"lucide-react"
|
|
16646
|
-
],
|
|
16647
|
-
internalDependencies: [
|
|
16648
|
-
"accordion",
|
|
16649
|
-
"textarea"
|
|
16650
|
-
],
|
|
16651
|
-
isMultiFile: true,
|
|
16652
|
-
directory: "fallback-prompts-panel",
|
|
16653
|
-
mainFile: "fallback-prompts-panel.tsx",
|
|
16654
|
-
files: [
|
|
16655
|
-
{
|
|
16656
|
-
name: "fallback-prompts-panel.tsx",
|
|
16657
|
-
content: prefixTailwindClasses(`import * as React from "react";
|
|
16658
|
-
import { Info } from "lucide-react";
|
|
16659
|
-
import { cn } from "../../../lib/utils";
|
|
16660
|
-
import { TextArea } from "../textarea";
|
|
16661
|
-
import {
|
|
16662
|
-
Accordion,
|
|
16663
|
-
AccordionItem,
|
|
16664
|
-
AccordionTrigger,
|
|
16665
|
-
AccordionContent,
|
|
16666
|
-
} from "../accordion";
|
|
16667
|
-
import type { FallbackPromptsBaseProps } from "./types";
|
|
16668
|
-
|
|
16669
|
-
export interface FallbackPromptsPanelProps
|
|
16670
|
-
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">,
|
|
16671
|
-
FallbackPromptsBaseProps {}
|
|
16672
|
-
|
|
16673
|
-
const AGENT_BUSY_PLACEHOLDER =
|
|
16674
|
-
"Executives are busy at the moment, we will connect you soon.";
|
|
16675
|
-
const NO_EXTENSION_PLACEHOLDER =
|
|
16676
|
-
"Sorry, the requested extension is currently unavailable. Let me help you directly.";
|
|
16677
|
-
|
|
16678
|
-
const FallbackPromptsPanel = React.forwardRef<
|
|
16679
|
-
HTMLDivElement,
|
|
16680
|
-
FallbackPromptsPanelProps
|
|
16681
|
-
>(
|
|
16682
|
-
(
|
|
16683
|
-
{
|
|
16684
|
-
data,
|
|
16685
|
-
onChange,
|
|
16686
|
-
tooltipContent = "Configure the messages your bot speaks when it cannot reach an agent or extension.",
|
|
16687
|
-
defaultOpen = true,
|
|
16688
|
-
disabled,
|
|
16689
|
-
className,
|
|
16690
|
-
...rest
|
|
16691
|
-
},
|
|
16692
|
-
ref
|
|
16693
|
-
) => {
|
|
16694
|
-
return (
|
|
16695
|
-
<div
|
|
16696
|
-
ref={ref}
|
|
16697
|
-
className={cn(
|
|
16698
|
-
"bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden",
|
|
16699
|
-
className
|
|
16700
|
-
)}
|
|
16701
|
-
{...rest}
|
|
16702
|
-
>
|
|
16703
|
-
<Accordion
|
|
16704
|
-
type="single"
|
|
16705
|
-
defaultValue={defaultOpen ? ["fallback-prompts"] : []}
|
|
16706
|
-
>
|
|
16707
|
-
<AccordionItem value="fallback-prompts">
|
|
16708
|
-
<AccordionTrigger className="px-4 py-4 border-b border-semantic-border-layout hover:no-underline hover:bg-transparent sm:px-6 sm:py-5">
|
|
16709
|
-
<div className="flex items-center gap-1.5">
|
|
16710
|
-
<span className="text-base font-semibold text-semantic-text-primary">
|
|
16711
|
-
Fallback Prompts
|
|
16712
|
-
</span>
|
|
16713
|
-
<span
|
|
16714
|
-
title={tooltipContent}
|
|
16715
|
-
className="flex items-center text-semantic-text-muted cursor-default"
|
|
16716
|
-
aria-label={tooltipContent}
|
|
16717
|
-
>
|
|
16718
|
-
<Info className="size-3.5" />
|
|
16719
|
-
</span>
|
|
16720
|
-
</div>
|
|
16721
|
-
</AccordionTrigger>
|
|
16722
|
-
|
|
16723
|
-
<AccordionContent>
|
|
16724
|
-
<div className="flex flex-col gap-4 px-4 pt-4 sm:gap-6 sm:px-6 sm:pt-6">
|
|
16725
|
-
<TextArea
|
|
16726
|
-
label="Agent Busy Prompt"
|
|
16727
|
-
placeholder={AGENT_BUSY_PLACEHOLDER}
|
|
16728
|
-
value={data.agentBusyPrompt ?? ""}
|
|
16729
|
-
onChange={(e) =>
|
|
16730
|
-
onChange({ agentBusyPrompt: e.target.value })
|
|
16731
|
-
}
|
|
16732
|
-
rows={3}
|
|
16733
|
-
disabled={disabled}
|
|
16734
|
-
/>
|
|
16735
|
-
|
|
16736
|
-
<TextArea
|
|
16737
|
-
label="No Extension Found"
|
|
16738
|
-
placeholder={NO_EXTENSION_PLACEHOLDER}
|
|
16739
|
-
value={data.noExtensionFound ?? ""}
|
|
16740
|
-
onChange={(e) =>
|
|
16741
|
-
onChange({ noExtensionFound: e.target.value })
|
|
16742
|
-
}
|
|
16743
|
-
rows={3}
|
|
16744
|
-
disabled={disabled}
|
|
16745
|
-
/>
|
|
16746
|
-
</div>
|
|
16747
|
-
</AccordionContent>
|
|
16748
|
-
</AccordionItem>
|
|
16749
|
-
</Accordion>
|
|
16750
|
-
</div>
|
|
16751
|
-
);
|
|
16752
|
-
}
|
|
16753
|
-
);
|
|
16754
|
-
FallbackPromptsPanel.displayName = "FallbackPromptsPanel";
|
|
16755
|
-
|
|
16756
|
-
export { FallbackPromptsPanel };
|
|
16757
|
-
`, prefix)
|
|
16758
|
-
},
|
|
16759
|
-
{
|
|
16760
|
-
name: "types.ts",
|
|
16761
|
-
content: prefixTailwindClasses(`export interface FallbackPromptsData {
|
|
16762
|
-
/** Text spoken when all agents are busy */
|
|
16763
|
-
agentBusyPrompt: string;
|
|
16764
|
-
/** Text spoken when a dialed extension is not found */
|
|
16765
|
-
noExtensionFound: string;
|
|
16766
|
-
}
|
|
16767
|
-
|
|
16768
|
-
export interface FallbackPromptsBaseProps {
|
|
16769
|
-
/** Current prompt values */
|
|
16770
|
-
data: Partial<FallbackPromptsData>;
|
|
16771
|
-
/** Callback fired when any prompt value changes */
|
|
16772
|
-
onChange: (patch: Partial<FallbackPromptsData>) => void;
|
|
16773
|
-
/** Tooltip content shown next to the title */
|
|
16774
|
-
tooltipContent?: string;
|
|
16775
|
-
/** Opens the panel expanded by default */
|
|
16776
|
-
defaultOpen?: boolean;
|
|
16777
|
-
/** Disables all textareas */
|
|
16778
|
-
disabled?: boolean;
|
|
16779
|
-
}
|
|
16780
|
-
`, prefix)
|
|
16781
|
-
},
|
|
16782
|
-
{
|
|
16783
|
-
name: "index.ts",
|
|
16784
|
-
content: prefixTailwindClasses(`export { FallbackPromptsPanel } from "./fallback-prompts-panel";
|
|
16785
|
-
export type { FallbackPromptsPanelProps } from "./fallback-prompts-panel";
|
|
16786
|
-
export type { FallbackPromptsData, FallbackPromptsBaseProps } from "./types";
|
|
16787
16708
|
`, prefix)
|
|
16788
16709
|
}
|
|
16789
16710
|
]
|