From 6b8426868a70b796ec9c60c73886bbcca7a7d036 Mon Sep 17 00:00:00 2001 From: heimoshuiyu Date: Fri, 30 May 2025 18:35:40 +0800 Subject: [PATCH] Fix: Template attribute dialog input and type issues --- src/components/Settings.tsx | 39 +++--- src/components/TemplateAttributeDialog.tsx | 142 +++++++++++++++++++++ src/components/ui/controlled-input.tsx | 21 +++ 3 files changed, 186 insertions(+), 16 deletions(-) create mode 100644 src/components/TemplateAttributeDialog.tsx create mode 100644 src/components/ui/controlled-input.tsx diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 59cdc67..76b143c 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -77,6 +77,7 @@ import { Slider } from "@/components/ui/slider"; import { NonOverflowScrollArea, ScrollArea } from "@/components/ui/scroll-area"; import { AppChatStoreContext, AppContext } from "@/pages/App"; import { toast } from "@/hooks/use-toast"; +import { TemplateAttributeDialog } from "@/components/TemplateAttributeDialog"; const TTS_VOICES: string[] = [ "alloy", @@ -739,6 +740,7 @@ export default (props: {}) => { // @ts-ignore const { langCode, setLangCode } = useContext(langCodeContext); const [open, setOpen] = useState(false); + const [showTemplateDialog, setShowTemplateDialog] = useState(false); useEffect(() => { themeChange(false); @@ -804,7 +806,7 @@ export default (props: {}) => { @@ -1051,21 +1053,7 @@ export default (props: {}) => { @@ -1594,6 +1582,25 @@ export default (props: {}) => { + + setShowTemplateDialog(false)} + onSave={(name, selectedAttributes) => { + const tmp: ChatStore = { + ...chatStore, + ...selectedAttributes, + history: chatStore.history.filter((h) => h.example), + }; + // @ts-ignore + tmp.name = name; + templates.push(tmp as TemplateChatStore); + setTemplates([...templates]); + setShowTemplateDialog(false); + }} + /> ); }; diff --git a/src/components/TemplateAttributeDialog.tsx b/src/components/TemplateAttributeDialog.tsx new file mode 100644 index 0000000..d2fe539 --- /dev/null +++ b/src/components/TemplateAttributeDialog.tsx @@ -0,0 +1,142 @@ +import { useState } from "react"; +import { ChatStore } from "@/types/chatstore"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogFooter, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Label } from "@/components/ui/label"; +import { ControlledInput } from "@/components/ui/controlled-input"; +import { tr } from "@/translate"; + +interface TemplateAttributeDialogProps { + chatStore: ChatStore; + onSave: (name: string, selectedAttributes: Partial) => void; + onClose: () => void; + open: boolean; + langCode: "en-US" | "zh-CN"; +} + +export function TemplateAttributeDialog({ + chatStore, + onSave, + onClose, + open, + langCode, +}: TemplateAttributeDialogProps) { + // Create a map of all ChatStore attributes and their selection state + const [selectedAttributes, setSelectedAttributes] = useState>(() => { + const initial: Record = {}; + // Initialize all attributes as selected by default + Object.keys(chatStore).forEach((key) => { + initial[key] = true; + }); + return initial; + }); + + const [templateName, setTemplateName] = useState(""); + const [nameError, setNameError] = useState(""); + + const handleSave = () => { + // Validate name + if (!templateName.trim()) { + setNameError(tr("Template name is required", langCode)); + return; + } + setNameError(""); + + // Create a new object with only the selected attributes + const filteredStore = {} as Partial; + Object.entries(selectedAttributes).forEach(([key, isSelected]) => { + if (isSelected) { + const typedKey = key as keyof ChatStore; + // Use type assertion to ensure type safety + (filteredStore as any)[typedKey] = chatStore[typedKey]; + } + }); + onSave(templateName, filteredStore); + }; + + const toggleAll = (checked: boolean) => { + const newSelected = { ...selectedAttributes }; + Object.keys(newSelected).forEach((key) => { + newSelected[key] = checked; + }); + setSelectedAttributes(newSelected); + }; + + return ( + + + + Select Template Attributes + + Choose which attributes to include in your template. Unselected attributes will be omitted. + + + +
+
+ + { + setTemplateName(e.target.value); + setNameError(""); + }} + placeholder={tr("Enter template name", langCode)} + /> + {nameError && ( +

{nameError}

+ )} +
+ +
+ v)} + onCheckedChange={(checked) => toggleAll(checked as boolean)} + /> + +
+ + +
+ {Object.keys(chatStore).map((key) => ( +
+ + setSelectedAttributes((prev) => ({ + ...prev, + [key]: checked as boolean, + })) + } + /> + +
+ ))} +
+
+
+ + + + + +
+
+ ); +} \ No newline at end of file diff --git a/src/components/ui/controlled-input.tsx b/src/components/ui/controlled-input.tsx new file mode 100644 index 0000000..6c07cda --- /dev/null +++ b/src/components/ui/controlled-input.tsx @@ -0,0 +1,21 @@ +import { ComponentProps, forwardRef } from "react"; +import { cn } from "@/lib/utils"; + +const ControlledInput = forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ); + } +); +ControlledInput.displayName = "ControlledInput"; + +export { ControlledInput }; \ No newline at end of file