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;