matiru
Blockchain-Based Supply Chain Transparency for Agricultural Producegit clone https://github.com/maydayv7/matiru
frontend/screens/Distributor.js (6482B)
1import { useContext, useState } 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 TransferOwnershipForm from "../components/TransferOwnershipForm";
17
18import { api } from "../services/api";
19import styles from "../styles";
20import { AuthContext } from "../AuthContext";
21
22export default function DistributorScreen({ navigation, route }) {
23 const { user } = useContext(AuthContext);
24 const userId = route.params?.userId || user?.id;
25 const token = user?.token;
26
27 const [active, setActive] = useState(null);
28 const [produceId, setProduceId] = useState("");
29 const [location, setLocation] = useState("");
30 const [reason, setReason] = useState("");
31 const [newStatus, setNewStatus] = useState("");
32 const [storageConditions, setStorageConditions] = useState("");
33
34 const resetCommon = () => {
35 setProduceId("");
36 };
37
38 const updateLocation = async () => {
39 try {
40 await api.updateLocation(
41 {
42 produceId,
43 actorId: userId,
44 newLocation: location,
45 },
46 token
47 );
48 Alert.alert("Location Updated", "Location updated successfully");
49 resetCommon();
50 setLocation("");
51 setActive(null);
52 } catch (err) {
53 Alert.alert("Error", err.message);
54 }
55 };
56
57 const transferOwnership = async ({
58 produceId,
59 newOwnerId,
60 qty,
61 salePrice,
62 onComplete,
63 }) => {
64 if (!produceId || !newOwnerId || !qty)
65 return Alert.alert(
66 "Error",
67 "Please provide Produce ID, New Owner ID, and Quantity."
68 );
69 try {
70 await api.transferOwnership(
71 {
72 produceId,
73 newOwnerId,
74 qty: parseFloat(qty),
75 salePrice: parseFloat(salePrice),
76 },
77 token
78 );
79 Alert.alert("Transferred", "Ownership transferred successfully");
80 onComplete();
81 setActive(null);
82 } catch (err) {
83 Alert.alert("Error", err.message);
84 }
85 };
86
87 const markAsUnavailable = async () => {
88 try {
89 await api.markAsUnavailable(
90 { produceId, actorId: userId, reason, newStatus },
91 token
92 );
93 Alert.alert("Marked Unavailable", "Produce marked as unavailable");
94 resetCommon();
95 setReason("");
96 setNewStatus("");
97 setActive(null);
98 } catch (err) {
99 Alert.alert("Error", err.message);
100 }
101 };
102
103 const updateStorageConditions = async () => {
104 try {
105 await api.updateDetails(
106 {
107 produceId,
108 actorId: userId,
109 details: {
110 storageConditions: storageConditions
111 ? storageConditions.split(",").map((c) => c.trim())
112 : [],
113 },
114 },
115 token
116 );
117 Alert.alert("Updated", "Storage conditions updated successfully");
118 resetCommon();
119 setStorageConditions("");
120 setActive(null);
121 } catch (err) {
122 Alert.alert("Error", err.message);
123 }
124 };
125
126 return (
127 <SafeAreaView style={styles.container}>
128 <ScreenHeader
129 title="Distributor Dashboard"
130 navigation={navigation}
131 role="Distributor"
132 />
133 <ScrollView>
134 <View style={styles.actionGrid}>
135 <ActionButton
136 icon="map-marker"
137 text="Update Location"
138 onPress={() => setActive("location")}
139 />
140 <ActionButton
141 icon="cash"
142 text="Transfer Ownership"
143 onPress={() => setActive("transfer")}
144 />
145 <ActionButton
146 icon="cancel"
147 text="Mark Unavailable"
148 onPress={() => setActive("remove")}
149 />
150 <ActionButton
151 icon="thermometer"
152 text="Update Storage"
153 onPress={() => setActive("storage")}
154 />
155 </View>
156
157 {active === "location" && (
158 <View>
159 <Scanner value={produceId} onChange={setProduceId} />
160 <LocationPicker value={location} onChange={setLocation} />
161 <TouchableOpacity
162 style={styles.primaryButton}
163 onPress={updateLocation}
164 >
165 <Text style={styles.buttonText}>Update Location</Text>
166 </TouchableOpacity>
167 </View>
168 )}
169
170 {active === "transfer" && (
171 <TransferOwnershipForm onSubmit={transferOwnership} />
172 )}
173
174 {active === "remove" && (
175 <View>
176 <Scanner value={produceId} onChange={setProduceId} />
177 <TextInput
178 style={styles.input}
179 placeholder="Reason"
180 value={reason}
181 onChangeText={setReason}
182 />
183 <TextInput
184 style={styles.input}
185 placeholder="New Status (e.g. Removed, Missing)"
186 value={newStatus}
187 onChangeText={setNewStatus}
188 />
189 <TouchableOpacity
190 style={styles.primaryButton}
191 onPress={markAsUnavailable}
192 >
193 <Text style={styles.buttonText}>Mark as Unavailable</Text>
194 </TouchableOpacity>
195 </View>
196 )}
197
198 {active === "storage" && (
199 <View>
200 <Scanner value={produceId} onChange={setProduceId} />
201 <TextInput
202 style={styles.input}
203 placeholder="Storage Conditions (comma separated)"
204 value={storageConditions}
205 onChangeText={setStorageConditions}
206 />
207 <TouchableOpacity
208 style={styles.primaryButton}
209 onPress={updateStorageConditions}
210 >
211 <Text style={styles.buttonText}>Update Storage Conditions</Text>
212 </TouchableOpacity>
213 </View>
214 )}
215 </ScrollView>
216
217 <View style={{ padding: 12 }}>
218 <TouchableOpacity
219 style={styles.secondaryButton}
220 onPress={() =>
221 navigation.navigate("Inventory", {
222 userId,
223 role: "Distributor",
224 })
225 }
226 >
227 <Text style={styles.secondaryButtonText}>View Inventory</Text>
228 </TouchableOpacity>
229 </View>
230 </SafeAreaView>
231 );
232}