import { useCallback, useEffect, useState } from "react"; import { createAvailableItem, deleteAvailableItem, getAvailableItems, updateAvailableItem, } from "../../api/availableItems"; import { getLocationZones } from "../../api/stores"; import useActionToast from "../../hooks/useActionToast"; import getApiErrorMessage from "../../lib/getApiErrorMessage"; import AvailableItemEditorModal from "../modals/AvailableItemEditorModal"; import ConfirmSlideModal from "../modals/ConfirmSlideModal"; function itemImageSource(item) { if (!item?.item_image) { return null; } const mimeType = item.image_mime_type || "image/jpeg"; return `data:${mimeType};base64,${item.item_image}`; } export default function StoreAvailableItemsManager({ householdId, store, isAdmin, refreshStoreCounts, itemCount = 0, }) { const toast = useActionToast(); const [isOpen, setIsOpen] = useState(false); const [items, setItems] = useState([]); const [displayItemCount, setDisplayItemCount] = useState(itemCount); const [zones, setZones] = useState([]); const [catalogReady, setCatalogReady] = useState(true); const [catalogMessage, setCatalogMessage] = useState(""); const [query, setQuery] = useState(""); const [loading, setLoading] = useState(false); const [editorItem, setEditorItem] = useState(null); const [showEditor, setShowEditor] = useState(false); const [deleteMode, setDeleteMode] = useState(false); const [selectedDeleteIds, setSelectedDeleteIds] = useState(() => new Set()); const [pendingDeleteItems, setPendingDeleteItems] = useState([]); const selectedDeleteItems = items.filter((item) => selectedDeleteIds.has(item.item_id)); const selectedDeleteCount = selectedDeleteItems.length; const loadItems = useCallback(async (search = query) => { if (!householdId || !store?.id) { setItems([]); return; } setLoading(true); try { const response = await getAvailableItems(householdId, store.id, search); setItems(response.data.items || []); setCatalogReady(response.data.catalog_ready !== false); setCatalogMessage(response.data.message || ""); } catch (error) { console.error("Failed to load store items:", error); setCatalogReady(false); setCatalogMessage("Store item management is unavailable right now."); const message = getApiErrorMessage(error, "Failed to load store items"); toast.error("Load store items failed", `Load store items failed: ${message}`); } finally { setLoading(false); } }, [householdId, query, store?.id, toast]); const loadZones = useCallback(async () => { if (!householdId || !store?.id) { setZones([]); return; } try { const response = await getLocationZones(householdId, store.id); setZones(response.data?.zones || []); } catch (error) { console.error("Failed to load location zones:", error); setZones([]); } }, [householdId, store?.id]); useEffect(() => { if (!isOpen) { return; } loadItems(query); loadZones(); }, [isOpen, query, loadItems, loadZones]); useEffect(() => { setDisplayItemCount(itemCount); }, [itemCount]); const closeManager = () => { setIsOpen(false); setDeleteMode(false); setSelectedDeleteIds(new Set()); setPendingDeleteItems([]); }; const handleUpdate = async (payload) => { if (!catalogReady) { toast.info( "Store item management unavailable", catalogMessage || "Store item management is unavailable until the latest database migration is applied." ); return; } try { if (editorItem?.item_id) { await updateAvailableItem(householdId, store.id, editorItem.item_id, payload); toast.success("Updated store item", `Updated ${editorItem.item_name} for ${store.display_name || store.name}`); } else { const response = await createAvailableItem(householdId, store.id, payload); toast.success( "Created store item", `Created ${response.data?.item?.item_name || payload.itemName} for ${store.display_name || store.name}` ); } setShowEditor(false); setEditorItem(null); await loadItems(query); await refreshStoreCounts?.(); } catch (error) { const message = getApiErrorMessage(error, "Failed to update store item"); toast.error("Update store item failed", `Update store item failed: ${message}`); throw error; } }; const openEditor = (item) => { setEditorItem(item); setShowEditor(true); }; const toggleDeleteSelection = (itemId) => { setSelectedDeleteIds((currentIds) => { const nextIds = new Set(currentIds); if (nextIds.has(itemId)) { nextIds.delete(itemId); } else { nextIds.add(itemId); } return nextIds; }); }; const startDeleteMode = () => { setDeleteMode(true); setSelectedDeleteIds(new Set()); }; const cancelDeleteMode = () => { setDeleteMode(false); setSelectedDeleteIds(new Set()); }; const confirmSelectedDelete = () => { if (selectedDeleteCount === 0) { return; } setPendingDeleteItems(selectedDeleteItems); }; const handleDeleteConfirm = async () => { if (pendingDeleteItems.length === 0) { return; } try { await Promise.all( pendingDeleteItems.map((item) => deleteAvailableItem(householdId, store.id, item.item_id)) ); const count = pendingDeleteItems.length; toast.success( count === 1 ? "Deleted store item" : "Deleted store items", `Deleted ${count} ${count === 1 ? "item" : "items"} from ${store.display_name || store.name}` ); setPendingDeleteItems([]); setDeleteMode(false); setSelectedDeleteIds(new Set()); await loadItems(query); await refreshStoreCounts?.(); } catch (error) { const message = getApiErrorMessage(error, "Failed to delete store item"); toast.error("Delete store items failed", `Delete store items failed: ${message}`); } }; return ( <> {isOpen ? (
Manage location-specific items used for suggestions and defaults.
{catalogMessage || "Store item management is unavailable until the latest database migration is applied."}
) : null}Run the latest database migrations to enable store item management.
) : loading ? (Loading store items...
) : items.length === 0 ? (No household items found for this store yet.
) : (