Merge pull request #25 from heimoshuiyu/cursor
feat: Add edit and delete functionality for templates and enhance JSON editor
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^2.2.0",
|
"@heroicons/react": "^2.2.0",
|
||||||
"@hookform/resolvers": "^3.9.1",
|
"@hookform/resolvers": "^3.9.1",
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.2",
|
"@radix-ui/react-accordion": "^1.2.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.4",
|
"@radix-ui/react-alert-dialog": "^1.1.4",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.1.1",
|
"@radix-ui/react-aspect-ratio": "^1.1.1",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext, useState, useRef } from "react";
|
||||||
import {
|
import {
|
||||||
ChatStore,
|
ChatStore,
|
||||||
TemplateAPI,
|
TemplateAPI,
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
TemplateTools,
|
TemplateTools,
|
||||||
} from "@/types/chatstore";
|
} from "@/types/chatstore";
|
||||||
import { Tr } from "@/translate";
|
import { Tr } from "@/translate";
|
||||||
|
import Editor from "@monaco-editor/react";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -35,14 +36,17 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTrigger,
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
DialogClose,
|
||||||
} from "./ui/dialog";
|
} from "./ui/dialog";
|
||||||
import { DialogTitle } from "@radix-ui/react-dialog";
|
|
||||||
import { Textarea } from "./ui/textarea";
|
import { Textarea } from "./ui/textarea";
|
||||||
import { Label } from "./ui/label";
|
import { Label } from "./ui/label";
|
||||||
import { Input } from "./ui/input";
|
import { Input } from "./ui/input";
|
||||||
import { SetAPIsTemplate } from "./setAPIsTemplate";
|
import { SetAPIsTemplate } from "./setAPIsTemplate";
|
||||||
import { isVailedJSON } from "@/utils/isVailedJSON";
|
import { isVailedJSON } from "@/utils/isVailedJSON";
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
import { ConfirmationDialog } from "./ui/confirmation-dialog";
|
||||||
|
|
||||||
interface APITemplateDropdownProps {
|
interface APITemplateDropdownProps {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -50,6 +54,81 @@ interface APITemplateDropdownProps {
|
|||||||
apiField: string;
|
apiField: string;
|
||||||
keyField: string;
|
keyField: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EditTemplateDialogProps {
|
||||||
|
template: TemplateAPI;
|
||||||
|
onSave: (updatedTemplate: TemplateAPI) => void;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditTemplateDialog({ template, onSave, onClose }: EditTemplateDialogProps) {
|
||||||
|
const [name, setName] = useState(template.name);
|
||||||
|
const [endpoint, setEndpoint] = useState(template.endpoint);
|
||||||
|
const [key, setKey] = useState(template.key);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
if (!name.trim()) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Template name cannot be empty",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSave({
|
||||||
|
...template,
|
||||||
|
name: name.trim(),
|
||||||
|
endpoint: endpoint.trim(),
|
||||||
|
key: key.trim(),
|
||||||
|
});
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open onOpenChange={onClose}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Edit Template</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="name">Template Name</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="endpoint">API Endpoint</Label>
|
||||||
|
<Input
|
||||||
|
id="endpoint"
|
||||||
|
value={endpoint}
|
||||||
|
onChange={(e) => setEndpoint(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="key">API Key</Label>
|
||||||
|
<Input
|
||||||
|
id="key"
|
||||||
|
value={key}
|
||||||
|
onChange={(e) => setKey(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button variant="outline">Cancel</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button onClick={handleSave}>Save Changes</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function APIsDropdownList({
|
function APIsDropdownList({
|
||||||
label,
|
label,
|
||||||
shortLabel,
|
shortLabel,
|
||||||
@@ -70,18 +149,60 @@ function APIsDropdownList({
|
|||||||
setTemplateAPIsWhisper,
|
setTemplateAPIsWhisper,
|
||||||
setTemplateTools,
|
setTemplateTools,
|
||||||
} = useContext(AppContext);
|
} = useContext(AppContext);
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const [editingTemplate, setEditingTemplate] = useState<TemplateAPI | null>(null);
|
||||||
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
|
const [templateToDelete, setTemplateToDelete] = useState<TemplateAPI | null>(null);
|
||||||
|
|
||||||
let API = templateAPIs;
|
let API = templateAPIs;
|
||||||
|
let setAPI = setTemplateAPIs;
|
||||||
if (label === "Chat API") {
|
if (label === "Chat API") {
|
||||||
API = templateAPIs;
|
API = templateAPIs;
|
||||||
|
setAPI = setTemplateAPIs;
|
||||||
} else if (label === "Whisper API") {
|
} else if (label === "Whisper API") {
|
||||||
API = templateAPIsWhisper;
|
API = templateAPIsWhisper;
|
||||||
|
setAPI = setTemplateAPIsWhisper;
|
||||||
} else if (label === "TTS API") {
|
} else if (label === "TTS API") {
|
||||||
API = templateAPIsTTS;
|
API = templateAPIsTTS;
|
||||||
|
setAPI = setTemplateAPIsTTS;
|
||||||
} else if (label === "Image Gen API") {
|
} else if (label === "Image Gen API") {
|
||||||
API = templateAPIsImageGen;
|
API = templateAPIsImageGen;
|
||||||
|
setAPI = setTemplateAPIsImageGen;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [open, setOpen] = React.useState(false);
|
const handleEdit = (template: TemplateAPI) => {
|
||||||
|
setEditingTemplate(template);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = (updatedTemplate: TemplateAPI) => {
|
||||||
|
const index = API.findIndex(t => t.name === updatedTemplate.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
const newAPI = [...API];
|
||||||
|
newAPI[index] = updatedTemplate;
|
||||||
|
setAPI(newAPI);
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Template updated successfully",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (template: TemplateAPI) => {
|
||||||
|
setTemplateToDelete(template);
|
||||||
|
setDeleteDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmDelete = () => {
|
||||||
|
if (templateToDelete) {
|
||||||
|
const newAPI = API.filter(t => t.name !== templateToDelete.name);
|
||||||
|
setAPI(newAPI);
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Template deleted successfully",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-4 mx-3">
|
<div className="flex items-center space-x-4 mx-3">
|
||||||
@@ -116,10 +237,34 @@ function APIsDropdownList({
|
|||||||
[apiField]: t.endpoint,
|
[apiField]: t.endpoint,
|
||||||
[keyField]: t.key,
|
[keyField]: t.key,
|
||||||
});
|
});
|
||||||
setOpen(false); // Close popover after selecting
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t.name}
|
<div className="flex items-center justify-between w-full">
|
||||||
|
<span>{t.name}</span>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleEdit(t);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EditIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleDelete(t);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
@@ -127,6 +272,23 @@ function APIsDropdownList({
|
|||||||
</Command>
|
</Command>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
{editingTemplate && (
|
||||||
|
<EditTemplateDialog
|
||||||
|
template={editingTemplate}
|
||||||
|
onSave={handleSave}
|
||||||
|
onClose={() => setEditingTemplate(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<ConfirmationDialog
|
||||||
|
isOpen={deleteDialogOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setDeleteDialogOpen(false);
|
||||||
|
setTemplateToDelete(null);
|
||||||
|
}}
|
||||||
|
onConfirm={confirmDelete}
|
||||||
|
title="Delete Template"
|
||||||
|
description={`Are you sure you want to delete "${templateToDelete?.name}"?`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -207,12 +369,161 @@ function ToolsDropdownList() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EditChatTemplateDialogProps {
|
||||||
|
template: TemplateChatStore;
|
||||||
|
onSave: (updatedTemplate: TemplateChatStore) => void;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditChatTemplateDialog({ template, onSave, onClose }: EditChatTemplateDialogProps) {
|
||||||
|
const [name, setName] = useState(template.name);
|
||||||
|
const [jsonContent, setJsonContent] = useState(() => {
|
||||||
|
const { name: _, ...rest } = template;
|
||||||
|
return JSON.stringify(rest, null, 2);
|
||||||
|
});
|
||||||
|
const [editor, setEditor] = useState<any>(null);
|
||||||
|
|
||||||
|
const handleEditorDidMount = (editor: any) => {
|
||||||
|
setEditor(editor);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFormat = () => {
|
||||||
|
if (editor) {
|
||||||
|
editor.getAction('editor.action.formatDocument').run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
if (!name.trim()) {
|
||||||
|
toast.error('Template name cannot be empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsedJson = JSON.parse(jsonContent);
|
||||||
|
const updatedTemplate: TemplateChatStore = {
|
||||||
|
name: name.trim(),
|
||||||
|
...parsedJson
|
||||||
|
};
|
||||||
|
onSave(updatedTemplate);
|
||||||
|
toast.success('Template updated successfully');
|
||||||
|
} catch (error) {
|
||||||
|
toast.error('Invalid JSON format');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open onOpenChange={onClose}>
|
||||||
|
<DialogContent className="max-w-4xl">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Edit Template</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="name">Template Name</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
placeholder="Enter template name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>Template Content (JSON)</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="absolute right-2 top-2 z-10"
|
||||||
|
onClick={handleFormat}
|
||||||
|
>
|
||||||
|
Format JSON
|
||||||
|
</Button>
|
||||||
|
<div className="h-[400px] border rounded-md">
|
||||||
|
<Editor
|
||||||
|
height="400px"
|
||||||
|
defaultLanguage="json"
|
||||||
|
value={jsonContent}
|
||||||
|
onChange={(value) => setJsonContent(value || '')}
|
||||||
|
onMount={handleEditorDidMount}
|
||||||
|
options={{
|
||||||
|
minimap: { enabled: false },
|
||||||
|
fontSize: 14,
|
||||||
|
lineNumbers: 'on',
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
automaticLayout: true,
|
||||||
|
tabSize: 2,
|
||||||
|
wordWrap: 'on'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSave}>Save Changes</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ChatTemplateDropdownList() {
|
function ChatTemplateDropdownList() {
|
||||||
const ctx = useContext(AppContext);
|
const ctx = useContext(AppContext);
|
||||||
|
|
||||||
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
const { templates, setTemplates } = useContext(AppContext);
|
const { templates, setTemplates } = useContext(AppContext);
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const [editingTemplate, setEditingTemplate] = useState<TemplateChatStore | null>(null);
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
||||||
|
const [templateToApply, setTemplateToApply] = useState<TemplateChatStore | null>(null);
|
||||||
|
|
||||||
|
const handleEdit = (template: TemplateChatStore) => {
|
||||||
|
setEditingTemplate(template);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = (updatedTemplate: TemplateChatStore) => {
|
||||||
|
const index = templates.findIndex(t => t.name === updatedTemplate.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
const newTemplates = [...templates];
|
||||||
|
newTemplates[index] = updatedTemplate;
|
||||||
|
setTemplates(newTemplates);
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Template updated successfully",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (template: TemplateChatStore) => {
|
||||||
|
setTemplateToApply(template);
|
||||||
|
setConfirmDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTemplateSelect = (template: TemplateChatStore) => {
|
||||||
|
if (chatStore.history.length > 0 || chatStore.systemMessageContent) {
|
||||||
|
setTemplateToApply(template);
|
||||||
|
setConfirmDialogOpen(true);
|
||||||
|
} else {
|
||||||
|
applyTemplate(template);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyTemplate = (template: TemplateChatStore) => {
|
||||||
|
setChatStore({
|
||||||
|
...newChatStore({
|
||||||
|
...chatStore,
|
||||||
|
...{
|
||||||
|
use_this_history: template.history ?? chatStore.history,
|
||||||
|
},
|
||||||
|
...template,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-4 mx-3">
|
<div className="flex items-center space-x-4 mx-3">
|
||||||
@@ -237,34 +548,33 @@ function ChatTemplateDropdownList() {
|
|||||||
<CommandItem
|
<CommandItem
|
||||||
key={index}
|
key={index}
|
||||||
value={t.name}
|
value={t.name}
|
||||||
onSelect={() => {
|
onSelect={() => handleTemplateSelect(t)}
|
||||||
// Update chatStore with the selected template
|
>
|
||||||
if (
|
<div className="flex items-center justify-between w-full">
|
||||||
chatStore.history.length > 0 ||
|
<span>{t.name}</span>
|
||||||
chatStore.systemMessageContent
|
<div className="flex gap-2">
|
||||||
) {
|
<Button
|
||||||
console.log("you clicked", t.name);
|
variant="ghost"
|
||||||
const confirm = window.confirm(
|
size="icon"
|
||||||
"This will replace the current chat history. Are you sure? "
|
onClick={(e) => {
|
||||||
);
|
e.stopPropagation();
|
||||||
if (!confirm) {
|
handleEdit(t);
|
||||||
setOpen(false); // Close popover even if not confirmed
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setChatStore({
|
|
||||||
...newChatStore({
|
|
||||||
...chatStore,
|
|
||||||
...{
|
|
||||||
use_this_history: t.history ?? chatStore.history,
|
|
||||||
},
|
|
||||||
...t,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
setOpen(false); // Close popover after selecting
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t.name}
|
<EditIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleDelete(t);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
@@ -272,6 +582,23 @@ function ChatTemplateDropdownList() {
|
|||||||
</Command>
|
</Command>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
{editingTemplate && (
|
||||||
|
<EditChatTemplateDialog
|
||||||
|
template={editingTemplate}
|
||||||
|
onSave={handleSave}
|
||||||
|
onClose={() => setEditingTemplate(null)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<ConfirmationDialog
|
||||||
|
isOpen={confirmDialogOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setConfirmDialogOpen(false);
|
||||||
|
setTemplateToApply(null);
|
||||||
|
}}
|
||||||
|
onConfirm={() => templateToApply && applyTemplate(templateToApply)}
|
||||||
|
title="Replace Chat History"
|
||||||
|
description="This will replace the current chat history. Are you sure?"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
import { AppChatStoreContext, AppContext } from "../pages/App";
|
import { AppChatStoreContext, AppContext } from "../pages/App";
|
||||||
|
import { ConfirmationDialog } from "./ui/confirmation-dialog";
|
||||||
|
|
||||||
interface EditMessageProps {
|
interface EditMessageProps {
|
||||||
chat: ChatStoreMessage;
|
chat: ChatStoreMessage;
|
||||||
@@ -22,9 +23,19 @@ interface EditMessageProps {
|
|||||||
}
|
}
|
||||||
export function EditMessage(props: EditMessageProps) {
|
export function EditMessage(props: EditMessageProps) {
|
||||||
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||||
|
|
||||||
const { showEdit, setShowEdit, chat } = props;
|
const { showEdit, setShowEdit, chat } = props;
|
||||||
|
|
||||||
|
const handleSwitchMessageType = () => {
|
||||||
|
if (typeof chat.content === "string") {
|
||||||
|
chat.content = [];
|
||||||
|
} else {
|
||||||
|
chat.content = "";
|
||||||
|
}
|
||||||
|
setChatStore({ ...chatStore });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={showEdit} onOpenChange={setShowEdit}>
|
<Dialog open={showEdit} onOpenChange={setShowEdit}>
|
||||||
{/* <DialogTrigger>
|
{/* <DialogTrigger>
|
||||||
@@ -46,19 +57,7 @@ export function EditMessage(props: EditMessageProps) {
|
|||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={() => {
|
onClick={() => setShowConfirmDialog(true)}
|
||||||
const confirm = window.confirm(
|
|
||||||
"Change message type will clear the content, are you sure?"
|
|
||||||
);
|
|
||||||
if (!confirm) return;
|
|
||||||
|
|
||||||
if (typeof chat.content === "string") {
|
|
||||||
chat.content = [];
|
|
||||||
} else {
|
|
||||||
chat.content = "";
|
|
||||||
}
|
|
||||||
setChatStore({ ...chatStore });
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Switch to{" "}
|
Switch to{" "}
|
||||||
{typeof chat.content === "string"
|
{typeof chat.content === "string"
|
||||||
@@ -68,6 +67,13 @@ export function EditMessage(props: EditMessageProps) {
|
|||||||
)}
|
)}
|
||||||
<Button onClick={() => setShowEdit(false)}>Close</Button>
|
<Button onClick={() => setShowEdit(false)}>Close</Button>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
<ConfirmationDialog
|
||||||
|
isOpen={showConfirmDialog}
|
||||||
|
onClose={() => setShowConfirmDialog(false)}
|
||||||
|
onConfirm={handleSwitchMessageType}
|
||||||
|
title="Switch Message Type"
|
||||||
|
description="Change message type will clear the content, are you sure?"
|
||||||
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
48
src/components/ui/confirmation-dialog.tsx
Normal file
48
src/components/ui/confirmation-dialog.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
DialogDescription,
|
||||||
|
} from "./dialog";
|
||||||
|
import { Button } from "./button";
|
||||||
|
|
||||||
|
interface ConfirmationDialogProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onConfirm: () => void;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ConfirmationDialog({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onConfirm,
|
||||||
|
title,
|
||||||
|
description
|
||||||
|
}: ConfirmationDialogProps) {
|
||||||
|
return (
|
||||||
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{title}</DialogTitle>
|
||||||
|
<DialogDescription>{description}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={onClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button variant="destructive" onClick={() => {
|
||||||
|
onConfirm();
|
||||||
|
onClose();
|
||||||
|
}}>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user