diff --git a/app/_layout.tsx b/app/_layout.tsx
index 3e45276..a826b32 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -47,12 +47,10 @@ function AuthenticatedLayout() {
- {Platform.select({
+ {Platform.select({
//ios: ,
android: (
- }
- />
+ } />
),
})}
diff --git a/app/search/_layout.tsx b/app/search/_layout.tsx
index 3a7b906..c0fe6df 100644
--- a/app/search/_layout.tsx
+++ b/app/search/_layout.tsx
@@ -11,6 +11,8 @@ export default function SearchLayout() {
headerSearchBarOptions: {
placement: "automatic",
placeholder: "Search",
+ autoFocus: true,
+ shouldShowHintSearchIcon: true,
onChangeText: (event) => {
router.setParams({
globalSearch: event?.nativeEvent?.text,
diff --git a/app/search/index.tsx b/app/search/index.tsx
index 6193cb1..7b45230 100644
--- a/app/search/index.tsx
+++ b/app/search/index.tsx
@@ -1,12 +1,4 @@
-import { useLocalSearchParams } from "expo-router";
-import { ScrollView } from "react-native";
-import { Text } from "react-native-paper";
-
+import GlobalSearch from "../../components/global-search/global-search";
export default function SearchIndex() {
- const { globalSearch } = useLocalSearchParams();
- return (
-
- Some search results here for: {globalSearch}
-
- );
+ return ;
}
diff --git a/babel-translations.babel b/babel-translations.babel
index 3eb8845..3fd190c 100644
--- a/babel-translations.babel
+++ b/babel-translations.babel
@@ -131,6 +131,27 @@
labels
+
+ error
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
na
false
@@ -152,6 +173,58 @@
+
+ uploadprogress
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
+
+
+
+ globalsearch
+
+
+ labels
+
+
+ entersearch
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
@@ -4932,6 +5005,27 @@
+
+ wronginfo
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
wrongpassword
false
diff --git a/components/global-search/global-search.jsx b/components/global-search/global-search.jsx
new file mode 100644
index 0000000..2615b95
--- /dev/null
+++ b/components/global-search/global-search.jsx
@@ -0,0 +1,109 @@
+import axios from "axios";
+import { useLocalSearchParams } from "expo-router";
+import debounce from "lodash/debounce";
+import { useCallback, useEffect, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { FlatList, View } from "react-native";
+import { ActivityIndicator, Text } from "react-native-paper";
+import env from "../../env";
+import ErrorDisplay from "../error/error-display";
+import JobListItem from "../jobs-list/job-list-item";
+
+// Debounce delay (ms) – adjust as needed
+const GLOBAL_SEARCH_DEBOUNCE_MS = 400;
+
+/**
+ * Hook returning a debounced search trigger.
+ * It recreates the debounced function only when the underlying callback changes.
+ * Placeholder: Replace the body of `performSearch` with real API / GraphQL logic.
+ */
+function useDebouncedGlobalSearch(onSearch, delay = GLOBAL_SEARCH_DEBOUNCE_MS) {
+ const debouncedRef = useRef(() => {});
+
+ useEffect(() => {
+ // Create debounced wrapper
+ const debounced = debounce((query) => {
+ onSearch(query);
+ }, delay);
+ debouncedRef.current = debounced;
+ return () => debounced.cancel();
+ }, [onSearch, delay]);
+
+ return useCallback((query) => {
+ debouncedRef.current && debouncedRef.current(query);
+ }, []);
+}
+
+export default function GlobalSearch() {
+ const { globalSearch } = useLocalSearchParams();
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [results, setResults] = useState([]);
+ const { t } = useTranslation();
+ // Placeholder: Replace with actual API call (e.g., Apollo client query, REST fetch, Redux saga dispatch)
+ const performSearch = useCallback(async (query) => {
+ // Defensive trimr
+ const q = (query || "").trim();
+ if (!q) return;
+ setLoading(true);
+ setError(null);
+ // TODO: Integrate real search endpoint
+ console.log(`[GlobalSearch] (debounced placeholder) searching for: "${q}"`);
+ try {
+ const searchData = await axios.post(`${env.API_URL}/search`, {
+ search: q,
+ });
+
+ if (searchData.data) {
+ const jobResults = searchData.data?.hits?.hits
+ ?.filter((hit) => hit._index === "jobs")
+ .map((hit) => hit._source);
+
+ setResults(jobResults);
+ } else {
+ setError("No results available. Try again.");
+ }
+ } catch (error) {
+ console.error("Search error:", error);
+ setError(error.message);
+ }
+ setLoading(false);
+ }, []);
+
+ const debouncedSearch = useDebouncedGlobalSearch(performSearch);
+
+ // Trigger debounced search when the route param changes
+ useEffect(() => {
+ if (typeof globalSearch === "string" && globalSearch.length > 0) {
+ debouncedSearch(globalSearch);
+ }
+ }, [globalSearch, debouncedSearch]);
+
+ if (globalSearch === undefined || globalSearch.trim() === "") {
+ return (
+
+
+ {t("globalsearch.labels.entersearch")}
+
+
+ );
+ }
+
+ return (
+
+ {loading && }
+ {error && }
+
+
+ {results.length} results found
+
+
+ item.id?.toString()}
+ renderItem={(object) => }
+ />
+
+ );
+}
diff --git a/components/jobs-list/job-list-item.jsx b/components/jobs-list/job-list-item.jsx
index b104385..3f55e6a 100644
--- a/components/jobs-list/job-list-item.jsx
+++ b/components/jobs-list/job-list-item.jsx
@@ -3,7 +3,7 @@ import { useRouter } from "expo-router";
import React, { memo, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { Pressable, StyleSheet, View } from "react-native";
-import { IconButton, Text, useTheme } from "react-native-paper";
+import { Chip, IconButton, Text, useTheme } from "react-native-paper";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.analytics";
@@ -86,6 +86,7 @@ function JobListItemComponent({ openImagePicker, item }) {
{vehicle}
)}
+ {item.status}