commit 8e01d04de7f2ee3c3f2c9394129fa634d5cfa378
parent e1e3e3debcf35fc84e6096a2f40c1b155e8c3743
Author: maydayv7 <maydayv7@gmail.com>
Date: Sun, 5 Oct 2025 15:54:59 +0530
fix(frontend): Search overflow
Diffstat:
5 files changed, 136 insertions(+), 113 deletions(-)
diff --git a/chaincode/index.js b/chaincode/index.js
@@ -99,11 +99,11 @@ class ProduceContract extends Contract {
}
// Update location
- async updateLocation(ctx, produceId, actorId, newLocationOrInTransit) {
+ async updateLocation(ctx, produceId, actorId, newLocation) {
const produce = await this._getState(ctx, produceId);
- if (newLocationOrInTransit === "In Transit") produce.status = "In Transit";
+ if (newLocation === "In Transit") produce.status = "In Transit";
else {
- produce.currentLocation = newLocationOrInTransit;
+ produce.currentLocation = newLocation;
if (produce.status === "Harvested") produce.status = "In Transit";
}
diff --git a/frontend/components/LocationPicker.js b/frontend/components/LocationPicker.js
@@ -14,7 +14,7 @@ export default function LocationPicker({ value, onChange }) {
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
- Alert.alert("Permission denied", "Enable location services.");
+ Alert.alert("Permission denied", "Enable location services");
return;
}
diff --git a/frontend/components/Scanner.js b/frontend/components/Scanner.js
@@ -27,7 +27,7 @@ export default function Scanner({ value, onChange }) {
const handleScanned = ({ data }) => {
if (!data) {
- Alert.alert("Scan Failed", "No data found in QR code.");
+ Alert.alert("Scan Failed", "No data found in QR code");
return;
}
diff --git a/frontend/components/ScreenHeader.js b/frontend/components/ScreenHeader.js
@@ -27,7 +27,7 @@ export default function ScreenHeader({
/>
</TouchableOpacity>
) : (
- <View style={{ width: 28 }} /> // spacer
+ <View style={{ width: 28 }} />
)}
<Text style={styles.title}>{title}</Text>
diff --git a/frontend/screens/Search.js b/frontend/screens/Search.js
@@ -10,7 +10,10 @@ import {
TouchableOpacity,
View,
} from "react-native";
-import { SafeAreaView } from "react-native-safe-area-context";
+import {
+ SafeAreaView,
+ useSafeAreaInsets,
+} from "react-native-safe-area-context";
import RNPickerSelect from "react-native-picker-select";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import ScreenHeader from "../components/ScreenHeader";
@@ -34,6 +37,8 @@ const DetailRow = ({ icon, label, value }) => (
);
export default function SearchScreen({ navigation }) {
+ const insets = useSafeAreaInsets();
+
const [tab, setTab] = useState("Produce");
const [loading, setLoading] = useState(false);
const [result, setResult] = useState(null);
@@ -161,7 +166,7 @@ export default function SearchScreen({ navigation }) {
<DetailRow
icon="cash"
label="Price"
- value={`$${produce.pricePerUnit} / ${produce.qtyUnit}`}
+ value={`₹${produce.pricePerUnit} / ${produce.qtyUnit}`}
/>
<DetailRow
icon="account-circle-outline"
@@ -171,16 +176,20 @@ export default function SearchScreen({ navigation }) {
</View>
</View>
<View style={local.splitRow}>
- <DetailRow
- icon="calendar-arrow-left"
- label="Date of Harvest"
- value={new Date(produce.harvestDate).toLocaleDateString()}
- />
- <DetailRow
- icon="calendar-arrow-right"
- label="Date of Expiry"
- value={new Date(produce.expiryDate).toLocaleDateString()}
- />
+ <View style={{ flex: 1, marginRight: 8 }}>
+ <DetailRow
+ icon="calendar-arrow-left"
+ label="Date of Harvest"
+ value={new Date(produce.harvestDate).toLocaleDateString()}
+ />
+ </View>
+ <View style={{ flex: 1, marginLeft: 8 }}>
+ <DetailRow
+ icon="calendar-arrow-right"
+ label="Date of Expiry"
+ value={new Date(produce.expiryDate).toLocaleDateString()}
+ />
+ </View>
</View>
<View style={local.badgesRow}>
<View style={{ flexDirection: "row", flexWrap: "wrap" }}>
@@ -243,108 +252,122 @@ export default function SearchScreen({ navigation }) {
);
return (
- <SafeAreaView style={styles.container}>
- <ScreenHeader
- title="Global Search"
- navigation={navigation}
- hideSearchButton={true}
- showBack={true}
- />
+ <SafeAreaView style={styles.container} edges={["top", "left", "right"]}>
+ <ScrollView
+ style={{ flex: 1 }}
+ contentContainerStyle={{
+ flexGrow: 1,
+ paddingBottom: insets.bottom,
+ paddingHorizontal: 4,
+ }}
+ keyboardShouldPersistTaps="handled"
+ nestedScrollEnabled={true}
+ showsVerticalScrollIndicator={true}
+ scrollIndicatorInsets={{ bottom: insets.bottom }}
+ >
+ <ScreenHeader
+ title="Global Search"
+ navigation={navigation}
+ hideSearchButton={true}
+ showBack={true}
+ />
- <View style={local.searchContainer}>
- <Text
- style={{
- marginBottom: 12,
- color: colors.darkGreen,
- textAlign: "center",
- }}
- >
- Search for information about any produce or user in the supply chain
- </Text>
- <View style={{ flexDirection: "row", justifyContent: "space-around" }}>
- {["Produce", "User"].map((t) => (
- <TouchableOpacity
- key={t}
- style={[
- local.tabButton,
- {
- backgroundColor:
- tab === t ? colors.darkGreen : colors.lightGreen,
- },
- ]}
- onPress={() => {
- setTab(t);
- setResult(null);
- setProduceId("");
- setUserId("");
- setUserRole("FARMER");
- }}
- >
- <MaterialCommunityIcons
- name={t === "Produce" ? "leaf" : "account-badge"}
- size={20}
- color={tab === t ? "white" : colors.darkGreen}
- />
- <Text
+ <View style={local.searchContainer}>
+ <Text
+ style={{
+ marginBottom: 12,
+ color: colors.darkGreen,
+ textAlign: "center",
+ }}
+ >
+ Search for information about any produce or user in the supply chain
+ </Text>
+
+ <View
+ style={{ flexDirection: "row", justifyContent: "space-around" }}
+ >
+ {["Produce", "User"].map((t) => (
+ <TouchableOpacity
+ key={t}
style={[
- local.tabText,
- { color: tab === t ? "white" : colors.darkGreen },
+ local.tabButton,
+ {
+ backgroundColor:
+ tab === t ? colors.darkGreen : colors.lightGreen,
+ },
]}
+ onPress={() => {
+ setTab(t);
+ setResult(null);
+ setProduceId("");
+ setUserId("");
+ setUserRole("FARMER");
+ }}
>
- {t}
- </Text>
- </TouchableOpacity>
- ))}
- </View>
-
- {tab === "Produce" ? (
- <Scanner value={produceId} onChange={setProduceId} />
- ) : (
- <View>
- <Text style={[local.detailLabel, { marginTop: 20 }]}>
- 1. Select Role
- </Text>
- <RNPickerSelect
- onValueChange={(value) => setUserRole(value)}
- items={[
- { label: "Farmer", value: "FARMER" },
- { label: "Distributor", value: "DISTRIBUTOR" },
- { label: "Retailer", value: "RETAILER" },
- { label: "Inspector", value: "INSPECTOR" },
- ]}
- style={pickerSelectStyles}
- value={userRole}
- useNativeAndroidPickerStyle={false}
- placeholder={{}}
- />
- <Text
- style={[local.detailLabel, { marginTop: 20, marginBottom: 6 }]}
- >
- 2. Enter User ID
- </Text>
- <TextInput
- style={styles.input}
- placeholder="e.g. farmer1, distributor_xyz"
- value={userId}
- onChangeText={setUserId}
- />
+ <MaterialCommunityIcons
+ name={t === "Produce" ? "leaf" : "account-badge"}
+ size={20}
+ color={tab === t ? "white" : colors.darkGreen}
+ />
+ <Text
+ style={[
+ local.tabText,
+ { color: tab === t ? "white" : colors.darkGreen },
+ ]}
+ >
+ {t}
+ </Text>
+ </TouchableOpacity>
+ ))}
</View>
- )}
- <TouchableOpacity
- style={[styles.primaryButton, { marginTop: 20 }]}
- onPress={fetchResult}
- disabled={loading}
- >
- {loading ? (
- <ActivityIndicator color="white" />
+ {tab === "Produce" ? (
+ <Scanner value={produceId} onChange={setProduceId} />
) : (
- <Text style={styles.buttonText}>Search</Text>
+ <View>
+ <Text style={[local.detailLabel, { marginTop: 20 }]}>
+ 1. Select Role
+ </Text>
+ <RNPickerSelect
+ onValueChange={(value) => setUserRole(value)}
+ items={[
+ { label: "Farmer", value: "FARMER" },
+ { label: "Distributor", value: "DISTRIBUTOR" },
+ { label: "Retailer", value: "RETAILER" },
+ { label: "Inspector", value: "INSPECTOR" },
+ ]}
+ style={pickerSelectStyles}
+ value={userRole}
+ useNativeAndroidPickerStyle={false}
+ placeholder={{}}
+ />
+ <Text
+ style={[local.detailLabel, { marginTop: 20, marginBottom: 6 }]}
+ >
+ 2. Enter User ID
+ </Text>
+ <TextInput
+ style={styles.input}
+ placeholder="e.g. farmer1, distributor_xyz"
+ value={userId}
+ onChangeText={setUserId}
+ />
+ </View>
)}
- </TouchableOpacity>
- </View>
- <ScrollView contentContainerStyle={{ paddingHorizontal: 4 }}>
+ <TouchableOpacity
+ style={[styles.primaryButton, { marginTop: 20 }]}
+ onPress={fetchResult}
+ disabled={loading}
+ >
+ {loading ? (
+ <ActivityIndicator color="white" />
+ ) : (
+ <Text style={styles.buttonText}>Search</Text>
+ )}
+ </TouchableOpacity>
+ </View>
+
{loading && (
<ActivityIndicator
size="large"
@@ -362,7 +385,7 @@ export default function SearchScreen({ navigation }) {
) : (
<View style={local.card}>
<Text style={{ textAlign: "center", fontWeight: "500" }}>
- No results found for the provided ID.
+ No results found for provided ID
</Text>
</View>
))}
@@ -447,7 +470,7 @@ const local = StyleSheet.create({
detailsContainer: {
flex: 1,
marginLeft: 16,
- height: "100%",
+ minHeight: 120,
justifyContent: "space-around",
},
detailItem: {