Merged in test-beta (pull request #1186)

Test beta
This commit is contained in:
Dave Richer
2024-01-18 20:34:37 +00:00
10 changed files with 224 additions and 94 deletions

View File

@@ -1,15 +1,37 @@
import React from "react";
import { Card } from "antd"; import { Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectTechnician } from "../../redux/tech/tech.selectors";
export default function JobDetailCardTemplate({ const mapStateToProps = createStructuredSelector({
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobDetailCardTemplate);
export function JobDetailCardTemplate({
loading, loading,
title, title,
extraLink, extraLink,
technician,
...otherProps ...otherProps
}) { }) {
const { t } = useTranslation();
let extra; let extra;
if (extraLink) extra = { extra: <Link to={extraLink}>More</Link> }; if (extraLink && !technician)
extra = {
extra: <Link to={extraLink}>{t("jobs.labels.cards.more")}</Link>,
};
return ( return (
<Card <Card
size="small" size="small"

View File

@@ -15,6 +15,7 @@ import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateTimeFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter } from "../../utils/DateFormatter";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component"; import DataLabel from "../data-label/data-label.component";
import JobAltTransportChange from "../job-at-change/job-at-change.component"; import JobAltTransportChange from "../job-at-change/job-at-change.component";
@@ -160,19 +161,35 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<Card <Card
style={{ height: "100%" }} style={{ height: "100%" }}
title={ title={
<Link to={disabled ? "#" : `/manage/owners/${job.owner.id}`}> disabled ? (
<>
{ownerTitle.length > 0
? ownerTitle
: t("owner.labels.noownerinfo")}
</>
) : (
<Link to={`/manage/owners/${job.owner.id}`}>
{ownerTitle.length > 0 {ownerTitle.length > 0
? ownerTitle ? ownerTitle
: t("owner.labels.noownerinfo")} : t("owner.labels.noownerinfo")}
</Link> </Link>
)
} }
> >
<div> <div>
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}> <DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
{disabled ? (
<PhoneNumberFormatter>{job.ownr_ph1}</PhoneNumberFormatter>
) : (
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} /> <ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
)}
</DataLabel> </DataLabel>
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}> <DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
{disabled ? (
<PhoneNumberFormatter>{job.ownr_ph2}</PhoneNumberFormatter>
) : (
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} /> <ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
)}
</DataLabel> </DataLabel>
<DataLabel key="3" label={t("owners.fields.address")}> <DataLabel key="3" label={t("owners.fields.address")}>
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${ {`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
@@ -180,7 +197,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`} } ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
</DataLabel> </DataLabel>
<DataLabel key="4" label={t("owners.fields.ownr_ea")}> <DataLabel key="4" label={t("owners.fields.ownr_ea")}>
{job.ownr_ea || ""} {disabled ? (
<>{job.ownr_ea || ""}</>
) : job.ownr_ea ? (
<a href={`mailto:${job.ownr_ea}`}>{job.ownr_ea}</a>
) : null}
</DataLabel> </DataLabel>
{job.owner?.tax_number && ( {job.owner?.tax_number && (
<DataLabel key="5" label={t("owners.fields.tax_number")}> <DataLabel key="5" label={t("owners.fields.tax_number")}>
@@ -195,17 +216,19 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
style={{ height: "100%" }} style={{ height: "100%" }}
title={ title={
job.vehicle ? ( job.vehicle ? (
<Link disabled ? (
to={ <>
disabled {vehicleTitle.length > 0
? "#" ? vehicleTitle
: job.vehicle && `/manage/vehicles/${job.vehicle.id}` : t("vehicles.labels.novehinfo")}{" "}
} </>
> ) : (
<Link to={job.vehicle && `/manage/vehicles/${job.vehicle.id}`}>
{vehicleTitle.length > 0 {vehicleTitle.length > 0
? vehicleTitle ? vehicleTitle
: t("vehicles.labels.novehinfo")} : t("vehicles.labels.novehinfo")}
</Link> </Link>
)
) : ( ) : (
<span></span> <span></span>
) )
@@ -223,7 +246,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
</VehicleVinDisplay> </VehicleVinDisplay>
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? ( {bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
job.v_vin?.length !== 17 ? ( job.v_vin?.length !== 17 ? (
<WarningFilled style={{ color: "tomato", marginLeft: ".3rem" }} /> <WarningFilled
style={{ color: "tomato", marginLeft: ".3rem" }}
/>
) : null ) : null
) : null} ) : null}
</DataLabel> </DataLabel>
@@ -231,7 +256,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
{job.regie_number || t("general.labels.na")} {job.regie_number || t("general.labels.na")}
</DataLabel> </DataLabel>
<DataLabel label={t("jobs.labels.relatedros")}> <DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} job={job} /> <JobsRelatedRos jobid={job.id} job={job} disabled={disabled} />
</DataLabel> </DataLabel>
{job.vehicle && job.vehicle.notes && ( {job.vehicle && job.vehicle.notes && (
<DataLabel <DataLabel

View File

@@ -2,7 +2,7 @@ import { Space, Tag } from "antd";
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
export default function JobsRelatedRos({ jobid, job }) { export default function JobsRelatedRos({ jobid, job, disabled }) {
if (!(job && job.vehicle && job.vehicle.jobs)) return null; if (!(job && job.vehicle && job.vehicle.jobs)) return null;
return ( return (
<Space wrap> <Space wrap>
@@ -10,9 +10,15 @@ export default function JobsRelatedRos({ jobid, job }) {
.filter((j) => j.id !== job.id) .filter((j) => j.id !== job.id)
.map((j) => ( .map((j) => (
<Tag key={j.id}> <Tag key={j.id}>
{disabled ? (
<>{`${j.ro_number || "N/A"}${j.clm_no ? ` | ${j.clm_no}` : ""}${
j.status ? ` | ${j.status}` : ""
}`}</>
) : (
<Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${ <Link to={`/manage/jobs/${j?.id}`}>{`${j.ro_number || "N/A"}${
j.clm_no ? ` | ${j.clm_no}` : "" j.clm_no ? ` | ${j.clm_no}` : ""
}${j.status ? ` | ${j.status}` : ""}`}</Link> }${j.status ? ` | ${j.status}` : ""}`}</Link>
)}
</Tag> </Tag>
))} ))}
</Space> </Space>

View File

@@ -76,7 +76,14 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
dataIndex: "ownr", dataIndex: "ownr",
key: "ownr", key: "ownr",
ellipsis: true, ellipsis: true,
render: (text, record) => <OwnerNameDisplay ownerObject={record} />, render: (text, record) =>
technician ? (
<OwnerNameDisplay ownerObject={record} />
) : (
<Link to={`/manage/owners/${record.ownerid}`}>
<OwnerNameDisplay ownerObject={record} />
</Link>
),
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order, state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
@@ -93,7 +100,12 @@ const r = ({ technician, state, activeStatuses, data, bodyshop }) => {
), ),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order, state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
render: (text, record) => ( render: (text, record) =>
technician ? (
<>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
} ${record.v_color || ""} ${record.plate_no || ""}`}</>
) : (
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${ <Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
record.v_model_yr || "" record.v_model_yr || ""
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${ } ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${

View File

@@ -14,12 +14,14 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import StartChatButton from "../chat-open-button/chat-open-button.component"; import StartChatButton from "../chat-open-button/chat-open-button.component";
import JobAtChange from "../job-at-change/job-at-change.component"; import JobAtChange from "../job-at-change/job-at-change.component";
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component"; import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component"; import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component";
import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component"; import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component";
import CardTemplate from "../job-detail-cards/job-detail-cards.template.component";
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container"; import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component"; import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
@@ -104,7 +106,13 @@ export function ProductionListDetail({
{error && <AlertComponent error={JSON.stringify(error)} />} {error && <AlertComponent error={JSON.stringify(error)} />}
{!loading && data && ( {!loading && data && (
<div> <div>
<Space direction="vertical">
<CardTemplate
title={t("jobs.labels.employeeassignments")}
loading={loading}
>
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} /> <JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
</CardTemplate>
<Descriptions bordered column={1}> <Descriptions bordered column={1}>
<Descriptions.Item label={t("jobs.fields.ro_number")}> <Descriptions.Item label={t("jobs.fields.ro_number")}>
{theJob.ro_number || ""} {theJob.ro_number || ""}
@@ -112,7 +120,7 @@ export function ProductionListDetail({
<Descriptions.Item label={t("jobs.fields.alt_transport")}> <Descriptions.Item label={t("jobs.fields.alt_transport")}>
<Space> <Space>
{data.jobs_by_pk.alt_transport || ""} {data.jobs_by_pk.alt_transport || ""}
<JobAtChange event={{ job: data.jobs_by_pk }} /> <JobAtChange job={data.jobs_by_pk} />
</Space> </Space>
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.clm_no")}> <Descriptions.Item label={t("jobs.fields.clm_no")}>
@@ -122,7 +130,10 @@ export function ProductionListDetail({
{theJob.ins_co_nm || ""} {theJob.ins_co_nm || ""}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.owner")}> <Descriptions.Item label={t("jobs.fields.owner")}>
<Space>
<OwnerNameDisplay ownerObject={theJob} /> <OwnerNameDisplay ownerObject={theJob} />
{!technician ? (
<>
<StartChatButton <StartChatButton
phone={data.jobs_by_pk.ownr_ph1} phone={data.jobs_by_pk.ownr_ph1}
jobid={data.jobs_by_pk.id} jobid={data.jobs_by_pk.id}
@@ -131,6 +142,18 @@ export function ProductionListDetail({
phone={data.jobs_by_pk.ownr_ph2} phone={data.jobs_by_pk.ownr_ph2}
jobid={data.jobs_by_pk.id} jobid={data.jobs_by_pk.id}
/> />
</>
) : (
<>
<PhoneNumberFormatter>
{data.jobs_by_pk.ownr_ph1}
</PhoneNumberFormatter>
<PhoneNumberFormatter>
{data.jobs_by_pk.ownr_ph2}
</PhoneNumberFormatter>
</>
)}
</Space>
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.vehicle")}> <Descriptions.Item label={t("jobs.fields.vehicle")}>
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${ {`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
@@ -147,12 +170,10 @@ export function ProductionListDetail({
<DateFormatter>{theJob.scheduled_completion}</DateFormatter> <DateFormatter>{theJob.scheduled_completion}</DateFormatter>
</Descriptions.Item> </Descriptions.Item>
</Descriptions> </Descriptions>
<JobDetailCardsPartsComponent <JobDetailCardsPartsComponent
loading={loading} loading={loading}
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
/> />
<JobDetailCardsNotesComponent <JobDetailCardsNotesComponent
loading={loading} loading={loading}
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
@@ -163,6 +184,7 @@ export function ProductionListDetail({
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
/> />
)} )}
</Space>
</div> </div>
)} )}
</Drawer> </Drawer>

View File

@@ -5,7 +5,7 @@ export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
$offset: Int $offset: Int
$limit: Int $limit: Int
$order: [jobs_order_by!] $order: [jobs_order_by!]
$statuses: [String!]!, $statuses: [String!]!
$isConverted: Boolean $isConverted: Boolean
) { ) {
jobs( jobs(
@@ -120,7 +120,9 @@ export const QUERY_PARTS_QUEUE = gql`
} }
} }
jobs( jobs(
where: { _and: [{ status: { _in: $statuses }, converted: { _eq: true } }] } where: {
_and: [{ status: { _in: $statuses }, converted: { _eq: true } }]
}
offset: $offset offset: $offset
limit: $limit limit: $limit
order_by: $order order_by: $order
@@ -336,6 +338,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
category category
iouparent iouparent
ro_number ro_number
ownerid
ownr_fn ownr_fn
ownr_ln ownr_ln
ownr_co_nm ownr_co_nm

View File

@@ -1704,6 +1704,7 @@
"estimator": "Estimator", "estimator": "Estimator",
"filehandler": "File Handler", "filehandler": "File Handler",
"insurance": "Insurance Details", "insurance": "Insurance Details",
"more": "More",
"notes": "Notes", "notes": "Notes",
"parts": "Parts", "parts": "Parts",
"totals": "Totals", "totals": "Totals",

View File

@@ -1704,6 +1704,7 @@
"estimator": "Estimador", "estimator": "Estimador",
"filehandler": "File Handler", "filehandler": "File Handler",
"insurance": "detalles del seguro", "insurance": "detalles del seguro",
"more": "Más",
"notes": "Notas", "notes": "Notas",
"parts": "Partes", "parts": "Partes",
"totals": "Totales", "totals": "Totales",

View File

@@ -1704,6 +1704,7 @@
"estimator": "Estimateur", "estimator": "Estimateur",
"filehandler": "Gestionnaire de fichiers", "filehandler": "Gestionnaire de fichiers",
"insurance": "Détails de l'assurance", "insurance": "Détails de l'assurance",
"more": "Plus",
"notes": "Remarques", "notes": "Remarques",
"parts": "les pièces", "parts": "les pièces",
"totals": "Totaux", "totals": "Totaux",

View File

@@ -0,0 +1,37 @@
export const BETA_KEY = 'betaSwitchImex';
export const checkBeta = () => {
const cookie = document.cookie.split('; ').find(row => row.startsWith(BETA_KEY));
return cookie ? cookie.split('=')[1] === 'true' : false;
}
export const setBeta = (value) => {
const domain = window.location.hostname.split('.').slice(-2).join('.');
document.cookie = `${BETA_KEY}=${value}; path=/; domain=.${domain}`;
}
export const handleBeta = () => {
// If the current host name does not start with beta or test, then we don't need to do anything.
if (window.location.hostname.startsWith('localhost')) {
console.log('Not on beta or test, so no need to handle beta.');
return;
}
const isBeta = checkBeta();
const currentHostName = window.location.hostname;
// Beta is enabled, but the current host name does start with beta.
if (isBeta && !currentHostName.startsWith('beta')) {
window.location.href = `${window.location.protocol}//beta.${currentHostName}${window.location.pathname}${window.location.search}${window.location.hash}`;
window.reload();
}
// Beta is not enabled, but the current host name does start with beta.
else if (!isBeta && currentHostName.startsWith('beta')) {
window.location.href = `${window.location.protocol}//${currentHostName.replace('beta.', '')}${window.location.pathname}${window.location.search}${window.location.hash}`;
window.reload();
}
}
export default handleBeta;