- the great reformat

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-02-06 18:20:58 -05:00
parent 30c530bcc4
commit e83badb454
912 changed files with 108516 additions and 107493 deletions

View File

@@ -1,15 +1,15 @@
import { PrinterFilled } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Col, Divider, Drawer, Grid, Row, Space } from "antd";
import {PrinterFilled} from "@ant-design/icons";
import {useQuery} from "@apollo/client";
import {Button, Card, Col, Divider, Drawer, Grid, Row, Space} from "antd";
import queryString from "query-string";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useNavigate, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {Link, useLocation, useNavigate} from "react-router-dom";
import {createStructuredSelector} from "reselect";
import {QUERY_JOB_CARD_DETAILS} from "../../graphql/jobs.queries";
import {setModalContext} from "../../redux/modals/modals.actions";
import {selectBodyshop} from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import JobSyncButton from "../job-sync-button/job-sync-button.component";
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
@@ -23,150 +23,151 @@ import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component";
import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "printCenter" })),
setPrintCenterContext: (context) =>
dispatch(setModalContext({context: context, modal: "printCenter"})),
});
const span = {
sm: { span: 24 },
md: { span: 12 },
lg: { span: 8 },
sm: {span: 24},
md: {span: 12},
lg: {span: 8},
};
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
export function JobDetailCards({bodyshop, setPrintCenterContext}) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
md: "100%",
lg: "75%",
xl: "75%",
xxl: "60%",
};
const drawerPercentage = selectedBreakpoint
? bpoints[selectedBreakpoint[0]]
: "100%";
const bpoints = {
xs: "100%",
sm: "100%",
md: "100%",
lg: "75%",
xl: "75%",
xxl: "60%",
};
const drawerPercentage = selectedBreakpoint
? bpoints[selectedBreakpoint[0]]
: "100%";
const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams;
const history = useNavigate();
const { loading, error, data, refetch } = useQuery(QUERY_JOB_CARD_DETAILS, {
variables: { id: selected },
skip: !selected,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const { t } = useTranslation();
const handleDrawerClose = () => {
delete searchParams.selected;
history({
search: queryString.stringify({
...searchParams,
}),
const searchParams = queryString.parse(useLocation().search);
const {selected} = searchParams;
const history = useNavigate();
const {loading, error, data, refetch} = useQuery(QUERY_JOB_CARD_DETAILS, {
variables: {id: selected},
skip: !selected,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
};
return (
<Drawer
open={!!selected}
destroyOnClose
width={drawerPercentage}
placement="right"
onClose={handleDrawerClose}
>
{loading ? <LoadingSpinner /> : null}
{error ? <AlertComponent message={error.message} type="error" /> : null}
{data ? (
<Card
title={
<Link to={`/manage/jobs/${data.jobs_by_pk.id}`}>
{data.jobs_by_pk.ro_number || t("general.labels.na")}
</Link>
}
extra={
<Space wrap>
<JobSyncButton job={data.jobs_by_pk} />
const {t} = useTranslation();
const handleDrawerClose = () => {
delete searchParams.selected;
history({
search: queryString.stringify({
...searchParams,
}),
});
};
<Button
onClick={() => {
setPrintCenterContext({
actions: { refetch: refetch },
context: {
id: data.jobs_by_pk.id,
job: data.jobs_by_pk,
type: "job",
},
});
}}
>
<PrinterFilled />
{t("jobs.actions.printCenter")}
</Button>
<Link to={`/manage/jobs/${data.jobs_by_pk.id}?tab=repairdata`}>
<Button>{t("parts.actions.order")}</Button>
</Link>
</Space>
}
return (
<Drawer
open={!!selected}
destroyOnClose
width={drawerPercentage}
placement="right"
onClose={handleDrawerClose}
>
<JobsDetailHeader job={data ? data.jobs_by_pk : null} />
<Divider type="horizontal" />
<Row gutter={[16, 16]}>
<Col {...span}>
<JobDetailCardsInsuranceComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsTotalsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsDatesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsNotesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
{!bodyshop.uselocalmediaserver && (
<Col {...span}>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
)}
<Col {...span}>
<JobDetailCardsDamageComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
</Row>
</Card>
) : null}
</Drawer>
);
{loading ? <LoadingSpinner/> : null}
{error ? <AlertComponent message={error.message} type="error"/> : null}
{data ? (
<Card
title={
<Link to={`/manage/jobs/${data.jobs_by_pk.id}`}>
{data.jobs_by_pk.ro_number || t("general.labels.na")}
</Link>
}
extra={
<Space wrap>
<JobSyncButton job={data.jobs_by_pk}/>
<Button
onClick={() => {
setPrintCenterContext({
actions: {refetch: refetch},
context: {
id: data.jobs_by_pk.id,
job: data.jobs_by_pk,
type: "job",
},
});
}}
>
<PrinterFilled/>
{t("jobs.actions.printCenter")}
</Button>
<Link to={`/manage/jobs/${data.jobs_by_pk.id}?tab=repairdata`}>
<Button>{t("parts.actions.order")}</Button>
</Link>
</Space>
}
>
<JobsDetailHeader job={data ? data.jobs_by_pk : null}/>
<Divider type="horizontal"/>
<Row gutter={[16, 16]}>
<Col {...span}>
<JobDetailCardsInsuranceComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsTotalsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsDatesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsNotesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
{!bodyshop.uselocalmediaserver && (
<Col {...span}>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
)}
<Col {...span}>
<JobDetailCardsDamageComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
</Row>
</Card>
) : null}
</Drawer>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(JobDetailCards);

View File

@@ -1,18 +1,18 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {useTranslation} from "react-i18next";
import CardTemplate from "./job-detail-cards.template.component";
import Car from "../job-damage-visual/job-damage-visual.component";
export default function JobDetailCardsDamageComponent({ loading, data }) {
const { t } = useTranslation();
const { area_of_damage } = data;
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.damage")}>
{area_of_damage ? (
<Car dmg1={area_of_damage.impact1} dmg2={area_of_damage.impact2} />
) : (
t("jobs.errors.nodamage")
)}
</CardTemplate>
);
export default function JobDetailCardsDamageComponent({loading, data}) {
const {t} = useTranslation();
const {area_of_damage} = data;
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.damage")}>
{area_of_damage ? (
<Car dmg1={area_of_damage.impact1} dmg2={area_of_damage.impact2}/>
) : (
t("jobs.errors.nodamage")
)}
</CardTemplate>
);
}

View File

@@ -1,33 +1,34 @@
import { Carousel } from "antd";
import {Carousel} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { GenerateThumbUrl } from "../jobs-documents-gallery/job-documents.utility";
import {useTranslation} from "react-i18next";
import {GenerateThumbUrl} from "../jobs-documents-gallery/job-documents.utility";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsDocumentsComponent({ loading, data }) {
const { t } = useTranslation();
if (!data)
export default function JobDetailCardsDocumentsComponent({loading, data}) {
const {t} = useTranslation();
if (!data)
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.documents")}>
null
</CardTemplate>
);
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.documents")}>
null
</CardTemplate>
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.documents")}
extraLink={`/manage/jobs/${data.id}?tab=documents`}
>
{data.documents.length > 0 ? (
<Carousel autoplay>
{data.documents.map((item) => (
<img key={item.id} src={GenerateThumbUrl(item)} alt={item.name}/>
))}
</Carousel>
) : (
<div>{t("documents.errors.nodocuments")}</div>
)}
</CardTemplate>
);
return (
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.documents")}
extraLink={`/manage/jobs/${data.id}?tab=documents`}
>
{data.documents.length > 0 ? (
<Carousel autoplay>
{data.documents.map((item) => (
<img key={item.id} src={GenerateThumbUrl(item)} alt={item.name} />
))}
</Carousel>
) : (
<div>{t("documents.errors.nodocuments")}</div>
)}
</CardTemplate>
);
}

View File

@@ -1,14 +1,15 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {useTranslation} from "react-i18next";
import DataLabel from "../data-label/data-label.component";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsInsuranceComponent({ loading, data }) {
const { t } = useTranslation();
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.insurance")}>
{data ? (
<span>
export default function JobDetailCardsInsuranceComponent({loading, data}) {
const {t} = useTranslation();
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.insurance")}>
{data ? (
<span>
<DataLabel label={t("jobs.fields.ins_co_nm")} hideIfNull>
{data.ins_co_nm}
</DataLabel>
@@ -16,29 +17,29 @@ export default function JobDetailCardsInsuranceComponent({ loading, data }) {
{data.clm_no}
</DataLabel>
<DataLabel
label={t("jobs.labels.cards.filehandler")}
open={data.ins_ct_fn && data.ins_ct_ln}>
label={t("jobs.labels.cards.filehandler")}
open={data.ins_ct_fn && data.ins_ct_ln}>
{data.ins_ea ? (
<a href={`mailto:${data.ins_ea}`}>
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
</a>
<a href={`mailto:${data.ins_ea}`}>
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
</a>
) : (
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
<div>{`${data.ins_ct_fn || ""} ${data.ins_ct_ln || ""}`}</div>
)}
</DataLabel>
<DataLabel
label={t("jobs.labels.cards.estimator")}
open={data.est_ct_fn && data.est_ct_ln}>
label={t("jobs.labels.cards.estimator")}
open={data.est_ct_fn && data.est_ct_ln}>
{data.ins_ea ? (
<a href={`mailto:${data.est_ea}`}>
<div>{`${data.est_ct_fn || ""} ${data.est_ct_ln || ""}`}</div>
</a>
<a href={`mailto:${data.est_ea}`}>
<div>{`${data.est_ct_fn || ""} ${data.est_ct_ln || ""}`}</div>
</a>
) : (
<div>{`${data.est_ct_fn || ""} ${data.est_ct_ln || ""}`}</div>
<div>{`${data.est_ct_fn || ""} ${data.est_ct_ln || ""}`}</div>
)}
</DataLabel>
</span>
) : null}
</CardTemplate>
);
) : null}
</CardTemplate>
);
}

View File

@@ -1,11 +1,7 @@
import { List } from "antd";
import {
WarningFilled,
EyeInvisibleFilled,
AuditOutlined,
} from "@ant-design/icons";
import {List} from "antd";
import {AuditOutlined, EyeInvisibleFilled, WarningFilled,} from "@ant-design/icons";
import React from "react";
import { useTranslation } from "react-i18next";
import {useTranslation} from "react-i18next";
import CardTemplate from "./job-detail-cards.template.component";
import styled from "styled-components";
@@ -14,33 +10,33 @@ const Container = styled.div`
overflow-y: auto;
`;
export default function JobDetailCardsNotesComponent({ loading, data }) {
const { t } = useTranslation();
export default function JobDetailCardsNotesComponent({loading, data}) {
const {t} = useTranslation();
return (
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.notes")}
extraLink={`/manage/jobs/${data.id}?tab=notes`}
>
{data ? (
<Container>
<List
bordered
dataSource={data.notes}
renderItem={(item) => (
<List.Item style={{ whiteSpace: "pre-line" }}>
{item.critical ? (
<EyeInvisibleFilled style={{ margin: 4, color: "red" }} />
) : null}
{item.private ? <WarningFilled style={{ margin: 4 }} /> : null}
{item.audit ? <AuditOutlined style={{ margin: 4 }} /> : null}
{item.text}
</List.Item>
)}
/>
</Container>
) : null}
</CardTemplate>
);
return (
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.notes")}
extraLink={`/manage/jobs/${data.id}?tab=notes`}
>
{data ? (
<Container>
<List
bordered
dataSource={data.notes}
renderItem={(item) => (
<List.Item style={{whiteSpace: "pre-line"}}>
{item.critical ? (
<EyeInvisibleFilled style={{margin: 4, color: "red"}}/>
) : null}
{item.private ? <WarningFilled style={{margin: 4}}/> : null}
{item.audit ? <AuditOutlined style={{margin: 4}}/> : null}
{item.text}
</List.Item>
)}
/>
</Container>
) : null}
</CardTemplate>
);
}

View File

@@ -1,17 +1,17 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {useTranslation} from "react-i18next";
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsPartsComponent({ loading, data }) {
const { t } = useTranslation();
const { joblines_status } = data;
export default function JobDetailCardsPartsComponent({loading, data}) {
const {t} = useTranslation();
const {joblines_status} = data;
return (
<div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status} />
</CardTemplate>
</div>
);
return (
<div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status}/>
</CardTemplate>
</div>
);
}

View File

@@ -1,47 +1,47 @@
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 { createStructuredSelector } from "reselect";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
import {Link} from "react-router-dom";
import {createStructuredSelector} from "reselect";
import {selectTechnician} from "../../redux/tech/tech.selectors";
const mapStateToProps = createStructuredSelector({
technician: selectTechnician,
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(JobDetailCardTemplate);
export function JobDetailCardTemplate({
loading,
title,
extraLink,
technician,
...otherProps
}) {
const { t } = useTranslation();
loading,
title,
extraLink,
technician,
...otherProps
}) {
const {t} = useTranslation();
let extra;
if (extraLink && !technician)
extra = {
extra: <Link to={extraLink}>{t("jobs.labels.cards.more")}</Link>,
};
return (
<Card
size="small"
className="job-card"
title={title}
loading={loading}
style={{ height: "100%" }}
{...extra}
>
{otherProps.children}
</Card>
);
let extra;
if (extraLink && !technician)
extra = {
extra: <Link to={extraLink}>{t("jobs.labels.cards.more")}</Link>,
};
return (
<Card
size="small"
className="job-card"
title={title}
loading={loading}
style={{height: "100%"}}
{...extra}
>
{otherProps.children}
</Card>
);
}

View File

@@ -1,37 +1,37 @@
import { Statistic } from "antd";
import {Statistic} from "antd";
import Dinero from "dinero.js";
import React from "react";
import { useTranslation } from "react-i18next";
import {useTranslation} from "react-i18next";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsTotalsComponent({ loading, data }) {
const { t } = useTranslation();
export default function JobDetailCardsTotalsComponent({loading, data}) {
const {t} = useTranslation();
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.totals")}>
{data.job_totals ? (
<div className="imex-flex-row imex-flex-row__flex-space-around">
<Statistic
className="imex-flex-row__margin-large"
title={t("jobs.labels.total_repairs")}
value={Dinero(data.job_totals.totals.total_repairs).toFormat()}
/>
<Statistic
className="imex-flex-row__margin-large"
title={t("jobs.fields.ded_amt")}
value={Dinero({
amount: Math.round((data.ded_amt || 0) * 100),
}).toFormat()}
/>
<Statistic
className="imex-flex-row__margin-large"
title={t("jobs.labels.net_repairs")}
value={Dinero(data.job_totals.totals.net_repairs).toFormat()}
/>
</div>
) : (
t("jobs.errors.nofinancial")
)}
</CardTemplate>
);
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.totals")}>
{data.job_totals ? (
<div className="imex-flex-row imex-flex-row__flex-space-around">
<Statistic
className="imex-flex-row__margin-large"
title={t("jobs.labels.total_repairs")}
value={Dinero(data.job_totals.totals.total_repairs).toFormat()}
/>
<Statistic
className="imex-flex-row__margin-large"
title={t("jobs.fields.ded_amt")}
value={Dinero({
amount: Math.round((data.ded_amt || 0) * 100),
}).toFormat()}
/>
<Statistic
className="imex-flex-row__margin-large"
title={t("jobs.labels.net_repairs")}
value={Dinero(data.job_totals.totals.net_repairs).toFormat()}
/>
</div>
) : (
t("jobs.errors.nofinancial")
)}
</CardTemplate>
);
}

View File

@@ -1,23 +1,23 @@
import React from "react";
import { useTranslation } from "react-i18next";
import {useTranslation} from "react-i18next";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsVehicleComponent({ loading, data }) {
const { t } = useTranslation();
export default function JobDetailCardsVehicleComponent({loading, data}) {
const {t} = useTranslation();
return (
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.vehicle")}
extraLink={data.vehicle ? `/manage/vehicles/${data.vehicle.id}` : null}
>
{data ? (
<span>
return (
<CardTemplate
loading={loading}
title={t("jobs.labels.cards.vehicle")}
extraLink={data.vehicle ? `/manage/vehicles/${data.vehicle.id}` : null}
>
{data ? (
<span>
{` ${data.vehicle?.v_model_yr || t("general.labels.na")} ${
data.vehicle?.v_make_desc || t("general.labels.na")
data.vehicle?.v_make_desc || t("general.labels.na")
} ${data.vehicle?.v_model_desc || t("general.labels.na")}`}
</span>
) : null}
</CardTemplate>
);
) : null}
</CardTemplate>
);
}