From 9f65ca3c2f48462882ed10d805747150ecdb297a Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 28 Oct 2025 14:52:54 -0700 Subject: [PATCH] Add custom image loader and start job status. --- components/job-documents/image-loader.jsx | 43 +++++++++++++ components/job-documents/job-documents.jsx | 11 +--- components/job-tombstone/job-tombstone.jsx | 71 +++++++++++++++++++++- components/jobs-list/jobs-list.jsx | 2 +- graphql/bodyshop.queries.js | 1 + package-lock.json | 14 +++++ package.json | 3 +- 7 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 components/job-documents/image-loader.jsx diff --git a/components/job-documents/image-loader.jsx b/components/job-documents/image-loader.jsx new file mode 100644 index 0000000..fa36f11 --- /dev/null +++ b/components/job-documents/image-loader.jsx @@ -0,0 +1,43 @@ +import { useMemo, useState } from "react"; +import { Image } from "react-native"; +import { ActivityIndicator } from "react-native-paper"; + +const ImageLoader = ({ style, source, ...props }) => { + const [loading, setLoading] = useState(true); + + const handleLoadStart = () => { + setLoading(true); + }; + + const handleLoadEnd = () => { + setLoading(false); + }; + + const handleLoad = () => { + setLoading(false); + }; + + const memorizedImage = useMemo( + () => ( + + ), + [source] + ); + if (loading) + return ( + <> + + {memorizedImage} + + ); + return memorizedImage; +}; + +export default ImageLoader; diff --git a/components/job-documents/job-documents.jsx b/components/job-documents/job-documents.jsx index 2ef7558..9157df6 100644 --- a/components/job-documents/job-documents.jsx +++ b/components/job-documents/job-documents.jsx @@ -2,13 +2,7 @@ import cleanAxios from "@/util/CleanAxios"; import axios from "axios"; import { useGlobalSearchParams } from "expo-router"; import React, { useCallback, useEffect, useState } from "react"; -import { - FlatList, - Image, - RefreshControl, - TouchableOpacity, - View, -} from "react-native"; +import { FlatList, RefreshControl, TouchableOpacity, View } from "react-native"; import ImageView from "react-native-image-viewing"; import { ActivityIndicator, Text } from "react-native-paper"; import { connect } from "react-redux"; @@ -17,6 +11,7 @@ import env from "../../env"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { DetermineFileType } from "../../util/document-upload.utility"; import ErrorDisplay from "../error/error-display"; +import ImageLoader from "./image-loader"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -142,7 +137,7 @@ export function JobDocumentsComponent({ bodyshop }) { setPreviewVisible(true); }} > - ({}); +export default connect(mapStateToProps, mapDispatchToProps)(JobTombstone); + +function JobTombstone({ bodyshop }) { const { jobId } = useLocalSearchParams(); const { loading, error, data, refetch } = useQuery(GET_JOB_BY_PK, { variables: { @@ -15,6 +31,10 @@ export default function JobTombstone() { }, skip: !jobId, }); + const [visible, setVisible] = useState(false); + const openMenu = () => setVisible(true); + const closeMenu = () => setVisible(false); + console.log("JobTombstone render", visible); const theme = useTheme(); @@ -22,6 +42,42 @@ export default function JobTombstone() { const onRefresh = async () => { return refetch(); }; + + const [availableStatuses, setAvailableStatuses] = useState([]); + useEffect(() => { + if (!job || !bodyshop) return; + + const { md_ro_statuses } = bodyshop; + const { + pre_production_statuses, + production_statuses, + post_production_statuses, + statuses, + default_invoiced, + default_exported, + } = md_ro_statuses; + + // Handle non-parts entry scenarios based on job status + if (pre_production_statuses.includes(job.status)) { + setAvailableStatuses(pre_production_statuses); + } else if (production_statuses.includes(job.status)) { + setAvailableStatuses(production_statuses); + } else if (post_production_statuses.includes(job.status)) { + // Filter out invoiced and exported statuses for post-production + setAvailableStatuses( + post_production_statuses.filter( + (status) => status !== default_invoiced && status !== default_exported + ) + ); + } else { + // Default to all statuses if no specific restrictions apply + console.log( + "Status didn't match any restrictions. Allowing all status changes." + ); + setAvailableStatuses(statuses); + } + }, [job, bodyshop, setAvailableStatuses]); + if (loading) { return ; } @@ -50,6 +106,15 @@ export default function JobTombstone() { /> {job.status} + {job.status}} + > + {availableStatuses.map((status) => ( + {}} title={status} /> + ))} + {job.inproduction && ( {t("objects.jobs.labels.inproduction")} )} diff --git a/components/jobs-list/jobs-list.jsx b/components/jobs-list/jobs-list.jsx index f8848a9..82dda10 100644 --- a/components/jobs-list/jobs-list.jsx +++ b/components/jobs-list/jobs-list.jsx @@ -31,7 +31,7 @@ export function JobListComponent({ bodyshop }) { return refetch(); }; - if (loading) return ; + if (loading) return ; if (error) return {error.message}; if (data && data.jobs && data.jobs.length === 0) return ( diff --git a/graphql/bodyshop.queries.js b/graphql/bodyshop.queries.js index 114d767..4c2772e 100644 --- a/graphql/bodyshop.queries.js +++ b/graphql/bodyshop.queries.js @@ -12,6 +12,7 @@ export const QUERY_BODYSHOP = gql` features localmediatoken imexshopid + md_ro_statuses } } `; diff --git a/package-lock.json b/package-lock.json index feeaa1b..7a5a884 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@apollo/client": "^3.14.0", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "2.2.0", + "@react-native-picker/picker": "2.11.1", "@react-native-vector-icons/material-design-icons": "^12.3.0", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.5", @@ -4546,6 +4547,19 @@ "react-native": "^0.0.0-0 || >=0.65 <1.0" } }, + "node_modules/@react-native-picker/picker": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.1.tgz", + "integrity": "sha512-ThklnkK4fV3yynnIIRBkxxjxR4IFbdMNJVF6tlLdOJ/zEFUEFUEdXY0KmH0iYzMwY8W4/InWsLiA7AkpAbnexA==", + "license": "MIT", + "workspaces": [ + "example" + ], + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/@react-native-vector-icons/common": { "version": "12.3.0", "resolved": "https://registry.npmjs.org/@react-native-vector-icons/common/-/common-12.3.0.tgz", diff --git a/package.json b/package.json index 6bc7312..4f2fcb0 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@apollo/client": "^3.14.0", "@expo/vector-icons": "^15.0.2", "@react-native-async-storage/async-storage": "2.2.0", + "@react-native-picker/picker": "2.11.1", "@react-native-vector-icons/material-design-icons": "^12.3.0", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.5", @@ -91,4 +92,4 @@ "eslint-config-expo": "~10.0.0", "typescript": "~5.9.3" } -} \ No newline at end of file +}