import React from "react";
import { Download, Info, Loader2, Pencil, Plus, Trash2, Upload } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { Switch } from "@/components/ui/switch";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import type { AiBuilderAgentConfig, AiBuilderRuleItem } from "./types";
// ---------------------------------------------------------------------------
// SectionHeader
// ---------------------------------------------------------------------------
export type SectionHeaderProps = {
title: string;
description: string;
className?: string;
};
export function SectionHeader({
title,
description,
className,
}: SectionHeaderProps) {
return (
);
}
// ---------------------------------------------------------------------------
// SettingRow
// ---------------------------------------------------------------------------
export type SettingRowProps = {
icon: React.ReactNode;
label: string;
description: string;
control: React.ReactNode;
className?: string;
};
export function SettingRow({
icon,
label,
description,
control,
className,
}: SettingRowProps) {
return (
{icon}
{label}
{description}
{control}
);
}
// ---------------------------------------------------------------------------
// SettingCard — wraps SettingRows with a bordered card + dividers
// ---------------------------------------------------------------------------
export type SettingCardProps = {
rows: SettingRowProps[];
className?: string;
};
export function SettingCard({ rows, className }: SettingCardProps) {
return (
{rows.map((row, i) => (
{i < rows.length - 1 && }
))}
);
}
// ---------------------------------------------------------------------------
// AgentConfigForm
// ---------------------------------------------------------------------------
export type AgentConfigFormProps = {
config: AiBuilderAgentConfig;
onChange: (config: AiBuilderAgentConfig) => void;
className?: string;
};
export function AgentConfigForm({
config,
onChange,
className,
}: AgentConfigFormProps) {
return (
);
}
// ---------------------------------------------------------------------------
// ResponseTemplateEditModal
// ---------------------------------------------------------------------------
export type ResponseTemplateEditModalProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
channel: "email" | "chat";
content: string;
onConfirm: (content: string) => void;
};
export function ResponseTemplateEditModal({
open,
onOpenChange,
channel,
content,
onConfirm,
}: ResponseTemplateEditModalProps) {
const [draft, setDraft] = React.useState(content);
React.useEffect(() => {
if (open) setDraft(content);
}, [open, content]);
const isEmail = channel === "email";
const channelLabel = isEmail ? "Email" : "Chat";
const helperText = isEmail
? "Use {customer} to insert the customer name and {answer} to insert the response"
: "Use {answer} to insert the response";
return (
);
}
// ---------------------------------------------------------------------------
// RuleOrderBadge
// ---------------------------------------------------------------------------
export type RuleOrderBadgeProps = {
order: number;
className?: string;
};
export function RuleOrderBadge({ order, className }: RuleOrderBadgeProps) {
return (
#{order}
);
}
// ---------------------------------------------------------------------------
// RuleSetSection
// ---------------------------------------------------------------------------
export type RuleListLoadMore = {
hasMore: boolean;
isLoadingMore: boolean;
onLoadMore: () => void;
};
export type RuleSetSectionProps = {
standardRules: AiBuilderRuleItem[];
handoffRules: AiBuilderRuleItem[];
onAddRule: (defaultType: "standard" | "handoff") => void;
onEditRule: (id: string) => void;
onDeleteRule: (id: string) => void;
onToggleRule: (id: string, enabled: boolean) => void;
onExport: () => void;
onImport: () => void;
/** Infinite-scroll wiring per rule list. */
standardLoadMore?: RuleListLoadMore;
handoffLoadMore?: RuleListLoadMore;
/** Max height (px) of each scrollable rule list. */
maxBodyHeight?: number;
className?: string;
};
// ---------------------------------------------------------------------------
// RuleRow — individual rule row
// ---------------------------------------------------------------------------
function RuleRow({
rule,
onEdit,
onDelete,
onToggle,
}: {
rule: AiBuilderRuleItem;
onEdit: (id: string) => void;
onDelete: (id: string) => void;
onToggle: (id: string, enabled: boolean) => void;
}) {
return (
{/* Rule text */}
{rule.text}
{/* Actions */}
onToggle(rule.id, checked)}
aria-label="Toggle rule"
/>
);
}
// ---------------------------------------------------------------------------
// RuleTable
// ---------------------------------------------------------------------------
function RuleTable({
rules,
onEdit,
onDelete,
onToggle,
}: {
rules: AiBuilderRuleItem[];
onEdit: (id: string) => void;
onDelete: (id: string) => void;
onToggle: (id: string, enabled: boolean) => void;
}) {
if (rules.length === 0) {
return (
No rules added yet.
);
}
return (
Rule Set
Actions
{rules.map((rule) => (
))}
);
}
function RuleTabContent({
rules,
description,
onEditRule,
onDeleteRule,
onToggleRule,
loadMore,
maxBodyHeight = 360,
}: {
rules: AiBuilderRuleItem[];
description: string;
onEditRule: (id: string) => void;
onDeleteRule: (id: string) => void;
onToggleRule: (id: string, enabled: boolean) => void;
/** Infinite scroll — when omitted, the list is simply scrollable. */
loadMore?: RuleListLoadMore;
maxBodyHeight?: number;
}) {
// Auto-load the next batch when the user scrolls near the bottom.
const handleScroll = (e: React.UIEvent) => {
if (!loadMore?.hasMore || loadMore.isLoadingMore) return;
const el = e.currentTarget;
if (el.scrollHeight - el.scrollTop - el.clientHeight < 48) {
loadMore.onLoadMore();
}
};
return (
{loadMore?.isLoadingMore && (
Loading more…
)}
);
}
export function RuleSetSection({
standardRules,
handoffRules,
onAddRule,
onEditRule,
onDeleteRule,
onToggleRule,
onExport,
onImport,
standardLoadMore,
handoffLoadMore,
maxBodyHeight,
className,
}: RuleSetSectionProps) {
const [activeTab, setActiveTab] = React.useState<"standard" | "handoff">(
"standard"
);
return (
{/* Toolbar */}
{/* Tabs + content */}
setActiveTab(v as "standard" | "handoff")}
>
Standard Rules
Hand-off Rules
);
}
// ---------------------------------------------------------------------------
// AddEditRuleModal
// ---------------------------------------------------------------------------
export type AddEditRuleModalProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
/** Pass a rule to edit; omit or pass null/undefined for "Add" mode */
rule?: AiBuilderRuleItem | null;
/** Pre-select the rule type in Add mode (defaults to "standard") */
defaultType?: "standard" | "handoff";
onConfirm: (text: string, type: "standard" | "handoff") => void;
};
export function AddEditRuleModal({
open,
onOpenChange,
rule,
defaultType = "standard",
onConfirm,
}: AddEditRuleModalProps) {
// Map between internal type key and display label (pattern: value === label)
const RULE_TYPE_LABELS: Record<"standard" | "handoff", string> = {
standard: "Standard Rule",
handoff: "Hand-off Rule",
};
const LABEL_TO_TYPE: Record = {
"Standard Rule": "standard",
"Hand-off Rule": "handoff",
};
const isEdit = !!rule;
const [draft, setDraft] = React.useState(rule?.text ?? "");
const [ruleType, setRuleType] = React.useState<"standard" | "handoff">(
defaultType
);
React.useEffect(() => {
if (open) {
setDraft(rule?.text ?? "");
setRuleType(defaultType);
}
}, [open, rule, defaultType]);
return (
);
}