matiru

Blockchain-Based Supply Chain Transparency for Agricultural Produce

frontend/screens/Farmer.js (11649B)


  1import { useContext, useState, useRef } from "react";
  2import {
  3  Alert,
  4  ScrollView,
  5  Text,
  6  TextInput,
  7  TouchableOpacity,
  8  View,
  9} from "react-native";
 10import { SafeAreaView } from "react-native-safe-area-context";
 11
 12import ScreenHeader from "../components/ScreenHeader";
 13import ActionButton from "../components/ActionButton";
 14import Scanner from "../components/Scanner";
 15import LocationPicker from "../components/LocationPicker";
 16import QRModal from "../components/QRModal";
 17import ImageUploader from "../components/ImageUploader";
 18import DatePicker from "../components/DatePicker";
 19import TransferOwnershipForm from "../components/TransferOwnershipForm";
 20
 21import { api } from "../services/api";
 22import styles from "../styles";
 23import { AuthContext } from "../AuthContext";
 24
 25export default function FarmerScreen({ navigation, route }) {
 26  const { user } = useContext(AuthContext);
 27  const userId = route.params?.userId || user?.id;
 28  const token = user?.token;
 29
 30  const [active, setActive] = useState(null);
 31  const imageUploaderRef = useRef(null);
 32
 33  const [produceId, setProduceId] = useState("");
 34  const [location, setLocation] = useState("");
 35  const [pricePerUnit, setPricePerUnit] = useState("");
 36  const [storageConditions, setStorageConditions] = useState("");
 37  const [certification, setCertification] = useState("");
 38
 39  const [cropType, setCropType] = useState("");
 40  const [qty, setQty] = useState("");
 41  const [qtyUnit, setQtyUnit] = useState("KG");
 42  const [harvestDate, setHarvestDate] = useState("");
 43  const [quality, setQuality] = useState("");
 44  const [expiryDate, setExpiryDate] = useState("");
 45
 46  const [splitQty, setSplitQty] = useState("");
 47
 48  const [qrVisible, setQrVisible] = useState(false);
 49  const [lastProduceId, setLastProduceId] = useState(null);
 50
 51  const resetRegisterForm = () => {
 52    setCropType("");
 53    setQty("");
 54    setQtyUnit("KG");
 55    setPricePerUnit("");
 56    setHarvestDate("");
 57    setQuality("");
 58    setExpiryDate("");
 59    setStorageConditions("");
 60    setCertification("");
 61    setLocation("");
 62    imageUploaderRef.current?.reset();
 63  };
 64
 65  const resetCommon = () => {
 66    setProduceId("");
 67  };
 68
 69  const registerProduce = async () => {
 70    try {
 71      const imageUrl = await imageUploaderRef.current?.upload();
 72      const { produce } = await api.registerProduce(
 73        {
 74          farmerId: userId,
 75          details: {
 76            imageUrl,
 77            cropType,
 78            qty: parseFloat(qty) || 0,
 79            qtyUnit,
 80            pricePerUnit: parseFloat(pricePerUnit) || 0,
 81            harvestDate,
 82            quality,
 83            expiryDate,
 84            storageConditions: storageConditions
 85              ? storageConditions.split(",").map((c) => c.trim())
 86              : [],
 87            certification: certification
 88              ? certification.split(",").map((c) => c.trim())
 89              : [],
 90            location,
 91          },
 92        },
 93        token
 94      );
 95      setLastProduceId(produce.id);
 96      setQrVisible(true);
 97      resetRegisterForm();
 98      setActive(null);
 99    } catch (err) {
100      Alert.alert("Error", err.message);
101    }
102  };
103
104  const updateDetails = async () => {
105    if (!produceId) return Alert.alert("Error", "Please enter Produce ID");
106    try {
107      await api.updateDetails(
108        {
109          produceId,
110          actorId: userId,
111          details: {
112            pricePerUnit: pricePerUnit ? parseFloat(pricePerUnit) : undefined,
113            storageConditions: storageConditions
114              ? storageConditions.split(",").map((c) => c.trim())
115              : undefined,
116            certification: certification
117              ? certification.split(",").map((c) => c.trim())
118              : undefined,
119          },
120        },
121        token
122      );
123      Alert.alert("Updated", "Produce details updated successfully");
124      resetCommon();
125      setPricePerUnit("");
126      setStorageConditions("");
127      setCertification("");
128      setActive(null);
129    } catch (err) {
130      Alert.alert("Error", err.message);
131    }
132  };
133
134  const splitProduce = async () => {
135    if (!produceId || !splitQty)
136      return Alert.alert("Error", "Please enter Produce ID and quantity");
137    try {
138      await api.splitProduce(
139        {
140          produceId,
141          qty: parseFloat(splitQty),
142          ownerId: userId,
143        },
144        token
145      );
146      Alert.alert("Split", "Produce split successfully");
147      setSplitQty("");
148      resetCommon();
149      setActive(null);
150    } catch (err) {
151      Alert.alert("Error", err.message);
152    }
153  };
154
155  const updateLocation = async () => {
156    if (!produceId || !location)
157      return Alert.alert("Error", "Please enter Produce ID and location");
158    try {
159      await api.updateLocation(
160        {
161          produceId,
162          actorId: userId,
163          newLocation: location,
164        },
165        token
166      );
167      Alert.alert("Moved", "Location updated successfully");
168      resetCommon();
169      setLocation("");
170      setActive(null);
171    } catch (err) {
172      Alert.alert("Error", err.message);
173    }
174  };
175
176  const transferOwnership = async ({
177    produceId,
178    newOwnerId,
179    qty,
180    salePrice,
181    onComplete,
182  }) => {
183    if (!produceId || !newOwnerId || !qty)
184      return Alert.alert(
185        "Error",
186        "Please provide Produce ID, New Owner ID, and Quantity."
187      );
188    try {
189      await api.transferOwnership(
190        {
191          produceId,
192          newOwnerId,
193          qty: parseFloat(qty),
194          salePrice: salePrice ? parseFloat(salePrice) : 0,
195        },
196        token
197      );
198      Alert.alert("Transferred", "Ownership transferred successfully");
199      onComplete();
200      setActive(null);
201    } catch (err) {
202      Alert.alert("Error", err.message);
203    }
204  };
205
206  return (
207    <SafeAreaView style={styles.container}>
208      <ScreenHeader
209        title="Farmer Dashboard"
210        navigation={navigation}
211        role="Farmer"
212      />
213      <ScrollView>
214        <View style={styles.actionGrid}>
215          <ActionButton
216            icon="plus"
217            text="Register Produce"
218            onPress={() => setActive("register")}
219          />
220          <ActionButton
221            icon="update"
222            text="Update Details"
223            onPress={() => setActive("update")}
224          />
225          <ActionButton
226            icon="call-split"
227            text="Split Produce"
228            onPress={() => setActive("split")}
229          />
230          <ActionButton
231            icon="map-marker"
232            text="Update Location"
233            onPress={() => setActive("location")}
234          />
235          <ActionButton
236            icon="cash"
237            text="Transfer Ownership"
238            onPress={() => setActive("transfer")}
239          />
240        </View>
241
242        {active === "register" && (
243          <View>
244            <TextInput
245              style={styles.input}
246              placeholder="Crop Type"
247              value={cropType}
248              onChangeText={setCropType}
249            />
250            <TextInput
251              style={styles.input}
252              placeholder="Quantity"
253              keyboardType="numeric"
254              value={qty}
255              onChangeText={setQty}
256            />
257            <TextInput
258              style={styles.input}
259              placeholder="Unit (KG/Number)"
260              value={qtyUnit}
261              onChangeText={setQtyUnit}
262            />
263            <TextInput
264              style={styles.input}
265              placeholder="Price Per Unit"
266              keyboardType="numeric"
267              value={pricePerUnit}
268              onChangeText={setPricePerUnit}
269            />
270            <DatePicker
271              label="Harvest Date"
272              value={harvestDate}
273              onChange={setHarvestDate}
274            />
275            <TextInput
276              style={styles.input}
277              placeholder="Quality"
278              value={quality}
279              onChangeText={setQuality}
280            />
281            <DatePicker
282              label="Expiry Date"
283              value={expiryDate}
284              onChange={setExpiryDate}
285            />
286            <TextInput
287              style={styles.input}
288              placeholder="Storage Conditions (comma separated)"
289              value={storageConditions}
290              onChangeText={setStorageConditions}
291            />
292            <TextInput
293              style={styles.input}
294              placeholder="Certifications (comma separated)"
295              value={certification}
296              onChangeText={setCertification}
297            />
298            <LocationPicker value={location} onChange={setLocation} />
299            <ImageUploader ref={imageUploaderRef} token={token} />
300            <TouchableOpacity
301              style={styles.primaryButton}
302              onPress={registerProduce}
303            >
304              <Text style={styles.buttonText}>Register Produce</Text>
305            </TouchableOpacity>
306          </View>
307        )}
308
309        {active === "update" && (
310          <View>
311            <Scanner value={produceId} onChange={setProduceId} />
312            <TextInput
313              style={styles.input}
314              placeholder="New Price Per Unit"
315              keyboardType="numeric"
316              value={pricePerUnit}
317              onChangeText={setPricePerUnit}
318            />
319            <TextInput
320              style={styles.input}
321              placeholder="Storage Conditions (comma separated)"
322              value={storageConditions}
323              onChangeText={setStorageConditions}
324            />
325            <TextInput
326              style={styles.input}
327              placeholder="Certifications (comma separated)"
328              value={certification}
329              onChangeText={setCertification}
330            />
331            <TouchableOpacity
332              style={styles.primaryButton}
333              onPress={updateDetails}
334            >
335              <Text style={styles.buttonText}>Update Details</Text>
336            </TouchableOpacity>
337          </View>
338        )}
339
340        {active === "split" && (
341          <View>
342            <Scanner value={produceId} onChange={setProduceId} />
343            <TextInput
344              style={styles.input}
345              placeholder="Quantity to Split"
346              keyboardType="numeric"
347              value={splitQty}
348              onChangeText={setSplitQty}
349            />
350            <TouchableOpacity
351              style={styles.primaryButton}
352              onPress={splitProduce}
353            >
354              <Text style={styles.buttonText}>Split Produce</Text>
355            </TouchableOpacity>
356          </View>
357        )}
358
359        {active === "location" && (
360          <View>
361            <Scanner value={produceId} onChange={setProduceId} />
362            <LocationPicker value={location} onChange={setLocation} />
363            <TouchableOpacity
364              style={styles.primaryButton}
365              onPress={updateLocation}
366            >
367              <Text style={styles.buttonText}>Update Location</Text>
368            </TouchableOpacity>
369          </View>
370        )}
371
372        {active === "transfer" && (
373          <TransferOwnershipForm onSubmit={transferOwnership} />
374        )}
375      </ScrollView>
376
377      <QRModal
378        visible={qrVisible}
379        onClose={() => setQrVisible(false)}
380        value={lastProduceId || ""}
381      />
382
383      <View style={{ padding: 12 }}>
384        <TouchableOpacity
385          style={styles.secondaryButton}
386          onPress={() =>
387            navigation.navigate("Inventory", { userId: userId, role: "Farmer" })
388          }
389        >
390          <Text style={styles.secondaryButtonText}>View Inventory</Text>
391        </TouchableOpacity>
392      </View>
393    </SafeAreaView>
394  );
395}