import React, { useState, useEffect } from 'react'; import { GroceryCategory } from '../types'; import { groceryCategoryApi } from '../services/api'; import AddGroceryCategoryModal from './AddGroceryCategoryModal'; import ConfirmDeleteModal from './ConfirmDeleteModal'; const GroceryCategoryList: React.FC = () => { const [categories, setCategories] = useState([]); const [loading, setLoading] = useState(true); const [message, setMessage] = useState(''); const [isModalOpen, setIsModalOpen] = useState(false); const [editingCategory, setEditingCategory] = useState(null); const [deletingCategory, setDeletingCategory] = useState(null); const [deleteLoading, setDeleteLoading] = useState(false); const [sortField, setSortField] = useState('name'); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc'); useEffect(() => { fetchCategories(); }, []); const fetchCategories = async () => { try { setLoading(true); const response = await groceryCategoryApi.getAll(); setCategories(response.data); } catch (error) { console.error('Error fetching categories:', error); setMessage('Error loading categories. Please try again.'); } finally { setLoading(false); } }; const handleDelete = async (category: GroceryCategory) => { setDeletingCategory(category); }; const confirmDelete = async () => { if (!deletingCategory) return; try { setDeleteLoading(true); await groceryCategoryApi.delete(deletingCategory.id); setMessage('Category deleted successfully!'); setDeletingCategory(null); fetchCategories(); setTimeout(() => setMessage(''), 1500); } catch (error: any) { console.error('Error deleting category:', error); if (error.response?.status === 400) { setMessage('Cannot delete category: groceries are still associated with this category.'); } else { setMessage('Error deleting category. Please try again.'); } setTimeout(() => setMessage(''), 3000); } finally { setDeleteLoading(false); } }; const handleCloseDeleteModal = () => { setDeletingCategory(null); }; const handleEdit = (category: GroceryCategory) => { setEditingCategory(category); setIsModalOpen(true); }; const handleModalClose = () => { setIsModalOpen(false); setEditingCategory(null); fetchCategories(); }; const handleSort = (field: keyof GroceryCategory) => { if (field === sortField) { setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); } else { setSortField(field); setSortDirection('asc'); } }; const sortedCategories = [...categories].sort((a, b) => { let aValue = a[sortField]; let bValue = b[sortField]; // Handle null/undefined values if (aValue === null || aValue === undefined) aValue = ''; if (bValue === null || bValue === undefined) bValue = ''; // Convert to string for comparison const aStr = String(aValue).toLowerCase(); const bStr = String(bValue).toLowerCase(); if (sortDirection === 'asc') { return aStr.localeCompare(bStr); } else { return bStr.localeCompare(aStr); } }); const getSortIcon = (field: keyof GroceryCategory) => { if (sortField !== field) { return ( ); } if (sortDirection === 'asc') { return ( ); } else { return ( ); } }; if (loading) { return (
); } return (

Grocery Categories

{message && (
{message}
)}
{categories.length === 0 ? (

No categories

Get started by adding your first category.

) : ( <> {/* Desktop Table */}
{sortedCategories.map((category) => ( ))}
handleSort('name')} >
Name {getSortIcon('name')}
handleSort('created_at')} >
Created {getSortIcon('created_at')}
Actions
{category.name}
{new Date(category.created_at).toLocaleDateString()}
{/* Mobile Card Layout */}
{sortedCategories.map((category) => (

{category.name}

Created: {new Date(category.created_at).toLocaleDateString()}

))}
)}
{isModalOpen && ( )}
); }; export default GroceryCategoryList;