matiru

Blockchain-Based Supply Chain Transparency for Agricultural Produce

frontend/screens/Inventory.js (4488B)


  1import { useContext, useEffect, useState, useCallback } from "react";
  2import {
  3  ActivityIndicator,
  4  Alert,
  5  RefreshControl,
  6  ScrollView,
  7  Text,
  8  View,
  9} from "react-native";
 10import { SafeAreaView } from "react-native-safe-area-context";
 11import AsyncStorage from "@react-native-async-storage/async-storage";
 12
 13import ScreenHeader from "../components/ScreenHeader";
 14import ProduceCard from "../components/ProduceCard";
 15
 16import { AuthContext } from "../AuthContext";
 17import { api } from "../services/api";
 18import styles, { colors } from "../styles";
 19
 20const sortByLastAction = (a, b) => {
 21  const getLatestTimestamp = (produce) => {
 22    if (produce.actionHistory && produce.actionHistory.length > 0)
 23      return produce.actionHistory[produce.actionHistory.length - 1].timestamp;
 24    return produce.harvestDate;
 25  };
 26
 27  const timeA = getLatestTimestamp(a);
 28  const timeB = getLatestTimestamp(b);
 29
 30  return new Date(timeB) - new Date(timeA);
 31};
 32
 33export default function InventoryScreen({ navigation, route }) {
 34  const { user } = useContext(AuthContext);
 35  const routeUserId = route.params?.userId || user?.id;
 36  const role = route.params?.role || user?.role;
 37  const [loading, setLoading] = useState(false);
 38  const [produces, setProduces] = useState([]);
 39  const [refreshing, setRefreshing] = useState(false);
 40
 41  const CACHE_KEY = `@inventory_${routeUserId}`;
 42
 43  const loadInventory = useCallback(
 44    async (isRefreshing = false) => {
 45      if (!isRefreshing) setLoading(true);
 46
 47      try {
 48        const cachedData = await AsyncStorage.getItem(CACHE_KEY);
 49        if (cachedData && !isRefreshing) {
 50          const parsedData = JSON.parse(cachedData);
 51          parsedData.sort(sortByLastAction);
 52          setProduces(parsedData);
 53        }
 54      } catch (e) {
 55        console.error("Failed to load cache:", e);
 56      }
 57
 58      try {
 59        let fetchedProduces;
 60        if (role.toUpperCase() === "INSPECTOR") {
 61          const userKey = `INSPECTOR-${routeUserId}`;
 62          const { user: userDetails } = await api.getUserDetails(userKey);
 63          const inspectedIds = userDetails.inspectedProduce || [];
 64          const producePromises = inspectedIds.map((pid) =>
 65            api.getProduceById(pid).catch(() => null)
 66          );
 67          const results = await Promise.all(producePromises);
 68          fetchedProduces = results
 69            .filter((res) => res && res.produce)
 70            .map((res) => res.produce);
 71        } else {
 72          const { produces: ownerProduces } =
 73            await api.getProduceByOwner(routeUserId);
 74          fetchedProduces = ownerProduces || [];
 75        }
 76
 77        fetchedProduces.sort(sortByLastAction);
 78        setProduces(fetchedProduces);
 79        await AsyncStorage.setItem(CACHE_KEY, JSON.stringify(fetchedProduces));
 80      } catch (err) {
 81        if (!produces.length) Alert.alert("Error", "Failed to fetch inventory");
 82        console.error("Network fetch failed:", err.message);
 83      } finally {
 84        if (!isRefreshing) setLoading(false);
 85      }
 86    },
 87    [role, routeUserId, CACHE_KEY]
 88  );
 89
 90  useEffect(() => {
 91    loadInventory();
 92  }, [loadInventory]);
 93
 94  const onRefresh = useCallback(async () => {
 95    setRefreshing(true);
 96    await loadInventory(true);
 97    setRefreshing(false);
 98  }, [loadInventory]);
 99
100  return (
101    <SafeAreaView style={styles.container}>
102      <ScreenHeader
103        title={route.params?.title || "My Inventory"}
104        navigation={navigation}
105        role={role}
106        hideSearchButton={true}
107        hideNotificationsButton={true}
108        showBack={true}
109      />
110      <ScrollView
111        contentContainerStyle={{ padding: 12 }}
112        refreshControl={
113          <RefreshControl
114            refreshing={refreshing}
115            onRefresh={onRefresh}
116            colors={[colors.darkGreen]}
117            tintColor={colors.darkGreen}
118          />
119        }
120      >
121        {loading && produces.length === 0 ? (
122          <View style={{ alignItems: "center", padding: 20 }}>
123            <ActivityIndicator size="large" />
124            <Text>Loading Inventory...</Text>
125          </View>
126        ) : (
127          <>
128            {produces.length === 0 ? (
129              <View style={{ marginTop: 20, alignItems: "center" }}>
130                <Text style={{ color: colors.gray }}>No produce found</Text>
131              </View>
132            ) : (
133              produces.map((p) => <ProduceCard key={p.id} produce={p} />)
134            )}
135          </>
136        )}
137      </ScrollView>
138    </SafeAreaView>
139  );
140}