diff --git a/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx b/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx
index 767c62553..4f8efc2c9 100644
--- a/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx
+++ b/client/src/components/jobs-admin-dates/jobs-admin-dates.component.jsx
@@ -106,7 +106,12 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
-
+
+
+
+
+
+
diff --git a/client/src/components/jobs-detail-dates/jobs-detail-dates.component.jsx b/client/src/components/jobs-detail-dates/jobs-detail-dates.component.jsx
index f32f1b94d..410707cba 100644
--- a/client/src/components/jobs-detail-dates/jobs-detail-dates.component.jsx
+++ b/client/src/components/jobs-detail-dates/jobs-detail-dates.component.jsx
@@ -7,6 +7,7 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
import { selectBodyshop } from "../../redux/user/user.selectors";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import FormRow from "../layout-form-row/layout-form-row.component";
+import dayjs from "../../utils/day";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -40,6 +41,20 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
+
+
+
+
+
+
@@ -76,21 +91,15 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
- {() => {
- return (
-
-
-
- );
- }}
+ {() => (
+
+
+
+ )}
@@ -103,15 +112,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
-
-
-
diff --git a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx
index f237b66ec..fcb6795e8 100644
--- a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx
+++ b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx
@@ -1,13 +1,15 @@
import { BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined, WarningFilled } from "@ant-design/icons";
-import { Card, Col, Divider, Row, Space, Tag, Tooltip } from "antd";
-import React, { useState } from "react";
+import { Card, Checkbox, Col, Divider, Row, Space, Tag, Tooltip } from "antd";
+import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
+import { useMutation } from "@apollo/client";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
+import { UPDATE_JOB } from "../../graphql/jobs.queries";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import dayjs from "../../utils/day";
@@ -22,6 +24,7 @@ import ProductionListColumnComment from "../production-list-columns/production-l
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
import "./jobs-detail-header.styles.scss";
+import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -29,41 +32,55 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
- setPrintCenterContext: (context) => dispatch(setModalContext({ context: context, modal: "printCenter" }))
+ setPrintCenterContext: (context) =>
+ dispatch(
+ setModalContext({
+ context: context,
+ modal: "printCenter"
+ })
+ )
});
const colSpan = {
- xs: {
- span: 24
- },
- sm: {
- span: 24
- },
- md: {
- span: 12
- },
- lg: {
- span: 6
- },
- xl: {
- span: 6
- }
+ xs: { span: 24 },
+ sm: { span: 24 },
+ md: { span: 12 },
+ lg: { span: 6 },
+ xl: { span: 6 }
};
export function JobsDetailHeader({ job, bodyshop, disabled }) {
const { t } = useTranslation();
+ const { notification } = useNotification();
const [notesClamped, setNotesClamped] = useState(true);
- const vehicleTitle = `${job.v_model_yr || ""} ${job.v_color || ""}
- ${job.v_make_desc || ""}
- ${job.v_model_desc || ""}`.trim();
-
+ const [updateJob] = useMutation(UPDATE_JOB);
+ const vehicleTitle =
+ `${job.v_model_yr || ""} ${job.v_color || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim();
const bodyHrs = job.joblines.filter((j) => j.mod_lbr_ty !== "LAR").reduce((acc, val) => acc + val.mod_lb_hrs, 0);
const refinishHrs = job.joblines
.filter((line) => line.mod_lbr_ty === "LAR")
.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
-
const ownerTitle = OwnerNameDisplayFunction(job).trim();
+ // Handle checkbox changes
+ const handleCheckboxChange = async (field, checked) => {
+ const value = checked ? dayjs().toISOString() : null;
+ try {
+ await updateJob({
+ variables: {
+ jobId: job.id,
+ job: { [field]: value }
+ },
+ refetchQueries: ["GET_JOB_BY_PK"],
+ awaitRefetchQueries: true
+ });
+ } catch (error) {
+ notification.error({
+ message: t("jobs.errors.saving", { error: error.message })
+ });
+ }
+ };
+
return (
@@ -72,11 +89,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
{job.status}
- {job.inproduction && (
-
- {t("jobs.labels.inproduction")}
-
- )}
+ {job.inproduction && {t("jobs.labels.inproduction")}}
{job.suspended && }
{job.iouparent && (
@@ -110,7 +123,6 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
/
{job.owner_owing}
-
{job.alt_transport}
@@ -127,11 +139,27 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
))}
)}
-
-
+
+ handleCheckboxChange("estimate_sent_approval", e.target.checked)}
+ disabled={disabled}
+ >
+ {t("jobs.labels.sent")}
+
+
+
+ handleCheckboxChange("estimate_approved", e.target.checked)}
+ disabled={disabled}
+ >
+ {t("jobs.labels.approved")}
+
+
{job.special_coverage_policy && (
diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js
index 6a53db9c2..5150b7a9f 100644
--- a/client/src/graphql/jobs.queries.js
+++ b/client/src/graphql/jobs.queries.js
@@ -685,6 +685,8 @@ export const GET_JOB_BY_PK = gql`
scheduled_delivery
scheduled_in
selling_dealer
+ estimate_approved
+ estimate_sent_approval
selling_dealer_contact
servicing_dealer
servicing_dealer_contact
@@ -929,6 +931,8 @@ export const QUERY_JOB_CARD_DETAILS = gql`
date_exported
date_repairstarted
date_scheduled
+ estimate_sent_approval
+ estimate_approved
date_estimated
employee_body_rel {
id
@@ -1077,6 +1081,8 @@ export const UPDATE_JOB = gql`
date_repairstarted
date_void
date_lost_sale
+ estimate_sent_approval
+ estimate_approved
}
}
}
@@ -2431,6 +2437,8 @@ export const QUERY_PARTS_QUEUE_CARD_DETAILS = gql`
plate_st
po_number
production_vars
+ estimate_sent_approval
+ estimate_approved
ro_number
scheduled_completion
scheduled_delivery
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 3669abcc7..8c69e17d5 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -1650,6 +1650,8 @@
"adjustment_bottom_line": "Adjustments",
"adjustmenthours": "Adjustment Hours",
"alt_transport": "Alt. Trans.",
+ "estimate_sent_approval": "Estimate Sent for Approval",
+ "estimate_approved": "Estimate Approved",
"area_of_damage_impact": {
"10": "Left Front Side",
"11": "Left Front Corner",
@@ -1955,6 +1957,8 @@
"scheddates": "Schedule Dates"
},
"labels": {
+ "sent": "",
+ "approved": "",
"accountsreceivable": "Accounts Receivable",
"act_price_ppc": "New Part Price",
"actual_completion_inferred": "$t(jobs.fields.actual_completion) inferred using $t(jobs.fields.scheduled_completion).",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 4889ec3ec..72b5d6199 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -1642,6 +1642,8 @@
"voiding": ""
},
"fields": {
+ "estimate_sent_approval": "",
+ "estimate_approved": "",
"active_tasks": "",
"actual_completion": "Realización real",
"actual_delivery": "Entrega real",
@@ -1955,6 +1957,8 @@
"scheddates": ""
},
"labels": {
+ "sent": "",
+ "approved": "",
"accountsreceivable": "",
"act_price_ppc": "",
"actual_completion_inferred": "",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 11382844d..dac8dd2ef 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -1642,6 +1642,8 @@
"voiding": ""
},
"fields": {
+ "estimate_sent_approval": "",
+ "estimate_approved": "",
"active_tasks": "",
"actual_completion": "Achèvement réel",
"actual_delivery": "Livraison réelle",
@@ -1955,6 +1957,8 @@
"scheddates": ""
},
"labels": {
+ "sent": "",
+ "approved": "",
"accountsreceivable": "",
"act_price_ppc": "",
"actual_completion_inferred": "",
diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml
index e6f9041d2..cd68acf61 100644
--- a/hasura/metadata/tables.yaml
+++ b/hasura/metadata/tables.yaml
@@ -3702,6 +3702,8 @@
- est_ph1
- est_st
- est_zip
+ - estimate_approved
+ - estimate_sent_approval
- federal_tax_rate
- flat_rate_ats
- g_bett_amt
@@ -3976,6 +3978,8 @@
- est_ph1
- est_st
- est_zip
+ - estimate_approved
+ - estimate_sent_approval
- federal_tax_rate
- flat_rate_ats
- g_bett_amt
@@ -4262,6 +4266,8 @@
- est_ph1
- est_st
- est_zip
+ - estimate_approved
+ - estimate_sent_approval
- federal_tax_rate
- flat_rate_ats
- g_bett_amt
diff --git a/hasura/migrations/1746563777976_alter_table_public_jobs_add_column_estimate_sent_approval/down.sql b/hasura/migrations/1746563777976_alter_table_public_jobs_add_column_estimate_sent_approval/down.sql
new file mode 100644
index 000000000..eace669af
--- /dev/null
+++ b/hasura/migrations/1746563777976_alter_table_public_jobs_add_column_estimate_sent_approval/down.sql
@@ -0,0 +1,4 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- alter table "public"."jobs" add column "estimate_sent_approval" timestamptz
+-- null;
diff --git a/hasura/migrations/1746563777976_alter_table_public_jobs_add_column_estimate_sent_approval/up.sql b/hasura/migrations/1746563777976_alter_table_public_jobs_add_column_estimate_sent_approval/up.sql
new file mode 100644
index 000000000..ef384cc68
--- /dev/null
+++ b/hasura/migrations/1746563777976_alter_table_public_jobs_add_column_estimate_sent_approval/up.sql
@@ -0,0 +1,2 @@
+alter table "public"."jobs" add column "estimate_sent_approval" timestamptz
+ null;
diff --git a/hasura/migrations/1746563808335_alter_table_public_jobs_add_column_estimate_approved/down.sql b/hasura/migrations/1746563808335_alter_table_public_jobs_add_column_estimate_approved/down.sql
new file mode 100644
index 000000000..f0eedd1ee
--- /dev/null
+++ b/hasura/migrations/1746563808335_alter_table_public_jobs_add_column_estimate_approved/down.sql
@@ -0,0 +1,4 @@
+-- Could not auto-generate a down migration.
+-- Please write an appropriate down migration for the SQL below:
+-- alter table "public"."jobs" add column "estimate_approved" timestamptz
+-- null;
diff --git a/hasura/migrations/1746563808335_alter_table_public_jobs_add_column_estimate_approved/up.sql b/hasura/migrations/1746563808335_alter_table_public_jobs_add_column_estimate_approved/up.sql
new file mode 100644
index 000000000..f60f93148
--- /dev/null
+++ b/hasura/migrations/1746563808335_alter_table_public_jobs_add_column_estimate_approved/up.sql
@@ -0,0 +1,2 @@
+alter table "public"."jobs" add column "estimate_approved" timestamptz
+ null;