matiru

Blockchain-Based Supply Chain Transparency for Agricultural Produce
commit ba3380a4ffce48f092c067cb35a53ee8ca70dd85
parent eeca2211851b288129b638fb5252b42a466a4568
Author: maydayv7 <maydayv7@gmail.com>
Date:   Sun,  5 Oct 2025 22:54:41 +0530

feat(frontend): Add Date Picker

Diffstat:
Mbackend/package-lock.json | 1-
Mbackend/package.json | 1-
Mbackend/server.js | 5++---
Afrontend/components/DatePicker.js | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfrontend/components/LocationPicker.js | 11++---------
Mfrontend/package-lock.json | 24++++++++++++++++++++++++
Mfrontend/package.json | 1+
Mfrontend/screens/Farmer.js | 17++++++++---------
Mfrontend/screens/Home.js | 4+---
Mfrontend/screens/Inspector.js | 8++++----
10 files changed, 113 insertions(+), 30 deletions(-)

diff --git a/backend/package-lock.json b/backend/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "dependencies": { "bcryptjs": "^3.0.2", - "body-parser": "^2.2.0", "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", diff --git a/backend/package.json b/backend/package.json @@ -8,7 +8,6 @@ }, "dependencies": { "bcryptjs": "^3.0.2", - "body-parser": "^2.2.0", "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^5.1.0", diff --git a/backend/server.js b/backend/server.js @@ -1,7 +1,6 @@ "use strict"; require("dotenv").config(); const express = require("express"); -const bodyParser = require("body-parser"); const cors = require("cors"); const morgan = require("morgan"); const fs = require("fs"); @@ -81,7 +80,7 @@ function authenticateMiddleware(req, res, next) { // User Authentication // Prototype: Uses backend/users.json -app.post("/api/auth/login", bodyParser.json(), async (req, res) => { +app.post("/api/auth/login", express.json(), async (req, res) => { try { const { username, password } = req.body || {}; if (!username || !password) @@ -346,7 +345,7 @@ router.get("/getUser/:userKey", async (req, res) => { } }); -app.use("/api", authenticateMiddleware, bodyParser.json(), router); +app.use("/api", authenticateMiddleware, express.json(), router); const PORT = process.env.PORT || 4000; app.listen(PORT, "0.0.0.0", () => diff --git a/frontend/components/DatePicker.js b/frontend/components/DatePicker.js @@ -0,0 +1,71 @@ +import { useState, useEffect } from "react"; +import { Platform, Text, TouchableOpacity, View } from "react-native"; +import DateTimePicker from "@react-native-community/datetimepicker"; +import styles, { colors } from "../styles"; + +export default function DatePicker({ label, value, onChange }) { + const [show, setShow] = useState(false); + const [selected, setSelected] = useState(value ? new Date(value) : null); + + useEffect(() => { + if (!value) setSelected(null); + }, [value]); + + const handleChange = (event, selectedDate) => { + setShow(false); + if (event.type === "dismissed") return; + + if (selectedDate) { + setSelected(selectedDate); + const formatted = selectedDate.toISOString().split("T")[0]; + onChange(formatted); + } + }; + + return ( + <View style={{ marginVertical: 8 }}> + {label && ( + <Text + style={{ + color: colors.gray, + marginBottom: 4, + fontWeight: "600", + fontSize: 16, + }} + > + {label} + </Text> + )} + + <TouchableOpacity + onPress={() => setShow(true)} + style={[ + styles.input, + { + justifyContent: "center", + backgroundColor: "white", + }, + ]} + > + <Text + style={{ + fontSize: 16, + color: selected ? colors.gray : "#999", + }} + > + {selected ? `${selected.toISOString().split("T")[0]}` : "Select Date"} + </Text> + </TouchableOpacity> + + {show && ( + <DateTimePicker + value={selected || new Date()} + mode="date" + display={Platform.OS === "ios" ? "spinner" : "default"} + onChange={handleChange} + minimumDate={new Date()} + /> + )} + </View> + ); +} diff --git a/frontend/components/LocationPicker.js b/frontend/components/LocationPicker.js @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; import { View, TextInput, Button, Text, Alert } from "react-native"; import * as Location from "expo-location"; -import { colors } from "../styles"; +import styles, { colors } from "../styles"; export default function LocationPicker({ value, onChange }) { const [coords, setCoords] = useState(null); @@ -49,14 +49,7 @@ export default function LocationPicker({ value, onChange }) { onPress={pickLocation} /> <TextInput - style={{ - borderWidth: 1, - borderColor: colors.darkGreen, - borderRadius: 10, - padding: 12, - marginTop: 8, - backgroundColor: "white", - }} + style={[styles.input, { marginTop: 10 }]} placeholder="Enter Location Name" value={value} onChangeText={onChange} diff --git a/frontend/package-lock.json b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@expo/ngrok": "^4.1.3", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "^2.2.0", + "@react-native-community/datetimepicker": "^8.4.5", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", @@ -3031,6 +3032,29 @@ "react-native": "^0.0.0-0 || >=0.65 <1.0" } }, + "node_modules/@react-native-community/datetimepicker": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.4.5.tgz", + "integrity": "sha512-vvVOJAHjU8TFBzTUjQzANCL6C3pZSE2zjfutCATk790uz7ASEc2tOBD+EIG4BTelWtP2G9jqvXp2L7XGdhEBRg==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4" + }, + "peerDependencies": { + "expo": ">=52.0.0", + "react": "*", + "react-native": "*", + "react-native-windows": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + }, + "react-native-windows": { + "optional": true + } + } + }, "node_modules/@react-native-picker/picker": { "version": "2.11.2", "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.2.tgz", diff --git a/frontend/package.json b/frontend/package.json @@ -9,6 +9,7 @@ "@expo/ngrok": "^4.1.3", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "^2.2.0", + "@react-native-community/datetimepicker": "^8.4.5", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", diff --git a/frontend/screens/Farmer.js b/frontend/screens/Farmer.js @@ -14,6 +14,7 @@ import Scanner from "../components/Scanner"; import LocationPicker from "../components/LocationPicker"; import QRModal from "../components/QRModal"; import ImageUploader from "../components/ImageUploader"; +import DatePicker from "../components/DatePicker"; import { API_BASE } from "../config"; import styles from "../styles"; import { AuthContext } from "../AuthContext"; @@ -112,7 +113,7 @@ export default function FarmerScreen({ navigation, route }) { setLastProduceId(produced.id); setQrVisible(true); - Alert.alert("Registered", "Produce registered successfully"); + console.info("Registered", "Produce registered successfully"); resetRegisterForm(); resetCommon(); } catch (err) { @@ -317,11 +318,10 @@ export default function FarmerScreen({ navigation, route }) { value={pricePerUnit} onChangeText={setPricePerUnit} /> - <TextInput - style={styles.input} - placeholder="Harvest Date (YYYY-MM-DD)" + <DatePicker + label="Harvest Date" value={harvestDate} - onChangeText={setHarvestDate} + onChange={setHarvestDate} /> <TextInput style={styles.input} @@ -329,11 +329,10 @@ export default function FarmerScreen({ navigation, route }) { value={quality} onChangeText={setQuality} /> - <TextInput - style={styles.input} - placeholder="Expiry Date (YYYY-MM-DD)" + <DatePicker + label="Expiry Date" value={expiryDate} - onChangeText={setExpiryDate} + onChange={setExpiryDate} /> <TextInput style={styles.input} diff --git a/frontend/screens/Home.js b/frontend/screens/Home.js @@ -1,5 +1,3 @@ -// screens/Home.js -import React from "react"; import { ImageBackground, ScrollView, @@ -27,7 +25,7 @@ export default function HomeScreen({ navigation }) { <SafeAreaView style={{ flex: 1 }}> <ScrollView contentContainerStyle={styles.overlay}> <View style={styles.topContent}> - <Text style={styles.welcome}>Welcome to Matiru!</Text> + <Text style={styles.welcome}>Welcome!</Text> <TouchableOpacity style={[ diff --git a/frontend/screens/Inspector.js b/frontend/screens/Inspector.js @@ -12,6 +12,7 @@ import { SafeAreaView } from "react-native-safe-area-context"; import ScreenHeader from "../components/ScreenHeader"; import ActionButton from "../components/ActionButton"; import Scanner from "../components/Scanner"; +import DatePicker from "../components/DatePicker"; import { API_BASE } from "../config"; import styles, { colors } from "../styles"; import { AuthContext } from "../AuthContext"; @@ -103,11 +104,10 @@ export default function InspectorScreen({ navigation, route }) { value={quality} onChangeText={setQuality} /> - <TextInput - style={styles.input} - placeholder="Expiry Date (YYYY-MM-DD)" + <DatePicker + label="Expiry Date" value={expiryDate} - onChangeText={setExpiryDate} + onChange={setExpiryDate} /> <View style={{