361 lines
12 KiB
JavaScript
361 lines
12 KiB
JavaScript
import env from "@/env";
|
|
import { GET_JOB_TOMBSTONE } from "@/graphql/jobs.queries";
|
|
import { selectBodyshop } from "@/redux/user/user.selectors";
|
|
import { useQuery } from "@apollo/client";
|
|
import { useLocalSearchParams, useRouter } from "expo-router";
|
|
import React, { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import {
|
|
Platform,
|
|
RefreshControl,
|
|
ScrollView,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
View,
|
|
} from "react-native";
|
|
import {
|
|
ActivityIndicator,
|
|
Button,
|
|
Card,
|
|
Chip,
|
|
Text,
|
|
} from "react-native-paper";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { firstNames, getRandomIndex, lastNames } from "../../util/demodata";
|
|
import DataLabelComponent from "../data-label/data-label";
|
|
import ErrorDisplay from "../error/error-display";
|
|
import { JobStatusSelector } from "../job-status-selector/job-status-selector";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop,
|
|
});
|
|
const mapDispatchToProps = (dispatch) => ({});
|
|
export default connect(mapStateToProps, mapDispatchToProps)(JobTombstone);
|
|
|
|
function JobTombstone({ bodyshop }) {
|
|
const { jobId } = useLocalSearchParams();
|
|
const router = useRouter();
|
|
const { loading, error, data, refetch } = useQuery(GET_JOB_TOMBSTONE, {
|
|
variables: {
|
|
id: jobId,
|
|
},
|
|
skip: !jobId,
|
|
});
|
|
|
|
const { t } = useTranslation();
|
|
const onRefresh = async () => {
|
|
return refetch();
|
|
};
|
|
|
|
const [availableStatuses, setAvailableStatuses] = useState([]);
|
|
const job = data?.jobs_by_pk;
|
|
|
|
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 <ActivityIndicator size="large" style={{ flex: 1 }} />;
|
|
}
|
|
|
|
if (error) {
|
|
return <ErrorDisplay message={JSON.stringify(error)} />;
|
|
}
|
|
|
|
if (!data.jobs_by_pk) {
|
|
return (
|
|
<Card>
|
|
<Text>Job is not defined.</Text>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ScrollView
|
|
contentContainerStyle={{
|
|
rowGap: 16,
|
|
padding: 10,
|
|
}}
|
|
refreshControl={
|
|
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
|
}
|
|
>
|
|
<Card mode="outlined">
|
|
<Card.Title
|
|
title={t("jobdetail.labels.jobinfo")}
|
|
titleVariant="titleLarge"
|
|
/>
|
|
<Card.Content style={localStyles.twoColumnCard}>
|
|
<View style={localStyles.twoColumnCardColumn}>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.status")}
|
|
content={
|
|
<JobStatusSelector
|
|
statuses={availableStatuses}
|
|
currentStatus={job.status}
|
|
label={t("jobdetail.labels.status")}
|
|
/>
|
|
}
|
|
/>
|
|
{job.inproduction && (
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.inproduction")}
|
|
noTextWrap
|
|
content={
|
|
<Chip
|
|
style={{ marginTop: 8, maxWidth: "75%" }}
|
|
mode="outlined"
|
|
>
|
|
{t("objects.jobs.labels.inproduction")}
|
|
</Chip>
|
|
}
|
|
/>
|
|
)}
|
|
{job.inproduction &&
|
|
job.production_vars &&
|
|
!!job.production_vars.note && (
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.production_note")}
|
|
content={<Text>{job.production_vars.note}</Text>}
|
|
/>
|
|
)}
|
|
</View>
|
|
<View style={localStyles.twoColumnCardColumn}>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.related_ros")}
|
|
content={
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
flexWrap: "wrap",
|
|
gap: 8,
|
|
paddingTop: 8,
|
|
}}
|
|
>
|
|
{job.vehicle?.jobs
|
|
?.filter((ro) => ro.id !== job.id)
|
|
.map((ro) => (
|
|
<TouchableOpacity
|
|
onPress={() => {
|
|
router.navigate({
|
|
pathname: `/jobs/${ro.id}`,
|
|
params: {
|
|
title: ro.ro_number || t("general.labels.na"),
|
|
},
|
|
});
|
|
}}
|
|
key={ro.id}
|
|
>
|
|
<Button comp mode="outlined">
|
|
{ro.ro_number || "N/A"}
|
|
</Button>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
}
|
|
/>
|
|
</View>
|
|
</Card.Content>
|
|
</Card>
|
|
|
|
<Card mode="outlined">
|
|
<Card.Title
|
|
title={t("jobdetail.labels.claiminformation")}
|
|
titleVariant="titleLarge"
|
|
/>
|
|
<Card.Content style={localStyles.twoColumnCard}>
|
|
<View style={localStyles.twoColumnCardColumn}>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.owner")}
|
|
content={
|
|
env.DEMO_MODE
|
|
? `${firstNames[getRandomIndex()] || ""} ${
|
|
lastNames[getRandomIndex()] || ""
|
|
} ${job.ownr_co_nm || ""}`
|
|
: `${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
|
job.ownr_co_nm || ""
|
|
}`
|
|
}
|
|
/>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.vehicle")}
|
|
content={
|
|
<View>
|
|
<Text>
|
|
{env.DEMO_MODE
|
|
? `${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
|
|
job.v_model_desc || ""
|
|
} - 1GNDX33L46D168902`
|
|
: `${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
|
|
job.v_model_desc || ""
|
|
} - ${job.v_vin}`}
|
|
</Text>
|
|
<Text>
|
|
{(() => {
|
|
const paintCodes = Object.keys(
|
|
job.vehicle?.v_paint_codes || {}
|
|
)
|
|
.filter(
|
|
(key) =>
|
|
job.vehicle.v_paint_codes[key] !== "" &&
|
|
job.vehicle.v_paint_codes[key] !== null &&
|
|
job.vehicle.v_paint_codes[key] !== undefined
|
|
)
|
|
.map((key) => job.vehicle.v_paint_codes[key]);
|
|
|
|
if (paintCodes.length > 0) {
|
|
return `${job.v_color || ""} (${paintCodes.join(
|
|
", "
|
|
)})`.trim();
|
|
}
|
|
return job.v_color || "";
|
|
})()}
|
|
</Text>
|
|
</View>
|
|
}
|
|
/>
|
|
</View>
|
|
<View style={localStyles.twoColumnCardColumn}>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.ins_co_nm")}
|
|
content={env.DEMO_MODE ? "ABC Ins." : job.ins_co_nm}
|
|
/>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.clm_no")}
|
|
content={job.clm_no}
|
|
/>
|
|
</View>
|
|
</Card.Content>
|
|
</Card>
|
|
<Card mode="outlined">
|
|
<Card.Title
|
|
title={t("jobdetail.labels.employeeassignments")}
|
|
titleVariant="titleLarge"
|
|
/>
|
|
<Card.Content>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.employee_body")}
|
|
content={`${
|
|
(job.employee_body_rel && job.employee_body_rel.first_name) ||
|
|
t("general.labels.na")
|
|
} ${
|
|
(job.employee_body_rel && job.employee_body_rel.last_name) || ""
|
|
}`}
|
|
/>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.employee_prep")}
|
|
content={`${
|
|
(job.employee_prep_rel && job.employee_prep_rel.first_name) ||
|
|
t("general.labels.na")
|
|
} ${
|
|
(job.employee_prep_rel && job.employee_prep_rel.last_name) || ""
|
|
}`}
|
|
/>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.employee_refinish")}
|
|
content={`${
|
|
(job.employee_refinish_rel &&
|
|
job.employee_refinish_rel.first_name) ||
|
|
t("general.labels.na")
|
|
} ${
|
|
(job.employee_refinish_rel &&
|
|
job.employee_refinish_rel.last_name) ||
|
|
""
|
|
}`}
|
|
/>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.employee_csr")}
|
|
content={`${
|
|
(job.employee_csr_rel && job.employee_csr_rel.first_name) ||
|
|
t("general.labels.na")
|
|
} ${
|
|
(job.employee_csr_rel && job.employee_csr_rel.last_name) || ""
|
|
}`}
|
|
/>
|
|
</Card.Content>
|
|
</Card>
|
|
<Card mode="outlined">
|
|
<Card.Title
|
|
title={t("jobdetail.labels.dates")}
|
|
titleVariant="titleLarge"
|
|
/>
|
|
<Card.Content style={localStyles.twoColumnCard}>
|
|
<View style={localStyles.twoColumnCardColumn}>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.scheduled_in")}
|
|
content={job.scheduled_in}
|
|
dateTime
|
|
/>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.actual_in")}
|
|
content={job.actual_in}
|
|
dateTime
|
|
/>
|
|
</View>
|
|
<View style={localStyles.twoColumnCardColumn}>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.scheduled_completion")}
|
|
content={job.scheduled_completion}
|
|
dateTime
|
|
/>
|
|
<DataLabelComponent
|
|
label={t("objects.jobs.fields.scheduled_delivery")}
|
|
content={job.scheduled_delivery}
|
|
dateTime
|
|
/>
|
|
</View>
|
|
</Card.Content>
|
|
</Card>
|
|
<View
|
|
style={{ height: Platform.OS === "ios" ? 64 : 96 }} //Spacer
|
|
/>
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
const localStyles = StyleSheet.create({
|
|
twoColumnCard: { display: "flex", flexDirection: "row" },
|
|
twoColumnCardColumn: { flex: 1 },
|
|
status: {
|
|
textAlign: "center",
|
|
flexDirection: "row",
|
|
justifyContent: "center",
|
|
},
|
|
inproduction: {
|
|
textAlign: "center",
|
|
flexDirection: "row",
|
|
justifyContent: "center",
|
|
},
|
|
});
|