diff --git a/babel-translations.babel b/babel-translations.babel
index 8375a85..61dc093 100644
--- a/babel-translations.babel
+++ b/babel-translations.babel
@@ -2685,6 +2685,27 @@
+
+ inproduction
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
ins_addr1
false
diff --git a/components/job-status-selector/JobStatusSelector.tsx b/components/job-status-selector/JobStatusSelector.tsx
new file mode 100644
index 0000000..f411880
--- /dev/null
+++ b/components/job-status-selector/JobStatusSelector.tsx
@@ -0,0 +1,166 @@
+import { UPDATE_JOB_STATUS } from "@/graphql/jobs.queries";
+import { useMutation } from "@apollo/client";
+import * as Haptics from "expo-haptics";
+import { useLocalSearchParams } from "expo-router";
+import React, { useCallback, useState } from "react";
+import { FlatList, StyleSheet, View } from "react-native";
+import {
+ Button,
+ Divider,
+ List,
+ Modal,
+ Portal,
+ Text,
+ useTheme,
+} from "react-native-paper";
+
+/**
+ * JobStatusSelector component contract
+ * Props:
+ * - statuses: string[] (list of available statuses)
+ * - currentStatus: string (currently applied status)
+ * - onSelect: (status: string) => void (fires when user selects a status)
+ * - label?: string (optional label for trigger button)
+ */
+export interface JobStatusSelectorProps {
+ statuses: string[];
+ currentStatus: string | undefined;
+ onSelect: (status: string) => void;
+ label?: string;
+ disabled?: boolean;
+}
+
+const keyExtractor = (item: string) => item;
+
+export const JobStatusSelector: React.FC = ({
+ statuses,
+ currentStatus,
+ onSelect,
+ label = "Change Status",
+ disabled = false,
+}) => {
+ const { jobId } = useLocalSearchParams();
+
+ const theme = useTheme();
+ const [visible, setVisible] = useState(false);
+ const show = () => setVisible(true);
+ const hide = () => setVisible(false);
+
+ const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
+
+ const handleSelect = useCallback(
+ async (status: string) => {
+ Haptics.selectionAsync().catch(() => {});
+ hide();
+ await updateJobStatus({
+ variables: {
+ jobId,
+ status,
+ },
+ });
+ if (onSelect && typeof onSelect === "function") onSelect(status);
+ },
+ [onSelect, jobId, updateJobStatus]
+ );
+
+ return (
+
+
+
+
+
+ {label}
+
+
+ {
+ const selected = item === currentStatus;
+ return (
+ handleSelect(item)}
+ style={selected ? styles.selectedItem : undefined}
+ titleStyle={selected ? { fontWeight: "600" } : undefined}
+ left={(props) =>
+ selected ? : null
+ }
+ />
+ );
+ }}
+ ItemSeparatorComponent={() => }
+ style={styles.list}
+ keyboardShouldPersistTaps="handled"
+ />
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ root: {
+ alignSelf: "flex-start",
+ },
+ trigger: {
+ minWidth: 140,
+ },
+ modalContainer: {
+ marginHorizontal: 24,
+ borderRadius: 16,
+ paddingVertical: 12,
+ paddingHorizontal: 8,
+ height: "60%",
+ display: "flex",
+ },
+ modalTitle: {
+ paddingHorizontal: 12,
+ paddingBottom: 8,
+ },
+ list: {
+ marginTop: 4,
+ flex: 1,
+ },
+ selectedItem: {
+ backgroundColor: "rgba(0,0,0,0.05)",
+ },
+ closeBtn: {
+ marginTop: 8,
+ alignSelf: "flex-end",
+ },
+});
+
+export default JobStatusSelector;
+
+/**
+ * Usage example:
+ * console.log('Status changed to', newStatus)}
+ * />
+ */
diff --git a/components/job-tombstone/job-tombstone.jsx b/components/job-tombstone/job-tombstone.jsx
index 00010cf..4856bb9 100644
--- a/components/job-tombstone/job-tombstone.jsx
+++ b/components/job-tombstone/job-tombstone.jsx
@@ -7,15 +7,15 @@ import { useTranslation } from "react-i18next";
import { RefreshControl, ScrollView, StyleSheet, View } from "react-native";
import {
ActivityIndicator,
- Button,
Card,
- Menu,
+ Chip,
Text,
useTheme,
} from "react-native-paper";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import DataLabelComponent from "../data-label/data-label";
+import { JobStatusSelector } from "../job-status-selector/JobStatusSelector";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -31,10 +31,7 @@ function JobTombstone({ bodyshop }) {
},
skip: !jobId,
});
- const [visible, setVisible] = useState(false);
- const openMenu = () => setVisible(true);
- const closeMenu = () => setVisible(false);
- console.log("JobTombstone render", visible);
+ console.log("JobTombstone render");
const theme = useTheme();
@@ -44,6 +41,8 @@ function JobTombstone({ bodyshop }) {
};
const [availableStatuses, setAvailableStatuses] = useState([]);
+ const job = data?.jobs_by_pk;
+
useEffect(() => {
if (!job || !bodyshop) return;
@@ -88,7 +87,7 @@ function JobTombstone({ bodyshop }) {
);
}
- const job = data.jobs_by_pk;
+
return (
- {job.status}
-
+
+ }
+ />
{job.inproduction && (
- {t("objects.jobs.labels.inproduction")}
+
+ {t("objects.jobs.labels.inproduction")}
+
+ }
+ />
)}
{job.inproduction &&
job.production_vars &&
!!job.production_vars.note && (
- {job.production_vars.note}
+ {job.production_vars.note}}
+ />
)}
diff --git a/graphql/jobs.queries.js b/graphql/jobs.queries.js
index bd4b017..82e59a9 100644
--- a/graphql/jobs.queries.js
+++ b/graphql/jobs.queries.js
@@ -430,6 +430,7 @@ export const UPDATE_JOB_STATUS = gql`
update_jobs(where: { id: { _eq: $jobId } }, _set: { status: $status }) {
returning {
id
+ status
}
}
}
@@ -886,9 +887,8 @@ export const generate_UPDATE_JOB_KANBAN = (
) => {
const oldChildQuery = `
updateOldChild: update_jobs(where: { id: { _eq: "${oldChildId}" } },
- _set: {kanbanparent: ${
- oldChildNewParent ? `"${oldChildNewParent}"` : null
- }}) {
+ _set: {kanbanparent: ${oldChildNewParent ? `"${oldChildNewParent}"` : null
+ }}) {
returning {
id
kanbanparent
@@ -897,9 +897,8 @@ export const generate_UPDATE_JOB_KANBAN = (
const movedQuery = `
updateMovedChild: update_jobs(where: { id: { _eq: "${movedId}" } },
- _set: {kanbanparent: ${
- movedNewParent ? `"${movedNewParent}"` : null
- } , status: "${movedNewStatus}"}) {
+ _set: {kanbanparent: ${movedNewParent ? `"${movedNewParent}"` : null
+ } , status: "${movedNewStatus}"}) {
returning {
id
status
diff --git a/translations/en-US/common.json b/translations/en-US/common.json
index b8a608d..4fba6dc 100644
--- a/translations/en-US/common.json
+++ b/translations/en-US/common.json
@@ -177,6 +177,7 @@
"est_ph1": "Appraiser Phone #",
"federal_tax_payable": "Federal Tax Payable",
"federal_tax_rate": "Federal Tax Rate",
+ "inproduction": "In Production",
"ins_addr1": "Insurance Co. Address",
"ins_city": "Insurance City",
"ins_co_id": "Insurance Co. ID",
diff --git a/translations/es-MX/common.json b/translations/es-MX/common.json
index f395feb..8bc4793 100644
--- a/translations/es-MX/common.json
+++ b/translations/es-MX/common.json
@@ -177,6 +177,7 @@
"est_ph1": "Número de teléfono del tasador",
"federal_tax_payable": "Impuesto federal por pagar",
"federal_tax_rate": "",
+ "inproduction": "",
"ins_addr1": "Dirección de Insurance Co.",
"ins_city": "Ciudad de seguros",
"ins_co_id": "ID de la compañía de seguros",
diff --git a/translations/fr-CA/common.json b/translations/fr-CA/common.json
index dccfdc9..d5b903f 100644
--- a/translations/fr-CA/common.json
+++ b/translations/fr-CA/common.json
@@ -177,6 +177,7 @@
"est_ph1": "Numéro de téléphone de l'évaluateur",
"federal_tax_payable": "Impôt fédéral à payer",
"federal_tax_rate": "",
+ "inproduction": "",
"ins_addr1": "Adresse Insurance Co.",
"ins_city": "Insurance City",
"ins_co_id": "ID de la compagnie d'assurance",