matiru

Blockchain-Based Supply Chain Transparency for Agricultural Produce

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};