// src/renderer/pages/DictionaryPage.tsx // 인스트루먼트 미학: MetalCard + PhosphorText import { useState, useEffect, useCallback } from 'react' import { Box, TextField, Button, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, InputAdornment } from '@mui/material' import AddIcon from '@mui/icons-material/Add' import DeleteIcon from '@mui/icons-material/Delete' import EditIcon from '@mui/icons-material/Edit' import SearchIcon from '@mui/icons-material/Search' import { MetalCard, PhosphorText, PhysicalButton } from '../components/ds' import { d3roPalette, d3roFontMono } from '../theme' import type { DictionaryEntry, DictionaryPage as DictPageData } from '@shared/types' const PAGE_SIZE = 50 export function DictionaryPage(): React.ReactElement { const [data, setData] = useState(null) const [search, setSearch] = useState('') const [loading, setLoading] = useState(true) const [dialogOpen, setDialogOpen] = useState(false) const [editId, setEditId] = useState(null) const [formWord, setFormWord] = useState('') const [formPronunciation, setFormPronunciation] = useState('') const loadData = useCallback(async () => { setLoading(true) const result = search.trim() ? await window.electronAPI.dictionary.search({ query: search, page: 0, pageSize: PAGE_SIZE }) : await window.electronAPI.dictionary.getAll({ page: 0, pageSize: PAGE_SIZE }) if (result.success) setData(result.data) setLoading(false) }, [search]) useEffect(() => { loadData() }, [loadData]) const openAdd = () => { setEditId(null) setFormWord('') setFormPronunciation('') setDialogOpen(true) } const openEdit = (entry: DictionaryEntry) => { setEditId(entry.id) setFormWord(entry.word) setFormPronunciation(entry.pronunciation ?? '') setDialogOpen(true) } const handleSave = async () => { if (!formWord.trim()) return if (editId) { await window.electronAPI.dictionary.update({ id: editId, word: formWord.trim(), pronunciation: formPronunciation.trim() || undefined, }) } else { await window.electronAPI.dictionary.add({ word: formWord.trim(), pronunciation: formPronunciation.trim() || undefined, }) } setDialogOpen(false) loadData() } return ( CUSTOM DICTIONARY — {data?.total ?? 0} WORDS setSearch(e.target.value)} fullWidth sx={{ mb: 3, '& .MuiInputBase-input': { fontFamily: d3roFontMono, fontSize: '12px', letterSpacing: '0.5px' } }} slotProps={{ input: { startAdornment: } }} /> {loading ? ( LOADING... ) : !data || data.entries.length === 0 ? ( {search ? 'NO RESULTS' : 'NO WORDS — ADD CUSTOM WORDS FOR BETTER STT'} ) : ( {data.entries.map((entry: DictionaryEntry) => ( {entry.word} {entry.pronunciation && ( [{entry.pronunciation}] )} {entry.category.toUpperCase()} · {entry.usageCount}× USED openEdit(entry)} sx={{ color: d3roPalette.text.inactive, '&:hover': { color: d3roPalette.accent.amber } }}> { window.electronAPI.dictionary.delete({ id: entry.id }); loadData() }} sx={{ color: d3roPalette.text.inactive, '&:hover': { color: d3roPalette.tag.red } }}> ))} )} setDialogOpen(false)} maxWidth="xs" fullWidth> {editId ? '단어 편집' : '단어 추가'} setFormWord(e.target.value)} fullWidth autoFocus sx={{ mt: 1 }} /> setFormPronunciation(e.target.value)} fullWidth sx={{ mt: 2 }} /> ) }