WIP Styling Changes
This commit is contained in:
@@ -16045,6 +16045,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ins_co_nm_short</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ins_ct_fn</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "craco start",
|
||||
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` react-scripts build",
|
||||
"build-deploy": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build && s3cmd sync build/* s3://imex-online-production && echo '🚀 Deployed!'",
|
||||
"test": "craco test",
|
||||
"eject": "react-scripts eject",
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function AppContainer() {
|
||||
return (
|
||||
<ApolloProvider client={client}>
|
||||
<ConfigProvider
|
||||
componentSize="small"
|
||||
//componentSize="small"
|
||||
input={{ autoComplete: "new-password" }}
|
||||
locale={enLocale}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import React, { lazy, Suspense, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -48,48 +47,46 @@ export function App({ checkUserSession, currentUser }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Switch>
|
||||
<Suspense fallback={<LoadingSpinner message="App.Js Suspense" />}>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/" component={LandingPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/signin" component={SignInPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/resetpassword" component={ResetPassword} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/csi/:surveyId" component={CsiPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/about" component={AboutPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route
|
||||
exact
|
||||
path="/mp/:paymentIs"
|
||||
component={MobilePaymentContainer}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
path="/manage"
|
||||
component={ManagePage}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
path="/tech"
|
||||
component={TechPageContainer}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Suspense>
|
||||
</Switch>
|
||||
</div>
|
||||
<Switch>
|
||||
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/" component={LandingPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/signin" component={SignInPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/resetpassword" component={ResetPassword} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/csi/:surveyId" component={CsiPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route exact path="/about" component={AboutPage} />
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<Route
|
||||
exact
|
||||
path="/mp/:paymentIs"
|
||||
component={MobilePaymentContainer}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
path="/manage"
|
||||
component={ManagePage}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
<ErrorBoundary>
|
||||
<PrivateRoute
|
||||
isAuthorized={currentUser.authorized}
|
||||
path="/tech"
|
||||
component={TechPageContainer}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Suspense>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
.ant-picker-input,
|
||||
.ant-picker,
|
||||
.ant-select {
|
||||
width: 100%;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.production-alert {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.breadcrumb-container {
|
||||
margin: 0.5rem 4rem;
|
||||
margin: 0.2rem 0.2rem 0.8rem 0.2rem;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { MessageFilled } from "@ant-design/icons";
|
||||
import { notification } from "antd";
|
||||
import parsePhoneNumber from "libphonenumber-js";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { openChatByPhone } from "../../redux/messaging/messaging.actions";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||
@@ -12,16 +12,13 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
export function ChatOpenButton({ phone, jobid, openChatByPhone }) {
|
||||
const { t } = useTranslation();
|
||||
if (!phone) return <></>;
|
||||
return (
|
||||
<MessageFilled
|
||||
style={{ margin: 4 }}
|
||||
<a
|
||||
href="# "
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const p = parsePhoneNumber(phone, "CA");
|
||||
console.log(
|
||||
"🚀 ~ file: chat-open-button.component.jsx ~ line 21 ~ p",
|
||||
p
|
||||
);
|
||||
|
||||
if (p && p.isValid()) {
|
||||
openChatByPhone({ phone_num: p.formatInternational(), jobid: jobid });
|
||||
@@ -29,7 +26,9 @@ export function ChatOpenButton({ phone, jobid, openChatByPhone }) {
|
||||
notification["error"]({ message: t("messaging.error.invalidphone") });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<PhoneNumberFormatter>{phone}</PhoneNumberFormatter>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(ChatOpenButton);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Typography } from "antd";
|
||||
import React from "react";
|
||||
|
||||
export default function DataLabel({
|
||||
@@ -11,14 +12,28 @@ export default function DataLabel({
|
||||
if (!visible || (hideIfNull && !!!children)) return null;
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
<div {...props} style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
display: vertical ? "block" : "inline-block",
|
||||
flex: 2,
|
||||
marginRight: ".2rem",
|
||||
}}>{`${label}: `}</div>
|
||||
<div style={{ display: vertical ? "block" : "inline-block" }}>
|
||||
{children}
|
||||
}}
|
||||
>
|
||||
<Typography.Text type="secondary">{`${label}:`}</Typography.Text>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: 4,
|
||||
marginLeft: ".3rem",
|
||||
fontWeight: "bolder",
|
||||
wordWrap: "break-word",
|
||||
}}
|
||||
>
|
||||
{typeof children === "string" ? (
|
||||
<Typography.Text>{children}</Typography.Text>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,10 +13,7 @@ export default function FormsFieldChanged({ form }) {
|
||||
const loc = useLocation();
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
shouldUpdate
|
||||
//style={{ margin: 0, padding: 0 }}
|
||||
>
|
||||
<Form.Item shouldUpdate style={{ margin: 0, padding: 0 }}>
|
||||
{() => {
|
||||
if (form.isFieldsTouched())
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,7 @@ import Icon, {
|
||||
UnorderedListOutlined,
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Avatar, Menu } from "antd";
|
||||
import { Menu } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { BsKanban } from "react-icons/bs";
|
||||
@@ -67,10 +67,10 @@ function Header({
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<div style={{ display: "flex", alignContent: "center" }}>
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
theme="dark"
|
||||
theme="light"
|
||||
style={{ flex: 5 }}
|
||||
selectedKeys={[selectedHeader]}
|
||||
onClick={handleMenuClick}
|
||||
@@ -297,38 +297,15 @@ function Header({
|
||||
<Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
</Menu>
|
||||
<GlobalSearch />
|
||||
<Menu
|
||||
mode="horizontal"
|
||||
theme="dark"
|
||||
selectedKeys={[selectedHeader]}
|
||||
onClick={handleMenuClick}
|
||||
>
|
||||
|
||||
<Menu.Item>
|
||||
<GlobalSearch />
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<div>
|
||||
{currentUser.photoURL ? (
|
||||
<Avatar
|
||||
src={currentUser.photoURL}
|
||||
style={{
|
||||
margin: "10px",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Avatar
|
||||
style={{
|
||||
backgroundColor: "#87d068",
|
||||
margin: "10px",
|
||||
}}
|
||||
icon={<UserOutlined />}
|
||||
/>
|
||||
)}
|
||||
|
||||
{currentUser.displayName ||
|
||||
currentUser.email ||
|
||||
t("general.labels.unknown")}
|
||||
</div>
|
||||
currentUser.displayName ||
|
||||
currentUser.email ||
|
||||
t("general.labels.unknown")
|
||||
}
|
||||
>
|
||||
<Menu.Item danger onClick={() => signOutStart()}>
|
||||
|
||||
@@ -8,17 +8,8 @@ export default function JiraSupportComponent() {
|
||||
|
||||
const useScript = () => {
|
||||
useEffect(() => {
|
||||
console.log("Creating JIRA widget.");
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://jsd-widget.atlassian.com/assets/embed.js";
|
||||
// script["data-jsd-embedded"] = true;
|
||||
// script["data-key"] = "d69bb65c-1dd3-483f-b109-66a970d03f44";
|
||||
// script["data-base-url"] = "https://jsd-widget.atlassian.com";
|
||||
|
||||
// script.attributes.setNamedItem("data-jsd-embedded");
|
||||
// script.attributes.setNamedItem("data-key");
|
||||
// script.attributes.setNamedItem("data-base-url");
|
||||
|
||||
script.setAttribute("data-jsd-embedded", true);
|
||||
script.setAttribute("data-key", "d69bb65c-1dd3-483f-b109-66a970d03f44");
|
||||
script.setAttribute("data-base-url", "https://jsd-widget.atlassian.com");
|
||||
|
||||
@@ -136,7 +136,7 @@ export function JobCostingModalComponent({ bodyshop, job }) {
|
||||
|
||||
let gppercentFormatted;
|
||||
if (isNaN(gppercent)) gppercentFormatted = "0%";
|
||||
else if (!isFinite(gppercent)) gppercentFormatted = "-∞";
|
||||
else if (!isFinite(gppercent)) gppercentFormatted = "- ∞";
|
||||
else {
|
||||
gppercentFormatted = `${gppercent}%`;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PrinterFilled } from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Col, Drawer, Grid, PageHeader, Row, Space, Tag } from "antd";
|
||||
import { Button, Card, Drawer, Grid, PageHeader, Space, Tag } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -25,15 +25,6 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
||||
});
|
||||
|
||||
const colBreakPoints = {
|
||||
xs: {
|
||||
span: 24,
|
||||
},
|
||||
sm: {
|
||||
span: 12,
|
||||
},
|
||||
};
|
||||
|
||||
export function JobDetailCards({ setPrintCenterContext }) {
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
@@ -69,20 +60,23 @@ export function JobDetailCards({ setPrintCenterContext }) {
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const gridStyle = {
|
||||
width: "25%",
|
||||
textAlign: "center",
|
||||
};
|
||||
return (
|
||||
<Drawer
|
||||
visible={!!selected}
|
||||
destroyOnClose
|
||||
width={drawerPercentage}
|
||||
placement="right"
|
||||
height={drawerPercentage}
|
||||
placement="top"
|
||||
onClose={handleDrawerClose}
|
||||
>
|
||||
{loading ? <LoadingSpinner /> : null}
|
||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||
{data ? (
|
||||
<PageHeader
|
||||
ghost={true}
|
||||
// ghost={true}
|
||||
tags={[
|
||||
<OwnerTagPopoverComponent key="owner" job={data.jobs_by_pk} />,
|
||||
<VehicleTagPopoverComponent key="vehicle" job={data.jobs_by_pk} />,
|
||||
@@ -99,79 +93,80 @@ export function JobDetailCards({ setPrintCenterContext }) {
|
||||
{t("jobs.labels.inproduction")}
|
||||
</Tag>,
|
||||
]}
|
||||
title={
|
||||
<Link to={`/manage/jobs/${data.jobs_by_pk.id}`}>
|
||||
{data.jobs_by_pk.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
}
|
||||
subTitle={data.jobs_by_pk.status}
|
||||
extra={
|
||||
<Space>
|
||||
<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>
|
||||
}
|
||||
>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col {...colBreakPoints}>
|
||||
<Card
|
||||
title={
|
||||
<Link to={`/manage/jobs/${data.jobs_by_pk.id}`}>
|
||||
{data.jobs_by_pk.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
}
|
||||
extra={
|
||||
<Space>
|
||||
<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>
|
||||
}
|
||||
>
|
||||
<Card.Grid style={gridStyle}>
|
||||
<JobDetailCardsInsuranceComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colBreakPoints}>
|
||||
</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>
|
||||
<JobDetailCardsTotalsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colBreakPoints}>
|
||||
</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>
|
||||
<JobDetailCardsDatesComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colBreakPoints}>
|
||||
</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colBreakPoints}>
|
||||
</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>
|
||||
<JobDetailCardsNotesComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colBreakPoints}>
|
||||
</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>
|
||||
<JobDetailCardsDocumentsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colBreakPoints}>
|
||||
</Card.Grid>
|
||||
<Card.Grid style={gridStyle}>
|
||||
<JobDetailCardsDamageComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card.Grid>
|
||||
</Card>
|
||||
</PageHeader>
|
||||
) : null}
|
||||
</Drawer>
|
||||
|
||||
@@ -246,10 +246,6 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
|
||||
const newTotals = await Axios.post("/job/totalsssu", {
|
||||
id: selectedJob,
|
||||
});
|
||||
console.log(
|
||||
"🚀 ~ file: jobs-available-table.container.jsx ~ line 247 ~ newTotals",
|
||||
newTotals
|
||||
);
|
||||
|
||||
if (newTotals.status !== 200) {
|
||||
notification["error"]({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DownCircleFilled } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Dropdown, Menu, notification } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -90,14 +90,15 @@ export function JobsChangeStatus({ job, bodyshop, jobRO }) {
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
className="imex-flex-row__margin"
|
||||
overlay={statusmenu}
|
||||
trigger={["click"]}
|
||||
key="changestatus"
|
||||
disabled={jobRO || !job.converted}
|
||||
>
|
||||
<Button>
|
||||
{t("jobs.actions.changestatus")} <DownCircleFilled />
|
||||
<Button shape="round">
|
||||
<span>{job.status}</span>
|
||||
|
||||
<DownCircleFilled />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
@@ -110,13 +110,14 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO }) {
|
||||
</div>
|
||||
);
|
||||
|
||||
if (job.converted) return <></>;
|
||||
|
||||
return (
|
||||
<Popover visible={visible} content={popMenu}>
|
||||
<Button
|
||||
key="convert"
|
||||
className="imex-flex-row__margin"
|
||||
type="danger"
|
||||
style={{ display: job.converted ? "none" : "" }}
|
||||
// style={{ display: job.converted ? "none" : "" }}
|
||||
disabled={job.converted || jobRO}
|
||||
onClick={() => setVisible(true)}
|
||||
>
|
||||
|
||||
@@ -390,14 +390,11 @@ export function JobsDetailHeaderActions({
|
||||
</Menu>
|
||||
);
|
||||
return (
|
||||
<Dropdown
|
||||
className="imex-flex-row__margin"
|
||||
overlay={statusmenu}
|
||||
trigger={["click"]}
|
||||
key="changestatus"
|
||||
>
|
||||
<Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus">
|
||||
<Button>
|
||||
{t("general.labels.actions")} <DownCircleFilled />
|
||||
<span>{t("general.labels.actions")}</span>
|
||||
|
||||
<DownCircleFilled />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
import { PrinterFilled } from "@ant-design/icons";
|
||||
import { Button, Divider, PageHeader, Tag } from "antd";
|
||||
import { Card, Col, Row, Tag } from "antd";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
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 CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
||||
import JobSyncButton from "../job-sync-button/job-sync-button.component";
|
||||
import JobsChangeStatus from "../jobs-change-status/jobs-change-status.component";
|
||||
import JobsConvertButton from "../jobs-convert-button/jobs-convert-button.component";
|
||||
import JobsDetailHeaderActions from "../jobs-detail-header-actions/jobs-detail-header-actions.component";
|
||||
import OwnerTagPopoverComponent from "../owner-tag-popover/owner-tag-popover.component";
|
||||
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
|
||||
import VehicleTagPopoverComponent from "../vehicle-tag-popover/vehicle-tag-popover.component";
|
||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import "./jobs-detail-header.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -45,80 +39,127 @@ export function JobsDetailHeader({
|
||||
);
|
||||
}, [job.status, bodyshop.md_ro_statuses.post_production_statuses]);
|
||||
|
||||
const menuExtra = (
|
||||
<div className="imex-flex-row">
|
||||
<JobsChangeStatus job={job} />
|
||||
<JobSyncButton job={job} />
|
||||
<Button
|
||||
className="imex-flex-row__margin"
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
id: job.id,
|
||||
job: job,
|
||||
type: "job",
|
||||
},
|
||||
});
|
||||
}}
|
||||
key="printing"
|
||||
>
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
<JobsConvertButton job={job} refetch={refetch} />
|
||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
disabled={jobRO}
|
||||
className="imex-flex-row__margin"
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
const gridStyle = {
|
||||
flex: 1,
|
||||
//textAlign: "center",
|
||||
};
|
||||
|
||||
return (
|
||||
<PageHeader
|
||||
title={job.ro_number || t("general.labels.na")}
|
||||
subTitle={job.status}
|
||||
tags={[
|
||||
<OwnerTagPopoverComponent key="owner" job={job} />,
|
||||
<VehicleTagPopoverComponent key="vehicle" job={job} />,
|
||||
<Tag
|
||||
color="#f50"
|
||||
key="production"
|
||||
style={{ display: job.inproduction ? "" : "none" }}
|
||||
>
|
||||
{t("jobs.labels.inproduction")}
|
||||
</Tag>,
|
||||
<Tag title={t("jobs.fields.repairtotal")} key="total" color="green">
|
||||
<CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
|
||||
<span style={{ margin: "0rem .5rem" }}>/</span>
|
||||
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
|
||||
</Tag>,
|
||||
]}
|
||||
extra={menuExtra}
|
||||
>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
{(job.inproduction || jobInPostProduction) && (
|
||||
<>
|
||||
<div style={{ display: "flex", flex: 1 }}>
|
||||
<div style={{ marginRight: "2rem" }}>
|
||||
{t("jobs.fields.production_vars.note")}
|
||||
</div>
|
||||
<ProductionListColumnProductionNote record={job} />
|
||||
<Row gutter={16} style={{ alignItems: "stretch" }}>
|
||||
<Col span={8}>
|
||||
<Card title="Job Status" style={{ height: "100%" }}>
|
||||
<div>
|
||||
<DataLabel label={t("jobs.fields.status")}>
|
||||
{job.status}
|
||||
{job.inproduction && (
|
||||
<span style={{ marginLeft: ".5rem" }}>
|
||||
<Tag color="#f50" key="production">
|
||||
{t("jobs.labels.inproduction")}
|
||||
</Tag>
|
||||
</span>
|
||||
)}
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.ins_co_nm_short")}>
|
||||
{job.ins_co_nm}
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.clm_no")}>{job.clm_no}</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.repairtotal")}>
|
||||
<CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
|
||||
<span style={{ margin: "0rem .5rem" }}>/</span>
|
||||
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
|
||||
</DataLabel>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Link to={`/manage/owners/${job.owner.id}`}>
|
||||
<Card
|
||||
className="ant-card-grid-hoverable"
|
||||
style={{ height: "100%" }}
|
||||
title={`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||
job.ownr_co_nm || ""
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
|
||||
<ChatOpenButton>{job.ownr_ph1 || ""}</ChatOpenButton>
|
||||
</DataLabel>
|
||||
<DataLabel key="3" label={t("owners.fields.address")}>
|
||||
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
|
||||
job.ownr_city || ""
|
||||
} ${job.ownr_st || ""} ${job.ownr_zip || ""}`}
|
||||
</DataLabel>
|
||||
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
||||
{job.ownr_ea || ""}
|
||||
</DataLabel>
|
||||
</div>
|
||||
<Divider type="vertical" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<JobEmployeeAssignments job={job} />
|
||||
</div>
|
||||
</PageHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Link to={`/manage/vehicles/${job.vehicle.id}`}>
|
||||
<Card
|
||||
className="ant-card-grid-hoverable"
|
||||
style={{ height: "100%" }}
|
||||
title={`${job.v_model_yr || ""} ${job.v_color || ""}
|
||||
${job.v_make_desc || ""}
|
||||
${job.v_model_desc || ""}`}
|
||||
>
|
||||
<div>
|
||||
<DataLabel key="2" label={t("vehicles.fields.plate_no")}>
|
||||
{`${job.plate_no || t("general.labels.na")} (${`${
|
||||
job.plate_st || t("general.labels.na")
|
||||
}`})`}
|
||||
</DataLabel>
|
||||
<DataLabel key="4" label={t("vehicles.fields.v_vin")}>
|
||||
{`${job.v_vin || t("general.labels.na")}`}
|
||||
</DataLabel>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <PageHeader
|
||||
// title={job.ro_number || t("general.labels.na")}
|
||||
// subTitle={job.status}
|
||||
// tags={[
|
||||
// <OwnerTagPopoverComponent key="owner" job={job} />,
|
||||
// <VehicleTagPopoverComponent key="vehicle" job={job} />,
|
||||
// <Tag
|
||||
// color="#f50"
|
||||
// key="production"
|
||||
// style={{ display: job.inproduction ? "" : "none" }}
|
||||
// >
|
||||
// {t("jobs.labels.inproduction")}
|
||||
// </Tag>,
|
||||
// <Tag title={t("jobs.fields.repairtotal")} key="total" color="green">
|
||||
// <CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
|
||||
// <span style={{ margin: "0rem .5rem" }}>/</span>
|
||||
// <CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
|
||||
// </Tag>,
|
||||
// ]}
|
||||
// extra={menuExtra}
|
||||
// >
|
||||
// <div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
// {(job.inproduction || jobInPostProduction) && (
|
||||
// <>
|
||||
// <div style={{ display: "flex", flex: 1 }}>
|
||||
// <div style={{ marginRight: "2rem" }}>
|
||||
// {t("jobs.fields.production_vars.note")}
|
||||
// </div>
|
||||
// <ProductionListColumnProductionNote record={job} />
|
||||
// </div>
|
||||
// <Divider type="vertical" />
|
||||
// </>
|
||||
// )}
|
||||
|
||||
// <JobEmployeeAssignments job={job} />
|
||||
// </div>
|
||||
// </PageHeader>
|
||||
// );
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailHeader);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Input, Table } from "antd";
|
||||
import { Button, Card, Input, Space, Table } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -11,7 +11,6 @@ import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||
@@ -130,14 +129,10 @@ export function JobsList({ bodyshop }) {
|
||||
dataIndex: "ownr_ph1",
|
||||
key: "ownr_ph1",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.ownr_ph1 ? (
|
||||
<span>
|
||||
<PhoneFormatter>{record.ownr_ph1}</PhoneFormatter>
|
||||
<StartChatButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
</span>
|
||||
) : null;
|
||||
},
|
||||
responsive: ["md"],
|
||||
render: (text, record) => (
|
||||
<StartChatButton phone={record.ownr_ph1} jobid={record.id} />
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
@@ -160,9 +155,6 @@ export function JobsList({ bodyshop }) {
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
render: (text, record) => {
|
||||
return record.status || t("general.labels.na");
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
@@ -188,28 +180,20 @@ export function JobsList({ bodyshop }) {
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.plate_no ? record.plate_no : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
ellipsis: true,
|
||||
responsive: ["md"],
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.clm_no ? (
|
||||
<span>{record.clm_no}</span>
|
||||
) : (
|
||||
t("general.labels.unknown")
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
@@ -224,68 +208,65 @@ export function JobsList({ bodyshop }) {
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.clm_total ? (
|
||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
) : (
|
||||
t("general.labels.unknown")
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner_owing"),
|
||||
dataIndex: "owner_owing",
|
||||
key: "owner_owing",
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// title: t("jobs.fields.owner_owing"),
|
||||
// dataIndex: "owner_owing",
|
||||
// key: "owner_owing",
|
||||
// responsive: ["md"],
|
||||
// render: (text, record) => (
|
||||
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
|
||||
// ),
|
||||
// },
|
||||
];
|
||||
|
||||
return (
|
||||
<Table
|
||||
loading={loading}
|
||||
size="small"
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
style={{ height: "100%" }}
|
||||
scroll={{ x: true }}
|
||||
title={() => {
|
||||
return (
|
||||
<div className="imex-table-header">
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
className="imex-table-header__search"
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
value={searchText}
|
||||
enterButton
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
rowSelection={{
|
||||
onSelect: (record) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
selectedRowKeys: [selected],
|
||||
type: "radio",
|
||||
}}
|
||||
onChange={handleTableChange}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
<Card
|
||||
title={t("titles.bc.jobs-active")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={(e) => {
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
value={searchText}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
size="small"
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
// scroll={{ x: true }}
|
||||
rowSelection={{
|
||||
onSelect: (record) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
selectedRowKeys: [selected],
|
||||
type: "radio",
|
||||
}}
|
||||
onChange={handleTableChange}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ function RbacWrapper({
|
||||
authLevel ||
|
||||
(!bodyshop.md_rbac && rbacDefaults[action] <= authLevel)
|
||||
)
|
||||
return <div>{React.cloneElement(children, restProps)}</div>;
|
||||
return children;
|
||||
//return <div>{React.cloneElement(children, restProps)}</div>;
|
||||
|
||||
return (
|
||||
noauth || (
|
||||
|
||||
@@ -42,7 +42,7 @@ ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<PersistGate
|
||||
loading={<LoadingSpinner message="PersistGate Loading." />}
|
||||
loading={<LoadingSpinner message="Restoring your settings..." />}
|
||||
persistor={persistor}
|
||||
>
|
||||
<AppContainer />
|
||||
@@ -54,7 +54,6 @@ ReactDOM.render(
|
||||
|
||||
const onServiceWorkerUpdate = (registration) => {
|
||||
console.log("onServiceWorkerUpdate", registration);
|
||||
|
||||
const key = `open${Date.now()}`;
|
||||
const btn = (
|
||||
<Button
|
||||
|
||||
@@ -3,140 +3,134 @@ import Icon, {
|
||||
CalendarFilled,
|
||||
DollarCircleOutlined,
|
||||
FileImageFilled,
|
||||
PrinterFilled,
|
||||
ToolFilled,
|
||||
} from "@ant-design/icons";
|
||||
import { Form, notification, Tabs } from "antd";
|
||||
import { Button, Form, notification, PageHeader, Space, Tabs } from "antd";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
import moment from "moment";
|
||||
import queryString from "query-string";
|
||||
import React, { lazy, Suspense, useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaHardHat, FaRegStickyNote, FaShieldAlt } from "react-icons/fa";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
//import JobsDetailChecklists from "../../components/jobs-detail-checklists/jobs-detail-checklists.component";
|
||||
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
||||
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
|
||||
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
||||
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
||||
import JobsChangeStatus from "../../components/jobs-change-status/jobs-change-status.component";
|
||||
import JobsConvertButton from "../../components/jobs-convert-button/jobs-convert-button.component";
|
||||
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
|
||||
import JobsDetailGeneral from "../../components/jobs-detail-general/jobs-detail-general.component";
|
||||
import JobsDetailHeaderActions from "../../components/jobs-detail-header-actions/jobs-detail-header-actions.component";
|
||||
import JobsDetailHeader from "../../components/jobs-detail-header/jobs-detail-header.component";
|
||||
import JobsDetailLaborContainer from "../../components/jobs-detail-labor/jobs-detail-labor.container";
|
||||
import JobsDetailPliContainer from "../../components/jobs-detail-pli/jobs-detail-pli.container";
|
||||
import JobsDetailRates from "../../components/jobs-detail-rates/jobs-detail-rates.component";
|
||||
import JobsDetailTotals from "../../components/jobs-detail-totals/jobs-detail-totals.component";
|
||||
import JobsDocumentsGalleryContainer from "../../components/jobs-documents-gallery/jobs-documents-gallery.container";
|
||||
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
|
||||
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const JobsLinesContainer = lazy(() =>
|
||||
import("../../components/job-detail-lines/job-lines.container")
|
||||
);
|
||||
|
||||
const JobsDetailDatesComponent = lazy(() =>
|
||||
import("../../components/jobs-detail-dates/jobs-detail-dates.component")
|
||||
);
|
||||
const JobsDetailTotals = lazy(() =>
|
||||
import("../../components/jobs-detail-totals/jobs-detail-totals.component")
|
||||
);
|
||||
const JobsDetailRates = lazy(() =>
|
||||
import("../../components/jobs-detail-rates/jobs-detail-rates.component")
|
||||
);
|
||||
const JobsDetailHeader = lazy(() =>
|
||||
import("../../components/jobs-detail-header/jobs-detail-header.component")
|
||||
);
|
||||
const JobsDetailGeneral = lazy(() =>
|
||||
import("../../components/jobs-detail-general/jobs-detail-general.component")
|
||||
);
|
||||
const JobsDocumentsGalleryContainer = lazy(() =>
|
||||
import(
|
||||
"../../components/jobs-documents-gallery/jobs-documents-gallery.container"
|
||||
)
|
||||
);
|
||||
const JobNotesContainer = lazy(() =>
|
||||
import("../../components/jobs-notes/jobs-notes.container")
|
||||
);
|
||||
const ScheduleJobModalContainer = lazy(() =>
|
||||
import("../../components/schedule-job-modal/schedule-job-modal.container")
|
||||
);
|
||||
const JobLineUpsertModalContainer = lazy(() =>
|
||||
import(
|
||||
"../../components/job-lines-upsert-modal/job-lines-upsert-modal.container"
|
||||
)
|
||||
);
|
||||
const JobsDetailPliContainer = lazy(() =>
|
||||
import("../../components/jobs-detail-pli/jobs-detail-pli.container")
|
||||
);
|
||||
// const JobsDetailAuditContainer = lazy(() =>
|
||||
// import("../../components/audit-trail-list/audit-trail-list.container")
|
||||
// );
|
||||
const JobsDetailLaborContainer = lazy(() =>
|
||||
import("../../components/jobs-detail-labor/jobs-detail-labor.container")
|
||||
);
|
||||
const JobReconciliationModal = lazy(() =>
|
||||
import(
|
||||
"../../components/job-reconciliation-modal/job-reconciliation.modal.container"
|
||||
)
|
||||
);
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPrintCenterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
||||
});
|
||||
export function JobsDetailPage({
|
||||
setPrintCenterContext,
|
||||
jobRO,
|
||||
job,
|
||||
mutationUpdateJob,
|
||||
handleSubmit,
|
||||
refetch,
|
||||
jobRO,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const history = useHistory();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const search = queryString.parse(useLocation().search);
|
||||
|
||||
const formItemLayout = {
|
||||
layout: "vertical",
|
||||
// size: "small",
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.resetFields();
|
||||
//form.setFieldsValue(transormJobToForm(job));
|
||||
form.resetFields();
|
||||
}, [form, job]);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
//const newTotals = CalculateJob({ ...job, ...values }, bodyshop.shoprates);
|
||||
const newTotals = (
|
||||
await Axios.post("/job/totals", {
|
||||
job: { ...job, ...values },
|
||||
})
|
||||
).data;
|
||||
|
||||
const result = await mutationUpdateJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
...values,
|
||||
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
|
||||
"0.00"
|
||||
),
|
||||
job_totals: newTotals, //JSON.stringify(newTotals),
|
||||
},
|
||||
job: values,
|
||||
},
|
||||
});
|
||||
const newTotals = await Axios.post("/job/totalsssu", {
|
||||
id: job.id,
|
||||
});
|
||||
|
||||
if (!!!result.errors) {
|
||||
if (newTotals.status !== 200 || result.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.totalscalc"),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.savetitle"),
|
||||
});
|
||||
await refetch();
|
||||
form.resetFields();
|
||||
form.setFieldsValue(transormJobToForm(job));
|
||||
form.resetFields();
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const menuExtra = (
|
||||
<Space wrap>
|
||||
<JobsChangeStatus job={job} />
|
||||
<JobSyncButton job={job} />
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
id: job.id,
|
||||
job: job,
|
||||
type: "job",
|
||||
},
|
||||
});
|
||||
}}
|
||||
key="printing"
|
||||
>
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
<JobsConvertButton job={job} refetch={refetch} />
|
||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
disabled={jobRO}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return (
|
||||
<Suspense
|
||||
fallback={<LoadingSpinner message={t("general.labels.loadingapp")} />}
|
||||
>
|
||||
<div>
|
||||
<ScheduleJobModalContainer />
|
||||
<JobReconciliationModal />
|
||||
<JobLineUpsertModalContainer />
|
||||
@@ -146,23 +140,16 @@ export function JobsDetailPage({
|
||||
onFinish={handleFinish}
|
||||
{...formItemLayout}
|
||||
autoComplete={"off"}
|
||||
initialValues={{
|
||||
...job,
|
||||
loss_date: job.loss_date ? moment(job.loss_date) : null,
|
||||
date_estimated: job.date_estimated
|
||||
? moment(job.date_estimated)
|
||||
: null,
|
||||
}}
|
||||
initialValues={transormJobToForm(job)}
|
||||
>
|
||||
<FormFieldsChanged form={form} />
|
||||
|
||||
<JobsDetailHeader
|
||||
form={form}
|
||||
job={job}
|
||||
refetch={refetch}
|
||||
handleSubmit={handleSubmit}
|
||||
loading={loading}
|
||||
<PageHeader
|
||||
onBack={() => window.history.back()}
|
||||
title={job.ro_number || t("general.labels.na")}
|
||||
extra={menuExtra}
|
||||
/>
|
||||
<JobsDetailHeader job={job} />
|
||||
|
||||
<FormFieldsChanged form={form} />
|
||||
<Tabs
|
||||
defaultActiveKey={search.tab}
|
||||
onChange={(key) => history.push({ search: `?tab=${key}` })}
|
||||
@@ -271,33 +258,17 @@ export function JobsDetailPage({
|
||||
>
|
||||
<JobNotesContainer jobId={job.id} />
|
||||
</Tabs.TabPane>
|
||||
{
|
||||
// <Tabs.TabPane
|
||||
// tab={
|
||||
// <span>
|
||||
// <Icon component={FaHistory} />
|
||||
// {t("jobs.labels.audit")}
|
||||
// </span>
|
||||
// }
|
||||
// key="audit"
|
||||
// >
|
||||
// <JobsDetailAuditContainer recordId={job.id} />
|
||||
// </Tabs.TabPane>
|
||||
// <Tabs.TabPane
|
||||
// tab={
|
||||
// <span>
|
||||
// <CheckSquareFilled />
|
||||
// {t("jobs.labels.checklists")}
|
||||
// </span>
|
||||
// }
|
||||
// key="checklists"
|
||||
// >
|
||||
// <JobsDetailChecklists job={job} />
|
||||
// </Tabs.TabPane>
|
||||
}
|
||||
</Tabs>
|
||||
</Form>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(JobsDetailPage);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailPage);
|
||||
|
||||
const transormJobToForm = (job) => {
|
||||
return {
|
||||
...job,
|
||||
loss_date: job.loss_date ? moment(job.loss_date) : null,
|
||||
date_estimated: job.date_estimated ? moment(job.date_estimated) : null,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,11 +3,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import JobDetailCards from "../../components/job-detail-cards/job-detail-cards.component";
|
||||
import JobsList from "../../components/jobs-list/jobs-list.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -27,10 +27,8 @@ export function JobsPage({ setBreadcrumbs, setSelectedHeader }) {
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:list-active">
|
||||
<div className="jobs-list-container">
|
||||
<JobsList />
|
||||
<JobDetailCards />
|
||||
</div>
|
||||
<JobsList />
|
||||
<JobDetailCards />
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,15 +12,14 @@ import ChatAffixContainer from "../../components/chat-affix/chat-affix.container
|
||||
import ConflictComponent from "../../components/conflict/conflict.component";
|
||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
|
||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||
//import FooterComponent from "../../components/footer/footer.component";
|
||||
//Component Imports
|
||||
|
||||
import HeaderContainer from "../../components/header/header.container";
|
||||
import JiraSupportComponent from "../../components/jira-support-widget/jira-support-widget.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
|
||||
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||
import TestComponent from "../../components/_test/test.component";
|
||||
import { QUERY_STRIPE_ID } from "../../graphql/bodyshop.queries";
|
||||
import {
|
||||
@@ -34,6 +33,7 @@ const ManageRootPage = lazy(() =>
|
||||
import("../manage-root/manage-root.page.container")
|
||||
);
|
||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||
|
||||
const JobsDetailPage = lazy(() =>
|
||||
import("../jobs-detail/jobs-detail.page.container")
|
||||
);
|
||||
@@ -154,7 +154,7 @@ const EmailTest = lazy(() =>
|
||||
import("../../components/email-test/email-test-component")
|
||||
);
|
||||
|
||||
const { Content, Header } = Layout;
|
||||
const { Content, Footer } = Layout;
|
||||
|
||||
const stripePromise = new Promise((resolve, reject) => {
|
||||
client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
|
||||
@@ -197,7 +197,6 @@ export function Manage({ match, conflict, bodyshop }) {
|
||||
<PrintCenterModalContainer />
|
||||
<Route exact path={`${match.path}/_test`} component={TestComponent} />
|
||||
<Route exact path={`${match.path}`} component={ManageRootPage} />
|
||||
|
||||
<Route exact path={`${match.path}/jobs`} component={JobsPage} />
|
||||
<Switch>
|
||||
<Route
|
||||
@@ -365,27 +364,27 @@ export function Manage({ match, conflict, bodyshop }) {
|
||||
|
||||
return (
|
||||
<Layout className="layout-container">
|
||||
<Header>
|
||||
<HeaderContainer />
|
||||
</Header>
|
||||
<HeaderContainer />
|
||||
|
||||
<Content className="content-container">
|
||||
<FcmNotification />
|
||||
<PartnerPingComponent />
|
||||
<ErrorBoundary>{PageContent}</ErrorBoundary>
|
||||
<ChatAffixContainer />
|
||||
|
||||
<BackTop />
|
||||
<div style={{ textAlign: "center", margin: "1rem 0rem" }}>
|
||||
<div>
|
||||
{`ImEX Online V.${process.env.NODE_ENV} - ${
|
||||
process.env.REACT_APP_GIT_SHA
|
||||
} - ${preval`module.exports = new Date().toLocaleString();`}`}
|
||||
<Footer>
|
||||
<div style={{ textAlign: "center", margin: "1rem 0rem" }}>
|
||||
<div>
|
||||
{`ImEX Online V.${process.env.NODE_ENV} - ${
|
||||
process.env.REACT_APP_GIT_SHA
|
||||
} - ${preval`module.exports = new Date().toLocaleString();`}`}
|
||||
</div>
|
||||
<Link to="/about" target="_blank" style={{ color: "#ccc" }}>
|
||||
Disclaimer
|
||||
</Link>
|
||||
<JiraSupportComponent />
|
||||
</div>
|
||||
<Link to="/about" target="_blank" style={{ color: "#ccc" }}>
|
||||
Disclaimer
|
||||
</Link>
|
||||
<JiraSupportComponent />
|
||||
</div>
|
||||
</Footer>
|
||||
</Content>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -2,21 +2,17 @@ import { useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
|
||||
import { setBodyshop } from "../../redux/user/user.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ManagePage from "./manage.page.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop });
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
|
||||
});
|
||||
|
||||
function ManagePageContainer({ match, setBodyshop, bodyshop }) {
|
||||
function ManagePageContainer({ match, setBodyshop }) {
|
||||
const { loading, error, data } = useQuery(QUERY_BODYSHOP, {
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
@@ -34,7 +30,4 @@ function ManagePageContainer({ match, setBodyshop, bodyshop }) {
|
||||
return <ManagePage match={match} />;
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ManagePageContainer);
|
||||
export default connect(null, mapDispatchToProps)(ManagePageContainer);
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
.content-container {
|
||||
overflow-y: auto;
|
||||
margin: 1rem 1rem 0rem 1rem;
|
||||
padding: 0.25rem 2.5rem 1rem 2.5rem;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
padding-bottom: 3rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.layout-container {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -1001,6 +1001,7 @@
|
||||
"ins_city": "Insurance City",
|
||||
"ins_co_id": "Insurance Co. ID",
|
||||
"ins_co_nm": "Insurance Company Name",
|
||||
"ins_co_nm_short": "Ins. Co.",
|
||||
"ins_ct_fn": "File Handler First Name",
|
||||
"ins_ct_ln": "File Handler Last Name",
|
||||
"ins_ea": "File Handler Email",
|
||||
|
||||
@@ -1001,6 +1001,7 @@
|
||||
"ins_city": "Ciudad de seguros",
|
||||
"ins_co_id": "ID de la compañía de seguros",
|
||||
"ins_co_nm": "Nombre de la compañía de seguros",
|
||||
"ins_co_nm_short": "",
|
||||
"ins_ct_fn": "Nombre del controlador de archivos",
|
||||
"ins_ct_ln": "Apellido del manejador de archivos",
|
||||
"ins_ea": "Correo electrónico del controlador de archivos",
|
||||
|
||||
@@ -1001,6 +1001,7 @@
|
||||
"ins_city": "Insurance City",
|
||||
"ins_co_id": "ID de la compagnie d'assurance",
|
||||
"ins_co_nm": "Nom de la compagnie d'assurance",
|
||||
"ins_co_nm_short": "",
|
||||
"ins_ct_fn": "Prénom du gestionnaire de fichiers",
|
||||
"ins_ct_ln": "Nom du gestionnaire de fichiers",
|
||||
"ins_ea": "Courriel du gestionnaire de fichiers",
|
||||
|
||||
18
client/src/utils/usetraceupdate.jsx
Normal file
18
client/src/utils/usetraceupdate.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
function useTraceUpdate(props) {
|
||||
const prev = useRef(props);
|
||||
useEffect(() => {
|
||||
const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
|
||||
if (prev.current[k] !== v) {
|
||||
ps[k] = [prev.current[k], v];
|
||||
}
|
||||
return ps;
|
||||
}, {});
|
||||
if (Object.keys(changedProps).length > 0) {
|
||||
console.log("Changed props:", changedProps);
|
||||
}
|
||||
prev.current = props;
|
||||
});
|
||||
}
|
||||
|
||||
export default useTraceUpdate;
|
||||
Reference in New Issue
Block a user