/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import { useState, useMemo } from 'react'
import { Pencil, Plus, Search, Trash2 } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { StaticDataTable } from '@/components/data-table'
import { safeJsonParseWithValidation } from '../utils/json-parser'
import { isArray } from '../utils/json-validators'
import { ChatDialog, type ChatEntryData } from './chat-dialog'
type ChatSettingsVisualEditorProps = {
value: string
onChange: (value: string) => void
}
type ChatEntry = ChatEntryData
export function ChatSettingsVisualEditor({
value,
onChange,
}: ChatSettingsVisualEditorProps) {
const { t } = useTranslation()
const [searchText, setSearchText] = useState('')
const [dialogOpen, setDialogOpen] = useState(false)
const [editData, setEditData] = useState(null)
const chats = useMemo(() => {
const parsed = safeJsonParseWithValidation(value, {
fallback: [],
validator: isArray,
validatorMessage: 'Chats must be a JSON array',
context: 'chats',
})
return parsed
.map((item) => {
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
const entries = Object.entries(item)
if (entries.length === 1) {
const [name, url] = entries[0]
return { name, url: String(url) }
}
}
return null
})
.filter((item): item is ChatEntry => item !== null)
}, [value])
const filteredChats = useMemo(() => {
if (!searchText) return chats
const lowerSearch = searchText.toLowerCase()
return chats.filter(
(chat) =>
chat.name.toLowerCase().includes(lowerSearch) ||
chat.url.toLowerCase().includes(lowerSearch)
)
}, [chats, searchText])
const handleSave = (data: ChatEntryData) => {
const chatsArray = safeJsonParseWithValidation(value, {
fallback: [],
validator: isArray,
silent: true,
})
let updatedArray = [...chatsArray]
if (editData) {
updatedArray = updatedArray.filter((item) => {
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
return !Object.keys(item).includes(editData.name)
}
return true
})
}
updatedArray.push({ [data.name]: data.url })
onChange(JSON.stringify(updatedArray, null, 2))
}
const handleDelete = (name: string) => {
const chatsArray = safeJsonParseWithValidation(value, {
fallback: [],
validator: isArray,
silent: true,
})
const updatedArray = chatsArray.filter((item) => {
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
return !Object.keys(item).includes(name)
}
return true
})
onChange(JSON.stringify(updatedArray, null, 2))
}
const handleEdit = (chat: ChatEntry) => {
setEditData(chat)
setDialogOpen(true)
}
const handleAdd = () => {
setEditData(null)
setDialogOpen(true)
}
return (