From 6b2d980060a628f8a8167a5c9ee979550f66563d Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 18 Sep 2025 09:30:21 +0700 Subject: [PATCH] update save config --- src/main/shotcut.ts | 12 +++--- src/renderer/src/App.tsx | 25 +++++++++++- src/renderer/src/components/ShotcutForm.tsx | 43 ++++++++++++++++++--- src/renderer/src/components/ShotcutList.tsx | 13 ++++++- 4 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/main/shotcut.ts b/src/main/shotcut.ts index 28818eb..e3c1074 100644 --- a/src/main/shotcut.ts +++ b/src/main/shotcut.ts @@ -15,15 +15,15 @@ export const defaultShortcuts: IShortcut[] = [ { id: uuid(), links: [ - 'https://int.ipsupply.com.au/erptools/001_search-vpn?search=${keyword}', - 'https://www.ebay.com/sch/i.html?_nkw=${keyword}&_sop=15' + 'https://int.ipsupply.com.au/erptools/001_search-vpn?search={keyword}', + 'https://www.ebay.com/sch/i.html?_nkw={keyword}&_sop=15' ], - shortcut: 'CommandOrControl+Shift+1' + shortcut: 'Control+Shift+1' }, { id: uuid(), - links: ['https://esearch.danielvu.com?keyword=${keyword}'], - shortcut: 'CommandOrControl+Shift+2' + links: ['https://esearch.danielvu.com?keyword={keyword}'], + shortcut: 'Control+Shift+2' } ] @@ -86,7 +86,7 @@ const handleOpenBrowserByLink = (data: IShortcut) => { const query = encodeURIComponent(text) data.links.forEach((link) => { - const url = link.replace(/\$\{keyword\}/g, query) // Thay ${query} trong link + const url = link.replace(/\$\{keyword\}|\{keyword\}/g, query) console.log(`[${new Date().toLocaleTimeString()}] Opening URL: ${url}`) shell.openExternal(url) // Mở trong browser mặc định }) diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index dffae70..925ff14 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -12,6 +12,7 @@ import { getConfigFile, saveConfigFile } from './api/config' function App(): React.JSX.Element { const [loading, setLoading] = useState(true) const [shotcuts, setShotcuts] = useState([]) + const [editing, setEditing] = useState(null) // Hàm lưu config const saveConfig = async (newShortcuts: IShotcut[]) => { @@ -30,6 +31,21 @@ function App(): React.JSX.Element { saveConfig(newShortcuts) } + // Khi nhấn edit từ list + const handleEdit = (shotcut: IShotcut) => { + setEditing(shotcut) + } + + // Submit update + const handleUpdate = (updatedShotcut: IShotcut) => { + const updatedList = shotcuts.map((sc) => + sc.id === updatedShotcut.id ? updatedShotcut : sc + ) + setShotcuts(updatedList) + saveConfig(updatedList) + setEditing(null) // reset form + } + // Gọi IPC để lấy dữ liệu config useEffect(() => { const fetchData = async () => { @@ -67,15 +83,22 @@ function App(): React.JSX.Element { > { const updated = [newItem, ...shotcuts] setShotcuts(updated) saveConfig(updated) }} + editingShotcut={editing || undefined} + onUpdate={handleUpdate} /> - + interface ShotcutFormProps { onSubmit: (shotcut: IShotcut) => void + onUpdate?: (shotcut: IShotcut) => void + editingShotcut?: IShotcut + shortcuts: IShotcut[] } -export function ShotcutForm({ onSubmit }: ShotcutFormProps) { +export function ShotcutForm({ onSubmit, onUpdate, editingShotcut, shortcuts }: ShotcutFormProps) { const form = useForm({ - initialValues: { shortcut: '', links: [''] }, + initialValues: editingShotcut + ? { shortcut: editingShotcut.shortcut, links: editingShotcut.links } + : { shortcut: '', links: [''] }, validate: zodResolver(ShotcutSchema) }) @@ -58,16 +64,41 @@ export function ShotcutForm({ onSubmit }: ShotcutFormProps) { const removeLink = (index: number) => form.removeListItem('links', index) const handleSubmit = (values: ShotcutFormValues) => { - onSubmit({ ...values, id: uuid() }) - form.reset() + // Kiểm tra trùng shortcut + const isDuplicate = shortcuts.some( + (item) => + item.shortcut === values.shortcut && + (!editingShotcut || item.id !== editingShotcut.id) + ) + + if (isDuplicate) { + form.setFieldError('shortcut', 'Shortcut already exists') + return + } + + if (editingShotcut && onUpdate) { + onUpdate({ ...editingShotcut, ...values }) + + form.reset() + } else { + onSubmit({ ...values, id: uuid() }) + form.reset() + } } + useEffect(() => { + if (!editingShotcut) return + + form.setValues(editingShotcut) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [editingShotcut]) + return (
- Create New Shortcut + {editingShotcut ? 'Update Shortcut' : 'Create New Shortcut'} diff --git a/src/renderer/src/components/ShotcutList.tsx b/src/renderer/src/components/ShotcutList.tsx index 7831b0e..1968fd9 100644 --- a/src/renderer/src/components/ShotcutList.tsx +++ b/src/renderer/src/components/ShotcutList.tsx @@ -3,14 +3,15 @@ import { useState } from 'react' import { ActionIcon, Badge, Card, Group, Stack, Text, Modal, Button, Flex } from '@mantine/core' import { IShotcut } from '@renderer/types' -import { IconExternalLink, IconTrash } from '@tabler/icons-react' +import { IconEdit, IconExternalLink, IconTrash } from '@tabler/icons-react' interface ShotcutListProps { shotcuts: IShotcut[] onDelete: (shotcut: IShotcut) => void + onEdit: (shotcut: IShotcut) => void } -export function ShotcutList({ shotcuts, onDelete }: ShotcutListProps) { +export function ShotcutList({ shotcuts, onDelete, onEdit }: ShotcutListProps) { const [opened, setOpened] = useState(false) const [selectedShotcut, setSelectedShotcut] = useState(null) @@ -58,6 +59,14 @@ export function ShotcutList({ shotcuts, onDelete }: ShotcutListProps) { > + onEdit(shotcut)} + title="Edit shortcut" + > + + {/* Content */}