commit ba3380a4ffce48f092c067cb35a53ee8ca70dd85
parent eeca2211851b288129b638fb5252b42a466a4568
Author: maydayv7 <maydayv7@gmail.com>
Date: Sun, 5 Oct 2025 22:54:41 +0530
feat(frontend): Add Date Picker
Diffstat:
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={{