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 ? (
event.stopPropagation()}>

{store.display_name || store.name} Items

Manage location-specific items used for suggestions and defaults.

{!catalogReady ? (

{catalogMessage || "Store item management is unavailable until the latest database migration is applied."}

) : null}
setQuery(event.target.value)} placeholder="Search household/store items" disabled={!catalogReady} />
{isAdmin && catalogReady && items.length > 0 ? (
{deleteMode ? ( ) : null}
) : null}
{!catalogReady ? (

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.

) : (
{items.map((item) => { const imageSrc = itemImageSource(item); const isSelectedForDelete = selectedDeleteIds.has(item.item_id); return ( ); })}
)}
) : null} { setShowEditor(false); setEditorItem(null); }} onSave={handleUpdate} /> 0} title={ pendingDeleteItems.length === 1 ? `Delete ${pendingDeleteItems[0].item_name}?` : `Delete ${pendingDeleteItems.length} items?` } description={ pendingDeleteItems.length > 0 ? `Slide to confirm. This permanently deletes ${pendingDeleteItems.length === 1 ? pendingDeleteItems[0].item_name : `${pendingDeleteItems.length} items`} from ${store.display_name || store.name} for this household, including current list entries and history.` : "" } confirmLabel={pendingDeleteItems.length === 1 ? "Delete Item" : "Delete Items"} onClose={() => setPendingDeleteItems([])} onConfirm={handleDeleteConfirm} /> ); }