import { useMemo, useState } from "react"; import { Alert, Pressable, View } from "react-native"; import { useLocalSearchParams } from "expo-router"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { api } from "@/lib/api"; import { t } from "@/lib/i18n"; import { formatQuantity } from "@/lib/units"; import { Body, Button, Card, EmptyState, ErrorView, Heading, Input, LoadingView, Row, Screen, Small, } from "@/components/ui"; import { colors, spacing } from "@/lib/theme"; /** * Matlager: allt du har hemma, städat per plats (Kyl/Frys/Skafferi …). * Sök, filtrera på plats, markera flera och ta bort. Radering = mjuk * (kvantitet → 0, historik bevaras). */ interface InventoryItem { id: string; displayName: string; quantity: number; unit: string; locationName: string; locationType: string; expiry: { status: string; daysLeft: number | null; pastBestBefore: boolean }; } const LOCATION_ORDER = ["fridge", "freezer", "pantry", "garage_freezer", "wine_fridge"]; const locLabel = (type: string) => LOCATION_ORDER.includes(type) ? t(`home.location.${type}`) : type; const orderIndex = (type: string) => { const i = LOCATION_ORDER.indexOf(type); return i === -1 ? LOCATION_ORDER.length : i; }; export default function KitchenScreen() { const queryClient = useQueryClient(); const params = useLocalSearchParams<{ loc?: string }>(); const initialLoc = typeof params.loc === "string" ? params.loc : null; const [search, setSearch] = useState(""); const [locFilter, setLocFilter] = useState(initialLoc); const [selected, setSelected] = useState>(new Set()); const query = useQuery({ queryKey: ["inventory", "all"], queryFn: () => api<{ items: InventoryItem[] }>("/v1/inventory?limit=500"), }); const bulkRemove = useMutation({ mutationFn: (ids: string[]) => api<{ deleted: number }>("/v1/inventory/bulk-delete", { method: "POST", body: { ids } }), onSuccess: () => setSelected(new Set()), onError: (err) => Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")), onSettled: () => { void queryClient.invalidateQueries({ queryKey: ["inventory"] }); void queryClient.invalidateQueries({ queryKey: ["inventory-expiring"] }); // Lagret styr receptförslagen – uppdatera "Vad ska vi äta?" direkt. void queryClient.invalidateQueries({ queryKey: ["what-to-eat"] }); }, }); const all = query.data?.items ?? []; const locTypes = useMemo(() => { const set = new Set(all.map((i) => i.locationType)); return [...set].sort((a, b) => orderIndex(a) - orderIndex(b) || a.localeCompare(b, "sv")); }, [all]); const q = search.trim().toLowerCase(); const visible = useMemo( () => all .filter((i) => (locFilter ? i.locationType === locFilter : true)) .filter((i) => (q ? i.displayName.toLowerCase().includes(q) : true)), [all, locFilter, q], ); const groups = useMemo(() => { const map = new Map(); for (const it of visible) { const list = map.get(it.locationType) ?? []; list.push(it); map.set(it.locationType, list); } return [...map.entries()] .sort(([a], [b]) => orderIndex(a) - orderIndex(b) || a.localeCompare(b, "sv")) .map(([type, items]) => ({ type, items: [...items].sort((x, y) => x.displayName.localeCompare(y.displayName, "sv")), })); }, [visible]); if (query.isLoading) return ; if (query.isError) return void query.refetch()} />; const toggle = (id: string) => setSelected((prev) => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); const confirmDelete = (ids: string[], label: string) => { if (ids.length === 0) return; Alert.alert(t("common.remove"), t("kitchen.deleteConfirm", { label }), [ { text: t("common.cancel"), style: "cancel" }, { text: t("common.remove"), style: "destructive", onPress: () => bulkRemove.mutate(ids) }, ]); }; const emptyLabel = locFilter ? locLabel(locFilter) : q ? t("kitchen.scopeSearch") : t("kitchen.scopeAll"); return (