import { useState, useEffect, useCallback } from 'react'; import { useRouter } from 'next/router'; import { isLoggedIn, getCategories, createCategory, deleteCategory } from '../lib/api'; import AppShell from '../components/AppShell'; import { Icon } from '../components/icons'; import { useToast } from '../components/ToastProvider'; import { useConfirm } from '../components/ConfirmProvider'; const ICONS = [ '๐', '๐ฅ', '๐ฅฌ', '๐ฅฉ', '๐', '๐', 'โ', '๐บ', '๐ง', '๐', '๐ช', '๐ซ', '๐', '๐', '๐', '๐', 'โฝ', '๐', '๐ต', 'โ๏ธ', '๐ข', '๐', '๐', '๐', '๐', '๐', '๐', '๐งข', '๐', '๐', '๐', '๐', '๐ ', '๐ข', '๐ฆ', '๐ฅ', '๐จ', '๐ฉ', '๐ง', '๐จ', '๐๏ธ', '๐บ', '๐ป', '๐ฑ', '๐', '๐ฐ', '๐', '๐', '๐งง', '๐ณ', '๐ฏ', '๐', '๐ต', '๐ฎ', '๐', 'โฝ', '๐', '๐ด', '๐ญ', '๐จ', '๐ฌ', '๐ท', '๐ฅ', '๐บ', '๐งธ', '๐', '๐', '๐ธ', '๐บ' ]; export default function Categories() { const router = useRouter(); const { showToast } = useToast(); const { confirm } = useConfirm(); const [categories, setCategories] = useState({ expense: [], income: [] }); const [modalOpen, setModalOpen] = useState(false); const [categoryType, setCategoryType] = useState('expense'); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [formError, setFormError] = useState(''); const [formData, setFormData] = useState({ name: '', icon: '๐', }); const loadCategories = useCallback(async () => { setLoading(true); try { const data = await getCategories(); setCategories(data); } catch (err) { console.error(err); showToast(err.message, { tone: 'error' }); } finally { setLoading(false); } }, [showToast]); useEffect(() => { if (!isLoggedIn()) { router.push('/login'); return; } loadCategories(); }, [router, loadCategories]); const openAddModal = (type) => { setCategoryType(type); setFormData({ name: '', icon: '๐' }); setFormError(''); setModalOpen(true); }; const closeModal = () => { if (submitting) return; setModalOpen(false); setFormError(''); }; const handleSubmit = async (e) => { e.preventDefault(); if (!formData.name || !formData.icon) { setFormError('่ฏทๅกซๅๅฎๆดไฟกๆฏ'); return; } setSubmitting(true); setFormError(''); try { await createCategory({ type: categoryType, name: formData.name, icon: formData.icon, }); closeModal(); showToast('ๅ็ฑปๅทฒๅๅปบ', { tone: 'success' }); await loadCategories(); } catch (err) { setFormError(err.message); showToast(err.message, { tone: 'error' }); } finally { setSubmitting(false); } }; const handleDelete = async (id) => { const shouldDelete = await confirm({ title: 'ๅ ้คๆญคๅ็ฑป๏ผ', description: '้ป่ฎคๅ็ฑปไธๅฏๅ ้ค๏ผ่ชๅฎไนๅ็ฑปๅ ้คๅๅฐๆ ๆณๆขๅคใ', confirmText: 'ๅ ้ค', }); if (!shouldDelete) return; try { await deleteCategory(id); showToast('ๅ็ฑปๅทฒๅ ้ค', { tone: 'success' }); await loadCategories(); } catch (err) { showToast(err.message, { tone: 'error' }); } }; const actions = ( router.push('/settings')}> ); return ( {loading ? ( ๅ ่ฝฝไธญ... ) : ( <> ๆฏๅบๅ็ฑป ๆฏๅบ openAddModal('expense')}> ๆทปๅ {categories.expense.map((cat) => ( {cat.icon} {cat.name} {!cat.is_default && ( handleDelete(cat.id)}> )} ))} ๆถๅ ฅๅ็ฑป ๆถๅ ฅ openAddModal('income')}> ๆทปๅ {categories.income.map((cat) => ( {cat.icon} {cat.name} {!cat.is_default && ( handleDelete(cat.id)}> )} ))} ้ป่ฎคๅ็ฑปๆ ๆณๅ ้ค๏ผ่ชๅฎไนๅ็ฑปๅฏไปฅๅ ้คใ > )} {modalOpen && ( e.stopPropagation()}> ๆฐๅขๅ็ฑป {categoryType === 'expense' ? 'ๆฐๅปบๆฏๅบๅ็ฑป' : 'ๆฐๅปบๆถๅ ฅๅ็ฑป'} {formError && {formError}} ๅ็ฑปๅ็งฐ setFormData({ ...formData, name: e.target.value })} placeholder="่ฏท่พๅ ฅๅ็ฑปๅ็งฐ" required disabled={submitting} /> ้ๆฉๅพๆ {ICONS.map((icon) => ( setFormData({ ...formData, icon })} disabled={submitting} > {icon} ))} {submitting ? ( ไฟๅญไธญ... ) : 'ไฟๅญ'} )} ); }
ๆฏๅบๅ็ฑป
ๆถๅ ฅๅ็ฑป
้ป่ฎคๅ็ฑปๆ ๆณๅ ้ค๏ผ่ชๅฎไนๅ็ฑปๅฏไปฅๅ ้คใ
ๆฐๅขๅ็ฑป