matiru
Blockchain-Based Supply Chain Transparency for Agricultural Producegit clone https://github.com/maydayv7/matiru
frontend/components/ProduceCard.js (7066B)
1import { View, Text, Image, useWindowDimensions } from "react-native";
2import { MaterialCommunityIcons } from "@expo/vector-icons";
3import { colors } from "../styles";
4import DetailRow from "./DetailRow";
5
6export default function ProduceCard({ produce }) {
7 const { width } = useWindowDimensions();
8 const isSmallScreen = width < 380;
9 const status = (produce.status || "").toLowerCase();
10 const statusStyle = getStatusStyle(status);
11
12 return (
13 <View style={local.card}>
14 <View style={local.header}>
15 <Text style={local.refNoText}>Ref. ID </Text>
16 <Text style={local.refNoId} numberOfLines={2} ellipsizeMode="middle">
17 {produce.id}
18 </Text>
19 </View>
20
21 <View style={local.splitRow}>
22 <View style={{ flex: 1, marginRight: 8 }}>
23 <Text style={local.detailLabel}>Crop Type</Text>
24 <Text style={local.title}>{produce.cropType || "N/A"}</Text>
25 </View>
26 <View style={{ flex: 1, marginLeft: 8 }}>
27 <Text style={local.detailLabel}>Owner</Text>
28 <Text style={local.title}>{produce.currentOwner}</Text>
29 </View>
30 </View>
31
32 <View
33 style={[
34 local.mainContentRow,
35 isSmallScreen && {
36 flexDirection: "column",
37 alignItems: "flex-start",
38 },
39 ]}
40 >
41 {produce.imageUrl ? (
42 <Image
43 source={{ uri: produce.imageUrl }}
44 style={[
45 local.produceImage,
46 isSmallScreen && { width: "100%", height: 150, marginBottom: 12 },
47 ]}
48 resizeMode="cover"
49 />
50 ) : (
51 <View
52 style={[
53 local.produceImage,
54 local.imagePlaceholder,
55 isSmallScreen && { width: "100%", height: 150, marginBottom: 12 },
56 ]}
57 >
58 <MaterialCommunityIcons
59 name="image-off-outline"
60 size={40}
61 color={colors.midGreen}
62 />
63 </View>
64 )}
65
66 <View
67 style={[local.detailsContainer, isSmallScreen && { marginLeft: 0 }]}
68 >
69 <DetailRow
70 icon="weight"
71 label="Quantity"
72 value={`${produce.qty} ${produce.qtyUnit}`}
73 />
74 <DetailRow
75 icon="cash"
76 label="Price"
77 value={`₹${produce.pricePerUnit} / ${produce.qtyUnit}`}
78 />
79 <DetailRow
80 icon="shield-star"
81 label="Quality"
82 value={produce.quality || "N/A"}
83 />
84 <DetailRow
85 icon="thermometer"
86 label="Storage Conditions"
87 value={produce.storageConditions?.join(", ") || "N/A"}
88 />
89 </View>
90 </View>
91
92 <View style={local.splitRow}>
93 <View style={{ flex: 1, marginRight: 8 }}>
94 <DetailRow
95 icon="calendar-check"
96 label="Harvest Date"
97 value={
98 produce.harvestDate
99 ? new Date(produce.harvestDate).toLocaleDateString()
100 : "N/A"
101 }
102 />
103 </View>
104 <View style={{ flex: 1, marginLeft: 8 }}>
105 <DetailRow
106 icon="calendar-alert"
107 label="Expiry Date"
108 value={
109 produce.expiryDate
110 ? new Date(produce.expiryDate).toLocaleDateString()
111 : "N/A"
112 }
113 />
114 </View>
115 </View>
116
117 <View style={local.badgesRow}>
118 <View style={local.badgesLeft}>
119 {produce.certification?.length > 0 ? (
120 produce.certification.map((cert, idx) => (
121 <View key={idx} style={local.certPill}>
122 <MaterialCommunityIcons
123 name="shield-check"
124 size={16}
125 color={colors.darkGreen}
126 />
127 <Text style={local.certText}>{cert} </Text>
128 </View>
129 ))
130 ) : (
131 <Text style={{ color: colors.gray, fontSize: 13, flexShrink: 1 }}>
132 No certification
133 </Text>
134 )}
135 </View>
136
137 <View style={[local.statusContainer, statusStyle.container]}>
138 <Text style={[local.statusText, statusStyle.text]}>
139 {produce.status || "Unknown"}
140 </Text>
141 </View>
142 </View>
143 </View>
144 );
145}
146
147function getStatusStyle(status) {
148 switch (status) {
149 case "harvested":
150 return {
151 container: { backgroundColor: "#e4f8e4" },
152 text: { color: "#228b22" },
153 };
154 case "in transit":
155 return {
156 container: { backgroundColor: "#fff7d6" },
157 text: { color: "#c28c00" },
158 };
159 case "retail":
160 return {
161 container: { backgroundColor: "#e2f0ff" },
162 text: { color: "#005fb8" },
163 };
164 case "failed inspection":
165 case "removed":
166 case "missing":
167 return {
168 container: { backgroundColor: "#fde7e7" },
169 text: { color: "#d22" },
170 };
171 case "sold":
172 return {
173 container: { backgroundColor: "#f3e6ff" },
174 text: { color: "#7a3fc9" },
175 };
176 default:
177 return {
178 container: { backgroundColor: "#efefef" },
179 text: { color: colors.gray },
180 };
181 }
182}
183
184const local = {
185 card: {
186 backgroundColor: "white",
187 padding: 12,
188 borderRadius: 12,
189 marginBottom: 12,
190 shadowColor: "#000",
191 shadowOpacity: 0.05,
192 shadowRadius: 8,
193 elevation: 2,
194 },
195 header: {
196 flexDirection: "row",
197 justifyContent: "space-between",
198 alignItems: "flex-start",
199 marginBottom: 8,
200 },
201 refNoText: { color: colors.gray, fontSize: 12, marginRight: 8 },
202 refNoId: {
203 fontSize: 12,
204 fontWeight: "700",
205 textAlign: "right",
206 flexShrink: 1,
207 },
208 splitRow: { flexDirection: "row", marginTop: 8 },
209 detailLabel: { fontSize: 12, color: colors.gray },
210 title: { fontSize: 16, fontWeight: "700" },
211 mainContentRow: { flexDirection: "row", marginTop: 12, alignItems: "center" },
212 produceImage: {
213 width: 150,
214 height: 150,
215 borderRadius: 8,
216 marginRight: 30,
217 },
218 imagePlaceholder: {
219 backgroundColor: "#f4f7f4",
220 alignItems: "center",
221 justifyContent: "center",
222 },
223 detailsContainer: { flex: 1 },
224 badgesRow: {
225 flexDirection: "row",
226 justifyContent: "space-between",
227 alignItems: "flex-start",
228 marginTop: 12,
229 flexWrap: "wrap",
230 gap: 8,
231 },
232 badgesLeft: {
233 flexDirection: "row",
234 flexWrap: "wrap",
235 alignItems: "center",
236 gap: 8,
237 flex: 1,
238 minWidth: "60%",
239 },
240 certPill: {
241 flexDirection: "row",
242 alignItems: "center",
243 backgroundColor: "#f0f9f0",
244 borderRadius: 16,
245 paddingVertical: 4,
246 paddingHorizontal: 10,
247 },
248 certText: {
249 marginLeft: 6,
250 color: colors.gray,
251 fontSize: 13,
252 },
253 statusContainer: {
254 paddingVertical: 4,
255 paddingHorizontal: 10,
256 borderRadius: 6,
257 alignSelf: "flex-start",
258 },
259 statusText: {
260 fontWeight: "700",
261 fontSize: 13,
262 textTransform: "capitalize",
263 },
264};