- Merge client update into test-beta
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -1,85 +1,83 @@
|
||||
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 {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AccountingPayablesTable from "../../components/accounting-payables-table/accounting-payables-table.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { checkPartnerStatus } from "../../components/partner-ping/partner-ping.component";
|
||||
import {checkPartnerStatus} from "../../components/partner-ping/partner-ping.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_BILLS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {QUERY_BILLS_FOR_EXPORT} from "../../graphql/accounting.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectPartnerVersion} from "../../redux/application/application.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function AccountingPayablesContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-payables");
|
||||
setSelectedHeader("payables");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/payables",
|
||||
label: t("titles.bc.accounting-payables"),
|
||||
},
|
||||
]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-payables");
|
||||
setSelectedHeader("payables");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/payables",
|
||||
label: t("titles.bc.accounting-payables"),
|
||||
},
|
||||
]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_BILLS_FOR_EXPORT, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {loading, error, data, refetch} = useQuery(QUERY_BILLS_FOR_EXPORT, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:payables">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingPayablesTable
|
||||
loadaing={loading}
|
||||
bills={data ? data.bills : []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:payables">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingPayablesTable
|
||||
loadaing={loading}
|
||||
bills={data ? data.bills : []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(AccountingPayablesContainer);
|
||||
|
||||
@@ -1,85 +1,84 @@
|
||||
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 {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AccountingPaymentsTable from "../../components/accounting-payments-table/accounting-payments-table.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_PAYMENTS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {QUERY_PAYMENTS_FOR_EXPORT} from "../../graphql/accounting.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { checkPartnerStatus } from "../../components/partner-ping/partner-ping.component";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
import {checkPartnerStatus} from "../../components/partner-ping/partner-ping.component";
|
||||
import {selectPartnerVersion} from "../../redux/application/application.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function AccountingPaymentsContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-payments");
|
||||
setSelectedHeader("payments");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/payments",
|
||||
label: t("titles.bc.accounting-payments"),
|
||||
},
|
||||
]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-payments");
|
||||
setSelectedHeader("payments");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/payments",
|
||||
label: t("titles.bc.accounting-payments"),
|
||||
},
|
||||
]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_PAYMENTS_FOR_EXPORT,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
}
|
||||
);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_PAYMENTS_FOR_EXPORT,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
}
|
||||
);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:payments">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingPaymentsTable
|
||||
loadaing={loading}
|
||||
payments={data ? data.payments : []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:payments">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingPaymentsTable
|
||||
loadaing={loading}
|
||||
payments={data ? data.payments : []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(AccountingPaymentsContainer);
|
||||
|
||||
@@ -1,47 +1,46 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import QboAuthorizeComponent from "../../components/qbo-authorize/qbo-authorize.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function AccountingReceivablesContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-qbo");
|
||||
setSelectedHeader("qbo");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/qbo",
|
||||
label: t("titles.bc.accounting-qbo"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-qbo");
|
||||
setSelectedHeader("qbo");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/qbo",
|
||||
label: t("titles.bc.accounting-qbo"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<QboAuthorizeComponent />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<QboAuthorizeComponent/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(AccountingReceivablesContainer);
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
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 AccountingReceivablesTable from "../../components/accounting-receivables-table/accounting-receivables-table.component";
|
||||
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 AccountingReceivablesTable
|
||||
from "../../components/accounting-receivables-table/accounting-receivables-table.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_JOBS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {QUERY_JOBS_FOR_EXPORT} from "../../graphql/accounting.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { checkPartnerStatus } from "../../components/partner-ping/partner-ping.component";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
import {checkPartnerStatus} from "../../components/partner-ping/partner-ping.component";
|
||||
import {selectPartnerVersion} from "../../redux/application/application.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function AccountingReceivablesContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-receivables");
|
||||
setSelectedHeader("receivables");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/receivables",
|
||||
label: t("titles.bc.accounting-receivables"),
|
||||
},
|
||||
]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.accounting-receivables");
|
||||
setSelectedHeader("receivables");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/receivables",
|
||||
label: t("titles.bc.accounting-receivables"),
|
||||
},
|
||||
]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_JOBS_FOR_EXPORT, {
|
||||
variables: {
|
||||
invoicedStatus: bodyshop.md_ro_statuses.default_invoiced || "Invoiced*",
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {loading, error, data, refetch} = useQuery(QUERY_JOBS_FOR_EXPORT, {
|
||||
variables: {
|
||||
invoicedStatus: bodyshop.md_ro_statuses.default_invoiced || "Invoiced*",
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:receivables">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingReceivablesTable
|
||||
loadaing={loading}
|
||||
jobs={data ? data.jobs : []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:receivables">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingReceivablesTable
|
||||
loadaing={loading}
|
||||
jobs={data ? data.jobs : []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(AccountingReceivablesContainer);
|
||||
|
||||
@@ -1,317 +1,318 @@
|
||||
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
|
||||
import {EditFilled, SyncOutlined} from "@ant-design/icons";
|
||||
import {Button, Card, Checkbox, Input, Space, Table, Typography} from "antd";
|
||||
import axios from "axios";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import BillDeleteButton from "../../components/bill-delete-button/bill-delete-button.component";
|
||||
import PartsOrderModalContainer from "../../components/parts-order-modal/parts-order-modal.container";
|
||||
import PrintWrapperComponent from "../../components/print-wrapper/print-wrapper.component";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import {DateFormatter} from "../../utils/DateFormatter";
|
||||
import {TemplateList} from "../../utils/TemplateConstants";
|
||||
import {alphaSort, dateSort} from "../../utils/sorters";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPartsOrderContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
setPartsOrderContext: (context) =>
|
||||
dispatch(setModalContext({context: context, modal: "partsOrder"})),
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({context: context, modal: "billEnter"})),
|
||||
});
|
||||
|
||||
export function BillsListPage({
|
||||
loading,
|
||||
data,
|
||||
refetch,
|
||||
total,
|
||||
setPartsOrderContext,
|
||||
setBillEnterContext,
|
||||
}) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const [openSearchResults, setOpenSearchResults] = useState([]);
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const { page } = search;
|
||||
const history = useHistory();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
});
|
||||
const Templates = TemplateList("bill");
|
||||
const { t } = useTranslation();
|
||||
const columns = [
|
||||
{
|
||||
title: t("bills.fields.vendorname"),
|
||||
dataIndex: "vendorname",
|
||||
key: "vendorname",
|
||||
// sortObject: (direction) => {
|
||||
// return {
|
||||
// vendor: {
|
||||
// name: direction
|
||||
// ? direction === "descend"
|
||||
// ? "desc"
|
||||
// : "asc"
|
||||
// : "desc",
|
||||
// },
|
||||
// };
|
||||
// },
|
||||
// sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||
// sortOrder:
|
||||
// state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
|
||||
render: (text, record) => <span>{record.vendor.name}</span>,
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: "invoice_number",
|
||||
key: "invoice_number",
|
||||
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "invoice_number" &&
|
||||
state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
// sortObject: (direction) => {
|
||||
// return {
|
||||
// job: {
|
||||
// ro_number: direction
|
||||
// ? direction === "descend"
|
||||
// ? "desc"
|
||||
// : "asc"
|
||||
// : "desc",
|
||||
// },
|
||||
// };
|
||||
// },
|
||||
// sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||
// sortOrder:
|
||||
// state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
record.job && (
|
||||
<Link to={`/manage/jobs/${record.job.id}`}>
|
||||
{record.job.ro_number}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.date"),
|
||||
dataIndex: "date",
|
||||
key: "date",
|
||||
sorter: (a, b) => dateSort(a.date, b.date),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.total"),
|
||||
dataIndex: "total",
|
||||
key: "total",
|
||||
sorter: (a, b) => a.total - b.total,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.total}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.is_credit_memo"),
|
||||
dataIndex: "is_credit_memo",
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "is_credit_memo" &&
|
||||
state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<Checkbox disabled checked={record.is_credit_memo} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.exported"),
|
||||
dataIndex: "exported",
|
||||
key: "exported",
|
||||
sorter: (a, b) => a.exported - b.exported,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "exported" && state.sortedInfo.order,
|
||||
render: (text, record) => <Checkbox disabled checked={record.exported} />,
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
<Link to={`/manage/bills?billid=${record.id}`}>
|
||||
<Button>
|
||||
<EditFilled />
|
||||
</Button>
|
||||
</Link>
|
||||
{
|
||||
// <Button
|
||||
// disabled={record.is_credit_memo}
|
||||
// onClick={() =>
|
||||
// setPartsOrderContext({
|
||||
// actions: {},
|
||||
// context: {
|
||||
// jobId: record.jobid,
|
||||
// vendorId: record.vendorid,
|
||||
// returnFromBill: record.id,
|
||||
// invoiceNumber: record.invoice_number,
|
||||
// linesToOrder: record.billlines.map((i) => {
|
||||
// return {
|
||||
// line_desc: i.line_desc,
|
||||
// // db_price: i.actual_price,
|
||||
// act_price: i.actual_price,
|
||||
// cost: i.actual_cost,
|
||||
// quantity: i.quantity,
|
||||
// joblineid: i.joblineid,
|
||||
// };
|
||||
// }),
|
||||
// isReturn: true,
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
// >
|
||||
// {t("bills.actions.return")}
|
||||
// </Button>
|
||||
}
|
||||
<BillDeleteButton
|
||||
bill={record}
|
||||
callback={(deletedBillid) => {
|
||||
//Filter out the state and set it again.
|
||||
setOpenSearchResults((currentResults) =>
|
||||
currentResults.filter((bill) => bill.id !== deletedBillid)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{record.isinhouse && (
|
||||
<PrintWrapperComponent
|
||||
templateObject={{
|
||||
name: Templates.inhouse_invoice.key,
|
||||
variables: { id: record.id },
|
||||
}}
|
||||
messageObject={{ subject: Templates.inhouse_invoice.subject }}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
loading,
|
||||
data,
|
||||
refetch,
|
||||
total,
|
||||
setPartsOrderContext,
|
||||
setBillEnterContext,
|
||||
}) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const [openSearchResults, setOpenSearchResults] = useState([]);
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const {page} = search;
|
||||
const history = useNavigate();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: {text: ""},
|
||||
});
|
||||
const Templates = TemplateList("bill");
|
||||
const {t} = useTranslation();
|
||||
const columns = [
|
||||
{
|
||||
title: t("bills.fields.vendorname"),
|
||||
dataIndex: "vendorname",
|
||||
key: "vendorname",
|
||||
// sortObject: (direction) => {
|
||||
// return {
|
||||
// vendor: {
|
||||
// name: direction
|
||||
// ? direction === "descend"
|
||||
// ? "desc"
|
||||
// : "asc"
|
||||
// : "desc",
|
||||
// },
|
||||
// };
|
||||
// },
|
||||
// sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
|
||||
// sortOrder:
|
||||
// state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
|
||||
render: (text, record) => <span>{record.vendor.name}</span>,
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: "invoice_number",
|
||||
key: "invoice_number",
|
||||
sorter: (a, b) => alphaSort(a.invoice_number, b.invoice_number),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "invoice_number" &&
|
||||
state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
// sortObject: (direction) => {
|
||||
// return {
|
||||
// job: {
|
||||
// ro_number: direction
|
||||
// ? direction === "descend"
|
||||
// ? "desc"
|
||||
// : "asc"
|
||||
// : "desc",
|
||||
// },
|
||||
// };
|
||||
// },
|
||||
// sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||
// sortOrder:
|
||||
// state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
record.job && (
|
||||
<Link to={`/manage/jobs/${record.job.id}`}>
|
||||
{record.job.ro_number}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.date"),
|
||||
dataIndex: "date",
|
||||
key: "date",
|
||||
sorter: (a, b) => dateSort(a.date, b.date),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
|
||||
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.total"),
|
||||
dataIndex: "total",
|
||||
key: "total",
|
||||
sorter: (a, b) => a.total - b.total,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.total}</CurrencyFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.is_credit_memo"),
|
||||
dataIndex: "is_credit_memo",
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.is_credit_memo - b.is_credit_memo,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "is_credit_memo" &&
|
||||
state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<Checkbox disabled checked={record.is_credit_memo}/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.exported"),
|
||||
dataIndex: "exported",
|
||||
key: "exported",
|
||||
sorter: (a, b) => a.exported - b.exported,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "exported" && state.sortedInfo.order,
|
||||
render: (text, record) => <Checkbox disabled checked={record.exported}/>,
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
<Link to={`/manage/bills?billid=${record.id}`}>
|
||||
<Button>
|
||||
<EditFilled/>
|
||||
</Button>
|
||||
</Link>
|
||||
{
|
||||
// <Button
|
||||
// disabled={record.is_credit_memo}
|
||||
// onClick={() =>
|
||||
// setPartsOrderContext({
|
||||
// actions: {},
|
||||
// context: {
|
||||
// jobId: record.jobid,
|
||||
// vendorId: record.vendorid,
|
||||
// returnFromBill: record.id,
|
||||
// invoiceNumber: record.invoice_number,
|
||||
// linesToOrder: record.billlines.map((i) => {
|
||||
// return {
|
||||
// line_desc: i.line_desc,
|
||||
// // db_price: i.actual_price,
|
||||
// act_price: i.actual_price,
|
||||
// cost: i.actual_cost,
|
||||
// quantity: i.quantity,
|
||||
// joblineid: i.joblineid,
|
||||
// };
|
||||
// }),
|
||||
// isReturn: true,
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
// >
|
||||
// {t("bills.actions.return")}
|
||||
// </Button>
|
||||
}
|
||||
<BillDeleteButton
|
||||
bill={record}
|
||||
callback={(deletedBillid) => {
|
||||
//Filter out the state and set it again.
|
||||
setOpenSearchResults((currentResults) =>
|
||||
currentResults.filter((bill) => bill.id !== deletedBillid)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{record.isinhouse && (
|
||||
<PrintWrapperComponent
|
||||
templateObject={{
|
||||
name: Templates.inhouse_invoice.key,
|
||||
variables: {id: record.id},
|
||||
}}
|
||||
messageObject={{subject: Templates.inhouse_invoice.subject}}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
search.page = pagination.current;
|
||||
if (sorter && sorter.column && sorter.column.sortObject) {
|
||||
search.searchObj = JSON.stringify(sorter.column.sortObject(sorter.order));
|
||||
} else {
|
||||
delete search.searchObj;
|
||||
search.sortcolumn = sorter.order ? sorter.columnKey : null;
|
||||
search.sortorder = sorter.order;
|
||||
}
|
||||
search.sort = JSON.stringify({ [sorter.columnKey]: sorter.order });
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (search.search && search.search.trim() !== "") {
|
||||
searchBills();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
async function searchBills(value) {
|
||||
try {
|
||||
setSearchLoading(true);
|
||||
const searchData = await axios.post("/search", {
|
||||
search: value || search.search,
|
||||
index: "bills",
|
||||
});
|
||||
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
|
||||
} catch (error) {
|
||||
console.log("Error while fetching search results", error);
|
||||
} finally {
|
||||
setSearchLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("bills.labels.bills")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
{search.search && (
|
||||
<>
|
||||
<Typography.Title level={4}>
|
||||
{t("general.labels.searchresults", { search: search.search })}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
onClick={() => {
|
||||
delete search.search;
|
||||
delete search.page;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}}
|
||||
>
|
||||
{t("general.actions.clear")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.postbills")}
|
||||
</Button>
|
||||
|
||||
<Input.Search
|
||||
placeholder={search.search || t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
search.search = value;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
searchBills(value);
|
||||
}}
|
||||
loading={loading || searchLoading}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<PartsOrderModalContainer />
|
||||
|
||||
<Table
|
||||
loading={loading || searchLoading}
|
||||
// scroll={{
|
||||
// x: "50%", // y: "40rem"
|
||||
// }}
|
||||
scroll={{ x: true }}
|
||||
pagination={
|
||||
search?.search
|
||||
? {
|
||||
pageSize: pageLimit,
|
||||
showSizeChanger: false,
|
||||
}
|
||||
: {
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
showSizeChanger: false,
|
||||
}
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({...state, filteredInfo: filters, sortedInfo: sorter});
|
||||
search.page = pagination.current;
|
||||
if (sorter && sorter.column && sorter.column.sortObject) {
|
||||
search.searchObj = JSON.stringify(sorter.column.sortObject(sorter.order));
|
||||
} else {
|
||||
delete search.searchObj;
|
||||
search.sortcolumn = sorter.order ? sorter.columnKey : null;
|
||||
search.sortorder = sorter.order;
|
||||
}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={search?.search ? openSearchResults : data}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
search.sort = JSON.stringify({[sorter.columnKey]: sorter.order});
|
||||
history({search: queryString.stringify(search)});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (search.search && search.search.trim() !== "") {
|
||||
searchBills();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
async function searchBills(value) {
|
||||
try {
|
||||
setSearchLoading(true);
|
||||
const searchData = await axios.post("/search", {
|
||||
search: value || search.search,
|
||||
index: "bills",
|
||||
});
|
||||
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
|
||||
} catch (error) {
|
||||
console.log("Error while fetching search results", error);
|
||||
} finally {
|
||||
setSearchLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("bills.labels.bills")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
{search.search && (
|
||||
<>
|
||||
<Typography.Title level={4}>
|
||||
{t("general.labels.searchresults", {search: search.search})}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
onClick={() => {
|
||||
delete search.search;
|
||||
delete search.page;
|
||||
history({search: queryString.stringify(search)});
|
||||
}}
|
||||
>
|
||||
{t("general.actions.clear")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setBillEnterContext({
|
||||
actions: {refetch: refetch},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.postbills")}
|
||||
</Button>
|
||||
|
||||
<Input.Search
|
||||
placeholder={search.search || t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
search.search = value;
|
||||
history({search: queryString.stringify(search)});
|
||||
searchBills(value);
|
||||
}}
|
||||
loading={loading || searchLoading}
|
||||
enterButton
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<PartsOrderModalContainer/>
|
||||
|
||||
<Table
|
||||
loading={loading || searchLoading}
|
||||
// scroll={{
|
||||
// x: "50%", // y: "40rem"
|
||||
// }}
|
||||
scroll={{x: true}}
|
||||
pagination={
|
||||
search?.search
|
||||
? {
|
||||
pageSize: pageLimit,
|
||||
showSizeChanger: false,
|
||||
}
|
||||
: {
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: total,
|
||||
showSizeChanger: false,
|
||||
}
|
||||
}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={search?.search ? openSearchResults : data}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(BillsListPage);
|
||||
|
||||
@@ -1,75 +1,73 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import BillDetailEditContainer from "../../components/bill-detail-edit/bill-detail-edit.container";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_ALL_BILLS_PAGINATED } from "../../graphql/bills.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {QUERY_ALL_BILLS_PAGINATED} from "../../graphql/bills.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import BillsPageComponent from "./bills.page.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function BillsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder, searchObj } = searchParams;
|
||||
export function BillsPageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder, searchObj} = searchParams;
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.bills-list");
|
||||
setSelectedHeader("bills");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/bills", label: t("titles.bc.bills-list") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.bills-list");
|
||||
setSelectedHeader("bills");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/bills", label: t("titles.bc.bills-list")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_ALL_BILLS_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
searchObj
|
||||
? JSON.parse(searchObj)
|
||||
: {
|
||||
[sortcolumn || "date"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_ALL_BILLS_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
searchObj
|
||||
? JSON.parse(searchObj)
|
||||
: {
|
||||
[sortcolumn || "date"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<RbacWrapper action="bills:list">
|
||||
<div>
|
||||
<BillsPageComponent
|
||||
data={data ? data.bills : []}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
total={data ? data.bills_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
return (
|
||||
<RbacWrapper action="bills:list">
|
||||
<div>
|
||||
<BillsPageComponent
|
||||
data={data ? data.bills : []}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
total={data ? data.bills_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
|
||||
<BillDetailEditContainer />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
<BillDetailEditContainer/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(BillsPageContainer);
|
||||
|
||||
@@ -1,68 +1,70 @@
|
||||
import { Button, Col, PageHeader, Row, Space, Form, Switch } from "antd";
|
||||
import {Button, Col, Form, Row, Space, Switch} from "antd";
|
||||
import {PageHeader} from "@ant-design/pro-layout";
|
||||
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import ContractCarsContainer from "../../components/contract-cars/contract-cars.container";
|
||||
import ContractFormComponent from "../../components/contract-form/contract-form.component";
|
||||
import ContractJobsContainer from "../../components/contract-jobs/contract-jobs.container";
|
||||
|
||||
export default function ContractCreatePageComponent({
|
||||
form,
|
||||
selectedJobState,
|
||||
selectedCarState,
|
||||
loading,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
form,
|
||||
selectedJobState,
|
||||
selectedCarState,
|
||||
loading,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
const CreateButton = (
|
||||
<Space size="large">
|
||||
{selectedJobState[0] && selectedCarState[0] && (
|
||||
<Form.Item
|
||||
label={t("jobs.actions.addtoproduction")}
|
||||
name="addtoproduction"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Button
|
||||
disabled={!selectedJobState[0] || !selectedCarState[0]}
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
>
|
||||
{t("general.actions.create")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
const CreateButton = (
|
||||
<Space size="large">
|
||||
{selectedJobState[0] && selectedCarState[0] && (
|
||||
<Form.Item
|
||||
label={t("jobs.actions.addtoproduction")}
|
||||
name="addtoproduction"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch/>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Button
|
||||
disabled={!selectedJobState[0] || !selectedCarState[0]}
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
>
|
||||
{t("general.actions.create")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<ContractJobsContainer selectedJobState={selectedJobState} />
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ContractCarsContainer
|
||||
selectedCarState={selectedCarState}
|
||||
form={form}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<div
|
||||
style={{
|
||||
display: selectedJobState[0] && selectedCarState[0] ? "" : "none",
|
||||
}}
|
||||
>
|
||||
<ContractFormComponent
|
||||
create
|
||||
form={form}
|
||||
selectedJobState={selectedJobState}
|
||||
selectedCar={selectedCarState[0]}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<PageHeader extra={CreateButton} />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<ContractJobsContainer selectedJobState={selectedJobState}/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ContractCarsContainer
|
||||
selectedCarState={selectedCarState}
|
||||
form={form}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<div
|
||||
style={{
|
||||
display: selectedJobState[0] && selectedCarState[0] ? "" : "none",
|
||||
}}
|
||||
>
|
||||
<ContractFormComponent
|
||||
create
|
||||
form={form}
|
||||
selectedJobState={selectedJobState}
|
||||
selectedCar={selectedCarState[0]}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<PageHeader extra={CreateButton}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,145 +1,145 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Form, notification } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Form, notification} from "antd";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { INSERT_NEW_CONTRACT } from "../../graphql/cccontracts.queries";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {INSERT_NEW_CONTRACT} from "../../graphql/cccontracts.queries";
|
||||
import {UPDATE_JOB} from "../../graphql/jobs.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import ContractCreatePageComponent from "./contract-create.page.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ContractCreatePageContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const selectedCarState = useState(null);
|
||||
const selectedJobState = useState(
|
||||
(location.state && location.state.jobId) || null
|
||||
);
|
||||
const [insertContract] = useMutation(INSERT_NEW_CONTRACT);
|
||||
const [intakeJob] = useMutation(UPDATE_JOB);
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const [form] = Form.useForm();
|
||||
const {t} = useTranslation();
|
||||
|
||||
const handleFinish = async ({ addtoproduction, ...values }) => {
|
||||
if (!!selectedCarState[0] && !!selectedJobState[0]) {
|
||||
setLoading(true);
|
||||
const result = await insertContract({
|
||||
variables: {
|
||||
ccId: selectedCarState[0].id,
|
||||
damage: values.damage,
|
||||
mileage: values.kmstart,
|
||||
contract: {
|
||||
...values,
|
||||
status: "contracts.status.out",
|
||||
courtesycarid: selectedCarState[0].id,
|
||||
jobid: selectedJobState[0],
|
||||
},
|
||||
},
|
||||
});
|
||||
const history = useNavigate();
|
||||
const location = useLocation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const selectedCarState = useState(null);
|
||||
const selectedJobState = useState(
|
||||
(location.state && location.state.jobId) || null
|
||||
);
|
||||
|
||||
if (!result.errors) {
|
||||
//Update the courtesy car to have the damage.
|
||||
notification["success"]({
|
||||
message: t("contracts.successes.saved"),
|
||||
});
|
||||
const [insertContract] = useMutation(INSERT_NEW_CONTRACT);
|
||||
const [intakeJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
//Intake the job if required
|
||||
if (addtoproduction) {
|
||||
const result2 = await intakeJob({
|
||||
variables: {
|
||||
jobId: selectedJobState[0],
|
||||
job: {
|
||||
actual_in: new Date(),
|
||||
inproduction: true,
|
||||
status: bodyshop.md_ro_statuses.default_arrived,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (result2.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
error: JSON.stringify(!result2.errors),
|
||||
}),
|
||||
const handleFinish = async ({addtoproduction, ...values}) => {
|
||||
if (!!selectedCarState[0] && !!selectedJobState[0]) {
|
||||
setLoading(true);
|
||||
const result = await insertContract({
|
||||
variables: {
|
||||
ccId: selectedCarState[0].id,
|
||||
damage: values.damage,
|
||||
mileage: values.kmstart,
|
||||
contract: {
|
||||
...values,
|
||||
status: "contracts.status.out",
|
||||
courtesycarid: selectedCarState[0].id,
|
||||
jobid: selectedJobState[0],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
//Update the courtesy car to have the damage.
|
||||
notification["success"]({
|
||||
message: t("contracts.successes.saved"),
|
||||
});
|
||||
|
||||
//Intake the job if required
|
||||
if (addtoproduction) {
|
||||
const result2 = await intakeJob({
|
||||
variables: {
|
||||
jobId: selectedJobState[0],
|
||||
job: {
|
||||
actual_in: new Date(),
|
||||
inproduction: true,
|
||||
status: bodyshop.md_ro_statuses.default_arrived,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (result2.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
error: JSON.stringify(!result2.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
history(
|
||||
`/manage/courtesycars/contracts/${result.data.insert_cccontracts.returning[0].id}`
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("contracts.errors.saving", {
|
||||
error: JSON.stringify(!result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("contracts.errors.selectjobandcar"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
history.push(
|
||||
`/manage/courtesycars/contracts/${result.data.insert_cccontracts.returning[0].id}`
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("contracts.errors.saving", {
|
||||
error: JSON.stringify(!result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("contracts.errors.selectjobandcar"),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
useEffect(() => {
|
||||
document.title = t("titles.contracts-create");
|
||||
setSelectedHeader("newcontract");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/courtesycars", label: t("titles.bc.courtesycars")},
|
||||
{
|
||||
link: "/manage/courtesycars/contracts",
|
||||
label: t("titles.bc.contracts"),
|
||||
},
|
||||
{
|
||||
link: "/manage/courtesycars/contracts/new",
|
||||
label: t("titles.bc.contracts-create"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.contracts-create");
|
||||
setSelectedHeader("newcontract");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/courtesycars", label: t("titles.bc.courtesycars") },
|
||||
{
|
||||
link: "/manage/courtesycars/contracts",
|
||||
label: t("titles.bc.contracts"),
|
||||
},
|
||||
{
|
||||
link: "/manage/courtesycars/contracts/new",
|
||||
label: t("titles.bc.contracts-create"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="contracts:create">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
>
|
||||
<ContractCreatePageComponent
|
||||
loading={loading}
|
||||
form={form}
|
||||
selectedJobState={selectedJobState}
|
||||
selectedCarState={selectedCarState}
|
||||
/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="contracts:create">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
>
|
||||
<ContractCreatePageComponent
|
||||
loading={loading}
|
||||
form={form}
|
||||
selectedJobState={selectedJobState}
|
||||
selectedCarState={selectedCarState}
|
||||
/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ContractCreatePageContainer);
|
||||
|
||||
@@ -1,160 +1,126 @@
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Dropdown,
|
||||
Form,
|
||||
Menu,
|
||||
PageHeader,
|
||||
Row,
|
||||
Space,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import {Button, Col, Dropdown, Form, Row, Space, Typography,} from "antd";
|
||||
import {PageHeader} from "@ant-design/pro-layout";
|
||||
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import ContractConvertToRo from "../../components/contract-convert-to-ro/contract-convert-to-ro.component";
|
||||
import ContractCourtesyCarBlock from "../../components/contract-courtesy-car-block/contract-courtesy-car-block.component";
|
||||
import ContractCourtesyCarBlock
|
||||
from "../../components/contract-courtesy-car-block/contract-courtesy-car-block.component";
|
||||
import ContractFormComponent from "../../components/contract-form/contract-form.component";
|
||||
import ContractJobBlock from "../../components/contract-job-block/contract-job-block.component";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {GenerateDocument} from "../../utils/RenderTemplate";
|
||||
import {TemplateList} from "../../utils/TemplateConstants";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setCourtesyCarReturnModalContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "courtesyCarReturn" })),
|
||||
setCourtesyCarReturnModalContext: (context) =>
|
||||
dispatch(setModalContext({context: context, modal: "courtesyCarReturn"})),
|
||||
});
|
||||
|
||||
export function ContractDetailPage({
|
||||
contract,
|
||||
job,
|
||||
courtesyCar,
|
||||
setCourtesyCarReturnModalContext,
|
||||
refetch,
|
||||
form,
|
||||
saveLoading,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<Row align="middle">
|
||||
<Typography.Title></Typography.Title>
|
||||
</Row>
|
||||
<PageHeader
|
||||
title={t("contracts.labels.agreement", {
|
||||
agreement_num: contract && contract.agreementnumber,
|
||||
status: t(contract && contract.status),
|
||||
})}
|
||||
extra={
|
||||
<Form.Item shouldUpdate>
|
||||
{() => {
|
||||
return (
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={saveLoading}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
!!!contract ||
|
||||
(contract && contract.status !== "contracts.status.out")
|
||||
}
|
||||
onClick={() => {
|
||||
setCourtesyCarReturnModalContext({
|
||||
actions: { refetch },
|
||||
context: {
|
||||
contractId: contract.id,
|
||||
courtesyCarId: courtesyCar.id,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("courtesycars.actions.return")}
|
||||
</Button>
|
||||
<Dropdown
|
||||
trigger="click"
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
onClick={() =>
|
||||
GenerateDocument(
|
||||
{
|
||||
name: TemplateList("courtesycarcontract")
|
||||
.courtesy_car_contract.key,
|
||||
variables: { id: contract.id },
|
||||
},
|
||||
{},
|
||||
"p"
|
||||
)
|
||||
}
|
||||
>
|
||||
{t("contracts.actions.printcontract")}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={() =>
|
||||
GenerateDocument(
|
||||
{
|
||||
name: TemplateList("courtesycarcontract")
|
||||
.courtesy_car_terms.key,
|
||||
variables: { id: contract.id },
|
||||
},
|
||||
{},
|
||||
"p"
|
||||
)
|
||||
}
|
||||
>
|
||||
{t(
|
||||
"printcenter.courtesycarcontract.courtesy_car_terms"
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={() =>
|
||||
GenerateDocument(
|
||||
{
|
||||
name: TemplateList("courtesycarcontract")
|
||||
.courtesy_car_impound.key,
|
||||
variables: { id: contract.id },
|
||||
},
|
||||
{},
|
||||
"p"
|
||||
)
|
||||
}
|
||||
>
|
||||
{t(
|
||||
"printcenter.courtesycarcontract.courtesy_car_impound"
|
||||
)}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button>{t("general.labels.print")}</Button>
|
||||
</Dropdown>
|
||||
contract,
|
||||
job,
|
||||
courtesyCar,
|
||||
setCourtesyCarReturnModalContext,
|
||||
refetch,
|
||||
form,
|
||||
saveLoading
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<Row align="middle">
|
||||
<Typography.Title></Typography.Title>
|
||||
</Row>
|
||||
<PageHeader
|
||||
title={t("contracts.labels.agreement", {
|
||||
agreement_num: contract && contract.agreementnumber,
|
||||
status: t(contract && contract.status),
|
||||
})}
|
||||
extra={
|
||||
<Form.Item shouldUpdate>
|
||||
{() => {
|
||||
const menu = {
|
||||
onClick: (e) => {
|
||||
GenerateDocument(
|
||||
{
|
||||
name: TemplateList("courtesycarcontract")[e.key].key,
|
||||
variables: {id: contract.id},
|
||||
},
|
||||
{},
|
||||
"p"
|
||||
);
|
||||
},
|
||||
items: [
|
||||
{
|
||||
key: "courtesy_car_contract",
|
||||
label: t("contracts.actions.printcontract"),
|
||||
},
|
||||
{
|
||||
key: "courtesy_car_terms",
|
||||
label: t("printcenter.courtesycarcontract.courtesy_car_terms"),
|
||||
},
|
||||
{
|
||||
key: "courtesy_car_impound",
|
||||
label: t("printcenter.courtesycarcontract.courtesy_car_impound"),
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
<ContractConvertToRo
|
||||
contract={contract}
|
||||
disabled={form.isFieldsTouched()}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
}
|
||||
/>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col sm={24} md={12}>
|
||||
<ContractJobBlock job={job} />
|
||||
</Col>
|
||||
<Col sm={24} md={12}>
|
||||
<ContractCourtesyCarBlock courtesyCar={courtesyCar} />
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ContractFormComponent form={form} />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={saveLoading}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
!!!contract ||
|
||||
(contract && contract.status !== "contracts.status.out")
|
||||
}
|
||||
onClick={() => {
|
||||
setCourtesyCarReturnModalContext({
|
||||
actions: {refetch},
|
||||
context: {
|
||||
contractId: contract.id,
|
||||
courtesyCarId: courtesyCar.id,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("courtesycars.actions.return")}
|
||||
</Button>
|
||||
<Dropdown trigger="click" menu={menu}>
|
||||
<Button>{t("general.labels.print")}</Button>
|
||||
</Dropdown>
|
||||
|
||||
<ContractConvertToRo
|
||||
contract={contract}
|
||||
disabled={form.isFieldsTouched()}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
}
|
||||
/>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col sm={24} md={12}>
|
||||
<ContractJobBlock job={job}/>
|
||||
</Col>
|
||||
<Col sm={24} md={12}>
|
||||
<ContractCourtesyCarBlock courtesyCar={courtesyCar}/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<ContractFormComponent form={form}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ContractDetailPage);
|
||||
|
||||
@@ -1,171 +1,166 @@
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import { Form, notification } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {useMutation, useQuery} from "@apollo/client";
|
||||
import {Form, notification} from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import CourtesyCarReturnModalContainer from "../../components/courtesy-car-return-modal/courtesy-car-return-modal.container";
|
||||
import CourtesyCarReturnModalContainer
|
||||
from "../../components/courtesy-car-return-modal/courtesy-car-return-modal.container";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
QUERY_CONTRACT_BY_PK,
|
||||
UPDATE_CONTRACT,
|
||||
} from "../../graphql/cccontracts.queries";
|
||||
import {
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import {QUERY_CONTRACT_BY_PK, UPDATE_CONTRACT,} from "../../graphql/cccontracts.queries";
|
||||
import {addRecentItem, setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {CreateRecentItem} from "../../utils/create-recent-item";
|
||||
import ContractDetailPageComponent from "./contract-detail.page.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ContractDetailPageContainer({
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updateContract] = useMutation(UPDATE_CONTRACT);
|
||||
const [saveLoading, setsaveLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const { contractId } = useParams();
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const [updateContract] = useMutation(UPDATE_CONTRACT);
|
||||
const [saveLoading, setsaveLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const {contractId} = useParams();
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_CONTRACT_BY_PK, {
|
||||
variables: { id: contractId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {loading, error, data, refetch} = useQuery(QUERY_CONTRACT_BY_PK, {
|
||||
variables: {id: contractId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedHeader("contracts");
|
||||
document.title = loading
|
||||
? t("titles.app")
|
||||
: error
|
||||
? t("titles.app")
|
||||
: t("titles.contracts-detail", {
|
||||
id:
|
||||
(data &&
|
||||
data.cccontracts_by_pk &&
|
||||
data.cccontracts_by_pk.agreementnumber) ||
|
||||
"",
|
||||
});
|
||||
useEffect(() => {
|
||||
setSelectedHeader("contracts");
|
||||
document.title = loading
|
||||
? t("titles.app")
|
||||
: error
|
||||
? t("titles.app")
|
||||
: t("titles.contracts-detail", {
|
||||
id:
|
||||
(data &&
|
||||
data.cccontracts_by_pk &&
|
||||
data.cccontracts_by_pk.agreementnumber) ||
|
||||
"",
|
||||
});
|
||||
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/courtesycars", label: t("titles.bc.courtesycars") },
|
||||
{
|
||||
link: "/manage/courtesycars/contracts",
|
||||
label: t("titles.bc.contracts"),
|
||||
},
|
||||
{
|
||||
link: "/manage/courtesycars/contracts/new",
|
||||
label: t("titles.bc.contracts-detail", {
|
||||
number:
|
||||
(data &&
|
||||
data.cccontracts_by_pk &&
|
||||
data.cccontracts_by_pk.agreementnumber) ||
|
||||
"",
|
||||
}),
|
||||
},
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/courtesycars", label: t("titles.bc.courtesycars")},
|
||||
{
|
||||
link: "/manage/courtesycars/contracts",
|
||||
label: t("titles.bc.contracts"),
|
||||
},
|
||||
{
|
||||
link: "/manage/courtesycars/contracts/new",
|
||||
label: t("titles.bc.contracts-detail", {
|
||||
number:
|
||||
(data &&
|
||||
data.cccontracts_by_pk &&
|
||||
data.cccontracts_by_pk.agreementnumber) ||
|
||||
"",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
if (data && data.cccontracts_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
contractId,
|
||||
"contract",
|
||||
data.cccontracts_by_pk.agreementnumber,
|
||||
`/manage/courtesycars/contracts/${contractId}`
|
||||
)
|
||||
);
|
||||
}, [
|
||||
t,
|
||||
data,
|
||||
error,
|
||||
loading,
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
contractId,
|
||||
setSelectedHeader,
|
||||
]);
|
||||
|
||||
if (data && data.cccontracts_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
contractId,
|
||||
"contract",
|
||||
data.cccontracts_by_pk.agreementnumber,
|
||||
`/manage/courtesycars/contracts/${contractId}`
|
||||
)
|
||||
);
|
||||
}, [
|
||||
t,
|
||||
data,
|
||||
error,
|
||||
loading,
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
contractId,
|
||||
setSelectedHeader,
|
||||
]);
|
||||
const handleFinish = async (values) => {
|
||||
setsaveLoading(true);
|
||||
const result = await updateContract({
|
||||
variables: {cccontract: {...values}, contractId: contractId},
|
||||
});
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("contracts.errors.saving", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
notification["success"]({message: t("contracts.successes.saved")});
|
||||
if (refetch) await refetch();
|
||||
setsaveLoading(false);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setsaveLoading(true);
|
||||
const result = await updateContract({
|
||||
variables: { cccontract: { ...values }, contractId: contractId },
|
||||
});
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("contracts.errors.saving", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
notification["success"]({ message: t("contracts.successes.saved") });
|
||||
if (refetch) await refetch();
|
||||
setsaveLoading(false);
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
};
|
||||
useEffect(() => {
|
||||
if (data && data.cccontracts_by_pk) form.resetFields();
|
||||
}, [data, form]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data.cccontracts_by_pk) form.resetFields();
|
||||
}, [data, form]);
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (!!!data.cccontracts_by_pk) return <NotFound/>;
|
||||
|
||||
if (!!!data.cccontracts_by_pk) return <NotFound />;
|
||||
|
||||
return (
|
||||
<RbacWrapper action="contracts:detail">
|
||||
<div>
|
||||
<CourtesyCarReturnModalContainer />
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="no"
|
||||
layout="vertical"
|
||||
onFinish={handleFinish}
|
||||
initialValues={{
|
||||
...data.cccontracts_by_pk,
|
||||
start: data.cccontracts_by_pk.start
|
||||
? moment(data.cccontracts_by_pk.start)
|
||||
: null,
|
||||
scheduledreturn: data.cccontracts_by_pk.scheduledreturn
|
||||
? moment(data.cccontracts_by_pk.scheduledreturn)
|
||||
: null,
|
||||
actualreturn: data.cccontracts_by_pk.actualreturn
|
||||
? moment(data.cccontracts_by_pk.actualreturn)
|
||||
: null,
|
||||
driver_dlexpiry: data.cccontracts_by_pk.driver_dlexpiry
|
||||
? moment(data.cccontracts_by_pk.driver_dlexpiry)
|
||||
: null,
|
||||
driver_dob: data.cccontracts_by_pk.driver_dob
|
||||
? moment(data.cccontracts_by_pk.driver_dob)
|
||||
: null,
|
||||
}}
|
||||
>
|
||||
<ContractDetailPageComponent
|
||||
contract={data ? data.cccontracts_by_pk : null}
|
||||
job={data ? data.cccontracts_by_pk.job : null}
|
||||
courtesyCar={data ? data.cccontracts_by_pk.courtesycar : null}
|
||||
refetch={refetch}
|
||||
form={form}
|
||||
saveLoading={saveLoading}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="contracts:detail">
|
||||
<div>
|
||||
<CourtesyCarReturnModalContainer/>
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="no"
|
||||
layout="vertical"
|
||||
onFinish={handleFinish}
|
||||
initialValues={{
|
||||
...data.cccontracts_by_pk,
|
||||
start: data.cccontracts_by_pk.start
|
||||
? dayjs(data.cccontracts_by_pk.start)
|
||||
: null,
|
||||
scheduledreturn: data.cccontracts_by_pk.scheduledreturn
|
||||
? dayjs(data.cccontracts_by_pk.scheduledreturn)
|
||||
: null,
|
||||
actualreturn: data.cccontracts_by_pk.actualreturn
|
||||
? dayjs(data.cccontracts_by_pk.actualreturn)
|
||||
: null,
|
||||
driver_dlexpiry: data.cccontracts_by_pk.driver_dlexpiry
|
||||
? dayjs(data.cccontracts_by_pk.driver_dlexpiry)
|
||||
: null,
|
||||
driver_dob: data.cccontracts_by_pk.driver_dob
|
||||
? dayjs(data.cccontracts_by_pk.driver_dob)
|
||||
: null,
|
||||
}}
|
||||
>
|
||||
<ContractDetailPageComponent
|
||||
contract={data ? data.cccontracts_by_pk : null}
|
||||
job={data ? data.cccontracts_by_pk.job : null}
|
||||
courtesyCar={data ? data.cccontracts_by_pk.courtesycar : null}
|
||||
refetch={refetch}
|
||||
form={form}
|
||||
saveLoading={saveLoading}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ContractDetailPageContainer);
|
||||
|
||||
@@ -2,17 +2,17 @@ import React from "react";
|
||||
import ContractsList from "../../components/contracts-list/contracts-list.component";
|
||||
|
||||
export default function ContractsPageComponent({
|
||||
loading,
|
||||
data,
|
||||
refetch,
|
||||
total,
|
||||
}) {
|
||||
return (
|
||||
<ContractsList
|
||||
loading={loading}
|
||||
contracts={data}
|
||||
refetch={refetch}
|
||||
total={total}
|
||||
/>
|
||||
);
|
||||
loading,
|
||||
data,
|
||||
refetch,
|
||||
total,
|
||||
}) {
|
||||
return (
|
||||
<ContractsList
|
||||
loading={loading}
|
||||
contracts={data}
|
||||
refetch={refetch}
|
||||
total={total}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,72 +1,70 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_ACTIVE_CONTRACTS_PAGINATED } from "../../graphql/cccontracts.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {QUERY_ACTIVE_CONTRACTS_PAGINATED} from "../../graphql/cccontracts.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import ContractsPageComponent from "./contracts.page.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ContractsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { search, page, sortcolumn, sortorder } = searchParams;
|
||||
export function ContractsPageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {search, page, sortcolumn, sortorder} = searchParams;
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_ACTIVE_CONTRACTS_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
search: search || "",
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "start"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.contracts");
|
||||
setSelectedHeader("contracts");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/courtesycars", label: t("titles.bc.courtesycars") },
|
||||
{
|
||||
link: "/manage/courtesycars/contracts",
|
||||
label: t("titles.bc.contracts"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_ACTIVE_CONTRACTS_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
search: search || "",
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "start"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.contracts");
|
||||
setSelectedHeader("contracts");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/courtesycars", label: t("titles.bc.courtesycars")},
|
||||
{
|
||||
link: "/manage/courtesycars/contracts",
|
||||
label: t("titles.bc.contracts"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<RbacWrapper action="contracts:list">
|
||||
<ContractsPageComponent
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
data={data ? data.search_cccontracts : []}
|
||||
total={data ? data.search_cccontracts_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
return (
|
||||
<RbacWrapper action="contracts:list">
|
||||
<ContractsPageComponent
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
data={data ? data.search_cccontracts : []}
|
||||
total={data ? data.search_cccontracts_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ContractsPageContainer);
|
||||
|
||||
@@ -1,87 +1,86 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Form, notification } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useMutation} from "@apollo/client";
|
||||
import {Form, notification} from "antd";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import CourtesyCarFormComponent from "../../components/courtesy-car-form/courtesy-car-form.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { INSERT_NEW_COURTESY_CAR } from "../../graphql/courtesy-car.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {INSERT_NEW_COURTESY_CAR} from "../../graphql/courtesy-car.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function CourtesyCarCreateContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertCourtesyCar] = useMutation(INSERT_NEW_COURTESY_CAR);
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertCourtesyCar] = useMutation(INSERT_NEW_COURTESY_CAR);
|
||||
const {t} = useTranslation();
|
||||
const history = useNavigate();
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await insertCourtesyCar({
|
||||
variables: { courtesycar: { ...values, bodyshopid: bodyshop.id } },
|
||||
});
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await insertCourtesyCar({
|
||||
variables: {courtesycar: {...values, bodyshopid: bodyshop.id}},
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("courtesycars.errors.saving", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
} else {
|
||||
setLoading(false);
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
notification["success"]({ message: t("courtesycars.successes.saved") });
|
||||
history.push(
|
||||
`/manage/courtesycars/${result.data.insert_courtesycars.returning[0].id}`
|
||||
);
|
||||
}
|
||||
};
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("courtesycars.errors.saving", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
} else {
|
||||
setLoading(false);
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
notification["success"]({message: t("courtesycars.successes.saved")});
|
||||
history(
|
||||
`/manage/courtesycars/${result.data.insert_courtesycars.returning[0].id}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedHeader("courtesycarsall");
|
||||
document.title = t("titles.courtesycars-create");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/courtesycars", label: t("titles.bc.courtesycars") },
|
||||
{
|
||||
link: "/manage/courtesycars/new",
|
||||
label: t("titles.bc.courtesycars-new"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
setSelectedHeader("courtesycarsall");
|
||||
document.title = t("titles.courtesycars-create");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/courtesycars", label: t("titles.bc.courtesycars")},
|
||||
{
|
||||
link: "/manage/courtesycars/new",
|
||||
label: t("titles.bc.courtesycars-new"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:create">
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="new-password"
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
>
|
||||
<CourtesyCarFormComponent form={form} saveLoading={loading} />
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:create">
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="new-password"
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
>
|
||||
<CourtesyCarFormComponent form={form} saveLoading={loading}/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CourtesyCarCreateContainer);
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import React from "react";
|
||||
import CourtesyCarCreateFormComponent from "../../components/courtesy-car-form/courtesy-car-form.component";
|
||||
import CourtesyCarContractListComponent from "../../components/courtesy-car-contract-list/courtesy-car-contract-list.component";
|
||||
import { Col, Divider, Row } from "antd";
|
||||
import CourtesyCarContractListComponent
|
||||
from "../../components/courtesy-car-contract-list/courtesy-car-contract-list.component";
|
||||
import {Col, Divider, Row} from "antd";
|
||||
|
||||
export default function CourtesyCarDetailPageComponent({
|
||||
contracts,
|
||||
form,
|
||||
saveLoading,
|
||||
totalContracts,
|
||||
}) {
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<CourtesyCarCreateFormComponent form={form} saveLoading={saveLoading} />
|
||||
</Col>
|
||||
<Divider type="horizontal" />
|
||||
<Col span={24}>
|
||||
<CourtesyCarContractListComponent
|
||||
contracts={contracts}
|
||||
totalContracts={totalContracts}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
contracts,
|
||||
form,
|
||||
saveLoading,
|
||||
totalContracts,
|
||||
}) {
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<CourtesyCarCreateFormComponent form={form} saveLoading={saveLoading}/>
|
||||
</Col>
|
||||
<Divider type="horizontal"/>
|
||||
<Col span={24}>
|
||||
<CourtesyCarContractListComponent
|
||||
contracts={contracts}
|
||||
totalContracts={totalContracts}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,201 +1,199 @@
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import { Form, notification } from "antd";
|
||||
import moment from "moment";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
import {useMutation, useQuery} from "@apollo/client";
|
||||
import {Form, notification} from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation, useParams} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
|
||||
import {
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { pageLimit } from "../../utils/config";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import {QUERY_CC_BY_PK, UPDATE_CC} from "../../graphql/courtesy-car.queries";
|
||||
import {addRecentItem, setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {CreateRecentItem} from "../../utils/create-recent-item";
|
||||
import UndefinedToNull from "./../../utils/undefinedtonull";
|
||||
import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import queryString from "query-string";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function CourtesyCarDetailPageContainer({
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder } = searchParams;
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder} = searchParams;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [updateCourtesyCar] = useMutation(UPDATE_CC);
|
||||
const [form] = Form.useForm();
|
||||
const { ccId } = useParams();
|
||||
const [saveLoading, setSaveLoading] = useState(false);
|
||||
const { loading, error, data } = useQuery(QUERY_CC_BY_PK, {
|
||||
variables: {
|
||||
id: ccId,
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "start"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
const {t} = useTranslation();
|
||||
const [updateCourtesyCar] = useMutation(UPDATE_CC);
|
||||
const [form] = Form.useForm();
|
||||
const {ccId} = useParams();
|
||||
const [saveLoading, setSaveLoading] = useState(false);
|
||||
const {loading, error, data} = useQuery(QUERY_CC_BY_PK, {
|
||||
variables: {
|
||||
id: ccId,
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "start"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedHeader("courtesycarsall");
|
||||
useEffect(() => {
|
||||
setSelectedHeader("courtesycarsall");
|
||||
|
||||
document.title = loading
|
||||
? t("titles.app")
|
||||
: error
|
||||
? t("titles.app")
|
||||
: t("titles.courtesycars-detail", {
|
||||
id:
|
||||
(data &&
|
||||
data.courtesycars_by_pk &&
|
||||
data.courtesycars_by_pk.fleet_number) ||
|
||||
"",
|
||||
});
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/courtesycars", label: t("titles.bc.courtesycars") },
|
||||
{
|
||||
link: `/manage/courtesycars/${
|
||||
(data && data.courtesycars_by_pk && data.courtesycars_by_pk.id) || ""
|
||||
}`,
|
||||
label: t("titles.bc.courtesycars-detail", {
|
||||
number:
|
||||
(data &&
|
||||
data.courtesycars_by_pk &&
|
||||
data.courtesycars_by_pk.fleetnumber) ||
|
||||
"",
|
||||
}),
|
||||
},
|
||||
document.title = loading
|
||||
? t("titles.app")
|
||||
: error
|
||||
? t("titles.app")
|
||||
: t("titles.courtesycars-detail", {
|
||||
id:
|
||||
(data &&
|
||||
data.courtesycars_by_pk &&
|
||||
data.courtesycars_by_pk.fleet_number) ||
|
||||
"",
|
||||
});
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/courtesycars", label: t("titles.bc.courtesycars")},
|
||||
{
|
||||
link: `/manage/courtesycars/${
|
||||
(data && data.courtesycars_by_pk && data.courtesycars_by_pk.id) || ""
|
||||
}`,
|
||||
label: t("titles.bc.courtesycars-detail", {
|
||||
number:
|
||||
(data &&
|
||||
data.courtesycars_by_pk &&
|
||||
data.courtesycars_by_pk.fleetnumber) ||
|
||||
"",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
if (data && data.courtesycars_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
ccId,
|
||||
"courtesycar",
|
||||
data.courtesycars_by_pk.fleet_number || data.courtesycars_by_pk.vin,
|
||||
`/manage/courtesycars/${ccId}`
|
||||
)
|
||||
);
|
||||
}, [
|
||||
t,
|
||||
data,
|
||||
error,
|
||||
loading,
|
||||
setBreadcrumbs,
|
||||
ccId,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
]);
|
||||
|
||||
if (data && data.courtesycars_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
ccId,
|
||||
"courtesycar",
|
||||
data.courtesycars_by_pk.fleet_number || data.courtesycars_by_pk.vin,
|
||||
`/manage/courtesycars/${ccId}`
|
||||
)
|
||||
);
|
||||
}, [
|
||||
t,
|
||||
data,
|
||||
error,
|
||||
loading,
|
||||
setBreadcrumbs,
|
||||
ccId,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
]);
|
||||
const handleFinish = async (values) => {
|
||||
setSaveLoading(true);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setSaveLoading(true);
|
||||
const result = await updateCourtesyCar({
|
||||
variables: {
|
||||
cc: {...UndefinedToNull(values, ["readiness"])},
|
||||
ccId: ccId,
|
||||
},
|
||||
refetchQueries: ["QUERY_CC_BY_PK"],
|
||||
awaitRefetchQueries: true,
|
||||
});
|
||||
|
||||
const result = await updateCourtesyCar({
|
||||
variables: {
|
||||
cc: { ...UndefinedToNull(values, ["readiness"]) },
|
||||
ccId: ccId,
|
||||
},
|
||||
refetchQueries: ["QUERY_CC_BY_PK"],
|
||||
awaitRefetchQueries: true,
|
||||
});
|
||||
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("courtesycars.errors.saving", { error: error }),
|
||||
});
|
||||
}
|
||||
|
||||
notification["success"]({
|
||||
message: t("courtesycars.successes.saved"),
|
||||
});
|
||||
|
||||
setSaveLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data.courtesycars_by_pk) {
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
}
|
||||
}, [data, form]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
if (!!!data.courtesycars_by_pk) return <NotFound />;
|
||||
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:detail">
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
initialValues={
|
||||
data
|
||||
? {
|
||||
...data.courtesycars_by_pk,
|
||||
purchasedate: data.courtesycars_by_pk.purchasedate
|
||||
? moment(data.courtesycars_by_pk.purchasedate)
|
||||
: null,
|
||||
servicestartdate: data.courtesycars_by_pk.servicestartdate
|
||||
? moment(data.courtesycars_by_pk.servicestartdate)
|
||||
: null,
|
||||
serviceenddate: data.courtesycars_by_pk.serviceenddate
|
||||
? moment(data.courtesycars_by_pk.serviceenddate)
|
||||
: null,
|
||||
leaseenddate: data.courtesycars_by_pk.leaseenddate
|
||||
? moment(data.courtesycars_by_pk.leaseenddate)
|
||||
: null,
|
||||
nextservicedate: data.courtesycars_by_pk.nextservicedate
|
||||
? moment(data.courtesycars_by_pk.nextservicedate)
|
||||
: null,
|
||||
registrationexpires: data.courtesycars_by_pk.registrationexpires
|
||||
? moment(data.courtesycars_by_pk.registrationexpires)
|
||||
: null,
|
||||
insuranceexpires: data.courtesycars_by_pk.insuranceexpires
|
||||
? moment(data.courtesycars_by_pk.insuranceexpires)
|
||||
: null,
|
||||
}
|
||||
: {}
|
||||
if (!!result.errors) {
|
||||
notification["error"]({
|
||||
message: t("courtesycars.errors.saving", {error: error}),
|
||||
});
|
||||
}
|
||||
>
|
||||
<CourtesyCarDetailPageComponent
|
||||
contracts={data ? data.courtesycars_by_pk.cccontracts : []}
|
||||
form={form}
|
||||
saveLoading={saveLoading}
|
||||
totalContracts={
|
||||
data
|
||||
? data.courtesycars_by_pk.cccontracts_aggregate.aggregate.count
|
||||
: 0
|
||||
}
|
||||
/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
|
||||
notification["success"]({
|
||||
message: t("courtesycars.successes.saved"),
|
||||
});
|
||||
|
||||
setSaveLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data.courtesycars_by_pk) {
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
}
|
||||
}, [data, form]);
|
||||
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
if (!!!data.courtesycars_by_pk) return <NotFound/>;
|
||||
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:detail">
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
initialValues={
|
||||
data
|
||||
? {
|
||||
...data.courtesycars_by_pk,
|
||||
purchasedate: data.courtesycars_by_pk.purchasedate
|
||||
? dayjs(data.courtesycars_by_pk.purchasedate)
|
||||
: null,
|
||||
servicestartdate: data.courtesycars_by_pk.servicestartdate
|
||||
? dayjs(data.courtesycars_by_pk.servicestartdate)
|
||||
: null,
|
||||
serviceenddate: data.courtesycars_by_pk.serviceenddate
|
||||
? dayjs(data.courtesycars_by_pk.serviceenddate)
|
||||
: null,
|
||||
leaseenddate: data.courtesycars_by_pk.leaseenddate
|
||||
? dayjs(data.courtesycars_by_pk.leaseenddate)
|
||||
: null,
|
||||
nextservicedate: data.courtesycars_by_pk.nextservicedate
|
||||
? dayjs(data.courtesycars_by_pk.nextservicedate)
|
||||
: null,
|
||||
registrationexpires: data.courtesycars_by_pk.registrationexpires
|
||||
? dayjs(data.courtesycars_by_pk.registrationexpires)
|
||||
: null,
|
||||
insuranceexpires: data.courtesycars_by_pk.insuranceexpires
|
||||
? dayjs(data.courtesycars_by_pk.insuranceexpires)
|
||||
: null,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<CourtesyCarDetailPageComponent
|
||||
contracts={data ? data.courtesycars_by_pk.cccontracts : []}
|
||||
form={form}
|
||||
saveLoading={saveLoading}
|
||||
totalContracts={
|
||||
data
|
||||
? data.courtesycars_by_pk.cccontracts_aggregate.aggregate.count
|
||||
: 0
|
||||
}
|
||||
/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(CourtesyCarDetailPageContainer);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import CourtesyCarsListComponent from "../../components/courtesy-cars-list/courtesy-cars-list.component";
|
||||
|
||||
export default function CourtesyCarsPageComponent({ loading, data, refetch }) {
|
||||
return (
|
||||
<CourtesyCarsListComponent
|
||||
loading={loading}
|
||||
courtesycars={data}
|
||||
refetch={refetch}
|
||||
/>
|
||||
);
|
||||
export default function CourtesyCarsPageComponent({loading, data, refetch}) {
|
||||
return (
|
||||
<CourtesyCarsListComponent
|
||||
loading={loading}
|
||||
courtesycars={data}
|
||||
refetch={refetch}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,48 +1,45 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_ALL_CC } from "../../graphql/courtesy-car.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {QUERY_ALL_CC} from "../../graphql/courtesy-car.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import CourtesyCarsPageComponent from "./courtesy-cars.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function CourtesyCarsPageContainer({
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_CC, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.courtesycars");
|
||||
setSelectedHeader("courtesycarsall");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/courtesycars", label: t("titles.bc.courtesycars") },
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {loading, error, data, refetch} = useQuery(QUERY_ALL_CC, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.courtesycars");
|
||||
setSelectedHeader("courtesycarsall");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/courtesycars", label: t("titles.bc.courtesycars")},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:list">
|
||||
<CourtesyCarsPageComponent
|
||||
loading={loading}
|
||||
data={(data && data.courtesycars) || []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:list">
|
||||
<CourtesyCarsPageComponent
|
||||
loading={loading}
|
||||
data={(data && data.courtesycars) || []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(CourtesyCarsPageContainer);
|
||||
|
||||
@@ -1,178 +1,178 @@
|
||||
import { useQuery, useMutation } from "@apollo/client";
|
||||
import { Form, Layout, Typography, Button, Result } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {useMutation, useQuery} from "@apollo/client";
|
||||
import {Button, Form, Layout, Result, Typography} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useParams} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_SURVEY, COMPLETE_SURVEY } from "../../graphql/csi.queries";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import {COMPLETE_SURVEY, QUERY_SURVEY} from "../../graphql/csi.queries";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
|
||||
|
||||
export function CsiContainerPage({ currentUser }) {
|
||||
const { surveyId } = useParams();
|
||||
const [form] = Form.useForm();
|
||||
const [submitting, setSubmitting] = useState({
|
||||
loading: false,
|
||||
submitted: false,
|
||||
});
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_SURVEY, {
|
||||
variables: { surveyId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [completeSurvey] = useMutation(COMPLETE_SURVEY);
|
||||
if (loading) return <LoadingSpinner />;
|
||||
|
||||
if (error || !!!data.csi_by_pk)
|
||||
return (
|
||||
<div>
|
||||
<Result
|
||||
status="error"
|
||||
title={t("csi.errors.notfoundtitle")}
|
||||
subTitle={t("csi.errors.notfoundsubtitle")}
|
||||
>
|
||||
{error ? (
|
||||
<div>ERROR: {error.graphQLErrors.map((e) => e.message)}</div>
|
||||
) : null}
|
||||
</Result>
|
||||
</div>
|
||||
);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setSubmitting({ ...submitting, loading: true });
|
||||
|
||||
const result = await completeSurvey({
|
||||
variables: {
|
||||
surveyId,
|
||||
survey: {
|
||||
response: values,
|
||||
valid: false,
|
||||
completedon: new Date(),
|
||||
},
|
||||
},
|
||||
export function CsiContainerPage({currentUser}) {
|
||||
const {surveyId} = useParams();
|
||||
const [form] = Form.useForm();
|
||||
const [submitting, setSubmitting] = useState({
|
||||
loading: false,
|
||||
submitted: false,
|
||||
});
|
||||
|
||||
if (!!!result.errors) {
|
||||
setSubmitting({ ...submitting, loading: false, submitted: true });
|
||||
} else {
|
||||
setSubmitting({
|
||||
...submitting,
|
||||
loading: false,
|
||||
error: JSON.stringify(result.errors),
|
||||
});
|
||||
}
|
||||
};
|
||||
const {loading, error, data} = useQuery(QUERY_SURVEY, {
|
||||
variables: {surveyId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const {
|
||||
relateddata: { bodyshop, job },
|
||||
csiquestion: { config: csiquestions },
|
||||
} = data.csi_by_pk;
|
||||
const {t} = useTranslation();
|
||||
const [completeSurvey] = useMutation(COMPLETE_SURVEY);
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
|
||||
if (currentUser && currentUser.authorized)
|
||||
return (
|
||||
<Layout
|
||||
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<Result
|
||||
status="error"
|
||||
title={t("csi.labels.nologgedinuser")}
|
||||
subTitle={t("csi.labels.nologgedinuser_sub")}
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
if (error || !!!data.csi_by_pk)
|
||||
return (
|
||||
<div>
|
||||
<Result
|
||||
status="error"
|
||||
title={t("csi.errors.notfoundtitle")}
|
||||
subTitle={t("csi.errors.notfoundsubtitle")}
|
||||
>
|
||||
{error ? (
|
||||
<div>ERROR: {error.graphQLErrors.map((e) => e.message)}</div>
|
||||
) : null}
|
||||
</Result>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout
|
||||
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center", margin: "2em" }}>
|
||||
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
||||
<img src={bodyshop.logo_img_path.src} alt="Logo" />
|
||||
) : null}
|
||||
<div style={{ margin: "2em" }}>
|
||||
<strong>{bodyshop.shopname || ""}</strong>
|
||||
<div>{`${bodyshop.address1 || ""}`}</div>
|
||||
<div>{`${bodyshop.address2 || ""}`}</div>
|
||||
<div>{`${bodyshop.city || ""} ${bodyshop.state || ""} ${
|
||||
bodyshop.zip_post || ""
|
||||
}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
||||
<strong>{`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`}</strong>
|
||||
<Typography.Paragraph>
|
||||
{`At ${
|
||||
bodyshop.shopname || ""
|
||||
}, we value your feedback. We would love to
|
||||
hear what you have to say. Please fill out the form below.`}
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
const handleFinish = async (values) => {
|
||||
setSubmitting({...submitting, loading: true});
|
||||
|
||||
{submitting.error ? (
|
||||
<AlertComponent message={submitting.error} type="error" />
|
||||
) : null}
|
||||
const result = await completeSurvey({
|
||||
variables: {
|
||||
surveyId,
|
||||
survey: {
|
||||
response: values,
|
||||
valid: false,
|
||||
completedon: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
{submitting.submitted ? (
|
||||
<Layout.Content
|
||||
style={{
|
||||
backgroundColor: "#fff",
|
||||
margin: "2em 4em",
|
||||
padding: "2em",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<Result
|
||||
status="success"
|
||||
title={t("csi.successes.submitted")}
|
||||
subTitle={t("csi.successes.submittedsub")}
|
||||
/>
|
||||
</Layout.Content>
|
||||
) : (
|
||||
<Layout.Content
|
||||
style={{
|
||||
backgroundColor: "#fff",
|
||||
margin: "2em 4em",
|
||||
padding: "2em",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<Form form={form} onFinish={handleFinish}>
|
||||
<ConfigFormComponents componentList={csiquestions} />
|
||||
<Button
|
||||
loading={submitting.loading}
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
if (!!!result.errors) {
|
||||
setSubmitting({...submitting, loading: false, submitted: true});
|
||||
} else {
|
||||
setSubmitting({
|
||||
...submitting,
|
||||
loading: false,
|
||||
error: JSON.stringify(result.errors),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
relateddata: {bodyshop, job},
|
||||
csiquestion: {config: csiquestions},
|
||||
} = data.csi_by_pk;
|
||||
|
||||
if (currentUser && currentUser.authorized)
|
||||
return (
|
||||
<Layout
|
||||
style={{height: "100vh", display: "flex", flexDirection: "column"}}
|
||||
>
|
||||
{t("general.actions.submit")}
|
||||
</Button>
|
||||
</Form>
|
||||
</Layout.Content>
|
||||
)}
|
||||
<Result
|
||||
status="error"
|
||||
title={t("csi.labels.nologgedinuser")}
|
||||
subTitle={t("csi.labels.nologgedinuser_sub")}
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
<Layout.Footer>
|
||||
{`Survey ID: ${surveyId}`}
|
||||
</Layout.Footer>
|
||||
</Layout>
|
||||
);
|
||||
return (
|
||||
<Layout
|
||||
style={{height: "100vh", display: "flex", flexDirection: "column"}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{display: "flex", alignItems: "center", margin: "2em"}}>
|
||||
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
||||
<img src={bodyshop.logo_img_path.src} alt="Logo"/>
|
||||
) : null}
|
||||
<div style={{margin: "2em"}}>
|
||||
<strong>{bodyshop.shopname || ""}</strong>
|
||||
<div>{`${bodyshop.address1 || ""}`}</div>
|
||||
<div>{`${bodyshop.address2 || ""}`}</div>
|
||||
<div>{`${bodyshop.city || ""} ${bodyshop.state || ""} ${
|
||||
bodyshop.zip_post || ""
|
||||
}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
||||
<strong>{`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`}</strong>
|
||||
<Typography.Paragraph>
|
||||
{`At ${
|
||||
bodyshop.shopname || ""
|
||||
}, we value your feedback. We would love to
|
||||
hear what you have to say. Please fill out the form below.`}
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
|
||||
{submitting.error ? (
|
||||
<AlertComponent message={submitting.error} type="error"/>
|
||||
) : null}
|
||||
|
||||
{submitting.submitted ? (
|
||||
<Layout.Content
|
||||
style={{
|
||||
backgroundColor: "#fff",
|
||||
margin: "2em 4em",
|
||||
padding: "2em",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<Result
|
||||
status="success"
|
||||
title={t("csi.successes.submitted")}
|
||||
subTitle={t("csi.successes.submittedsub")}
|
||||
/>
|
||||
</Layout.Content>
|
||||
) : (
|
||||
<Layout.Content
|
||||
style={{
|
||||
backgroundColor: "#fff",
|
||||
margin: "2em 4em",
|
||||
padding: "2em",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<Form form={form} onFinish={handleFinish}>
|
||||
<ConfigFormComponents componentList={csiquestions}/>
|
||||
<Button
|
||||
loading={submitting.loading}
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
>
|
||||
{t("general.actions.submit")}
|
||||
</Button>
|
||||
</Form>
|
||||
</Layout.Content>
|
||||
)}
|
||||
|
||||
<Layout.Footer>
|
||||
{`Survey ID: ${surveyId}`}
|
||||
</Layout.Footer>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import DashboardGridComponent from "../../components/dashboard-grid/dashboard-grid.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ExportsLogPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function ExportsLogPageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.dashboard");
|
||||
setSelectedHeader("dashboard");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/exportlogs",
|
||||
label: t("titles.bc.dashboard"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.dashboard");
|
||||
setSelectedHeader("dashboard");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/exportlogs",
|
||||
label: t("titles.bc.dashboard"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<FeatureWrapper featureName="dashboard">
|
||||
<RbacWrapper action="shop:dashboard">
|
||||
<DashboardGridComponent />
|
||||
</RbacWrapper>
|
||||
</FeatureWrapper>
|
||||
);
|
||||
return (
|
||||
<FeatureWrapper featureName="dashboard">
|
||||
<RbacWrapper action="shop:dashboard">
|
||||
<DashboardGridComponent/>
|
||||
</RbacWrapper>
|
||||
</FeatureWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ExportsLogPageContainer);
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import React from "react";
|
||||
import { Typography } from "antd";
|
||||
import {Typography} from "antd";
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div style={{ textAlign: "center", margin: "1rem 0rem" }}>
|
||||
<Typography.Title
|
||||
level={2}
|
||||
>{`Rome Online V.${process.env.NODE_ENV}-${process.env.REACT_APP_GIT_SHA}`}</Typography.Title>
|
||||
<Typography.Title level={4}>
|
||||
© 2019 - {new Date().getFullYear()} ImEX Systems Inc. used under
|
||||
license to Rome Technologies
|
||||
</Typography.Title>
|
||||
<Typography.Title level={2}>Third Party Notices</Typography.Title>
|
||||
<a href="/3rdparty-app.txt">
|
||||
<Typography.Title level={4}>Application</Typography.Title>
|
||||
</a>
|
||||
<a href="/3rdparty-api.txt">
|
||||
<Typography.Title level={4}>API</Typography.Title>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div style={{textAlign: "center", margin: "1rem 0rem"}}>
|
||||
<Typography.Title
|
||||
level={2}
|
||||
>{`Rome Online V.${process.env.NODE_ENV}-${process.env.REACT_APP_GIT_SHA}`}</Typography.Title>
|
||||
<Typography.Title level={4}>
|
||||
© 2019 - {new Date().getFullYear()} ImEX Systems Inc. used under
|
||||
license to Rome Technologies
|
||||
</Typography.Title>
|
||||
<Typography.Title level={2}>Third Party Notices</Typography.Title>
|
||||
<a href="/3rdparty-app.txt">
|
||||
<Typography.Title level={4}>Application</Typography.Title>
|
||||
</a>
|
||||
<a href="/3rdparty-api.txt">
|
||||
<Typography.Title level={4}>API</Typography.Title>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,163 +1,161 @@
|
||||
import { Button, Card, Col, notification, Row, Select, Space } from "antd";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {Button, Card, Col, notification, Row, Select, Space} from "antd";
|
||||
import React, {useEffect, useRef, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import SocketIO from "socket.io-client";
|
||||
import DmsAllocationsSummaryApComponent from "../../components/dms-allocations-summary-ap/dms-allocations-summary-ap.component";
|
||||
import DmsAllocationsSummaryApComponent
|
||||
from "../../components/dms-allocations-summary-ap/dms-allocations-summary-ap.component";
|
||||
import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {auth} from "../../firebase/firebase.utils";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
||||
|
||||
export const socket = SocketIO(
|
||||
process.env.NODE_ENV === "production"
|
||||
? process.env.REACT_APP_AXIOS_BASE_API_URL
|
||||
: window.location.origin,
|
||||
{
|
||||
path: "/ws",
|
||||
withCredentials: true,
|
||||
auth: async (callback) => {
|
||||
const token = auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
callback({ token });
|
||||
},
|
||||
}
|
||||
process.env.NODE_ENV === "production"
|
||||
? process.env.REACT_APP_AXIOS_BASE_API_URL
|
||||
: window.location.origin,
|
||||
{
|
||||
path: "/ws",
|
||||
withCredentials: true,
|
||||
auth: async (callback) => {
|
||||
const token = auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
callback({token});
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
const [logLevel, setLogLevel] = useState("DEBUG");
|
||||
const history = useHistory();
|
||||
const [logs, setLogs] = useState([]);
|
||||
export function DmsContainer({bodyshop, setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
const [logLevel, setLogLevel] = useState("DEBUG");
|
||||
const history = useNavigate();
|
||||
const [logs, setLogs] = useState([]);
|
||||
|
||||
const { state } = useLocation();
|
||||
const {state} = useLocation();
|
||||
|
||||
const logsRef = useRef(null);
|
||||
const logsRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.dms");
|
||||
setSelectedHeader("dms");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/payables",
|
||||
label: t("titles.bc.accounting-payables"),
|
||||
},
|
||||
{
|
||||
link: "/manage/dms",
|
||||
label: t("titles.bc.dms"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.dms");
|
||||
setSelectedHeader("dms");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/payables",
|
||||
label: t("titles.bc.accounting-payables"),
|
||||
},
|
||||
{
|
||||
link: "/manage/dms",
|
||||
label: t("titles.bc.dms"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
useEffect(() => {
|
||||
socket.on("connect", () => socket.emit("set-log-level", logLevel));
|
||||
socket.on("reconnect", () => {
|
||||
setLogs((logs) => {
|
||||
return [
|
||||
...logs,
|
||||
{
|
||||
timestamp: new Date(),
|
||||
level: "WARNING",
|
||||
message: "Reconnected to CDK Export Service",
|
||||
},
|
||||
];
|
||||
});
|
||||
});
|
||||
useEffect(() => {
|
||||
socket.on("connect", () => socket.emit("set-log-level", logLevel));
|
||||
socket.on("reconnect", () => {
|
||||
setLogs((logs) => {
|
||||
return [
|
||||
...logs,
|
||||
{
|
||||
timestamp: new Date(),
|
||||
level: "WARNING",
|
||||
message: "Reconnected to CDK Export Service",
|
||||
},
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("log-event", (payload) => {
|
||||
setLogs((logs) => {
|
||||
return [...logs, payload];
|
||||
});
|
||||
});
|
||||
socket.on("log-event", (payload) => {
|
||||
setLogs((logs) => {
|
||||
return [...logs, payload];
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("ap-export-complete", (payload) => {
|
||||
notification.open({
|
||||
type: "success",
|
||||
message: t("jobs.labels.dms.apexported"),
|
||||
});
|
||||
});
|
||||
socket.on("ap-export-complete", (payload) => {
|
||||
notification.open({
|
||||
type: "success",
|
||||
message: t("jobs.labels.dms.apexported"),
|
||||
});
|
||||
});
|
||||
|
||||
if (socket.disconnected) socket.connect();
|
||||
return () => {
|
||||
socket.removeAllListeners();
|
||||
socket.disconnect();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
if (socket.disconnected) socket.connect();
|
||||
return () => {
|
||||
socket.removeAllListeners();
|
||||
socket.disconnect();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!state?.billids) {
|
||||
history.push(`/manage/accounting/payables`);
|
||||
}
|
||||
if (!state?.billids) {
|
||||
history(`/manage/accounting/payables`);
|
||||
}
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col md={24} lg={12}>
|
||||
<DmsAllocationsSummaryApComponent
|
||||
socket={socket}
|
||||
billids={state?.billids}
|
||||
/>
|
||||
</Col>
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col md={24} lg={12}>
|
||||
<DmsAllocationsSummaryApComponent
|
||||
socket={socket}
|
||||
billids={state?.billids}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col md={24} lg={12}>
|
||||
<div ref={logsRef}>
|
||||
<Card
|
||||
title={t("jobs.labels.dms.logs")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Select
|
||||
placeholder="Log Level"
|
||||
value={logLevel}
|
||||
onChange={(value) => {
|
||||
setLogLevel(value);
|
||||
socket.emit("set-log-level", value);
|
||||
}}
|
||||
>
|
||||
<Select.Option key="TRACE">TRACE</Select.Option>
|
||||
<Select.Option key="DEBUG">DEBUG</Select.Option>
|
||||
<Select.Option key="INFO">INFO</Select.Option>
|
||||
<Select.Option key="WARNING">WARNING</Select.Option>
|
||||
<Select.Option key="ERROR">ERROR</Select.Option>
|
||||
</Select>
|
||||
<Button onClick={() => setLogs([])}>Clear Logs</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setLogs([]);
|
||||
<Col md={24} lg={12}>
|
||||
<div ref={logsRef}>
|
||||
<Card
|
||||
title={t("jobs.labels.dms.logs")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Select
|
||||
placeholder="Log Level"
|
||||
value={logLevel}
|
||||
onChange={(value) => {
|
||||
setLogLevel(value);
|
||||
socket.emit("set-log-level", value);
|
||||
}}
|
||||
>
|
||||
<Select.Option key="TRACE">TRACE</Select.Option>
|
||||
<Select.Option key="DEBUG">DEBUG</Select.Option>
|
||||
<Select.Option key="INFO">INFO</Select.Option>
|
||||
<Select.Option key="WARNING">WARNING</Select.Option>
|
||||
<Select.Option key="ERROR">ERROR</Select.Option>
|
||||
</Select>
|
||||
<Button onClick={() => setLogs([])}>Clear Logs</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setLogs([]);
|
||||
|
||||
socket.disconnect();
|
||||
socket.connect();
|
||||
}}
|
||||
>
|
||||
Reconnect
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<DmsLogEvents socket={socket} logs={logs} />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
socket.disconnect();
|
||||
socket.connect();
|
||||
}}
|
||||
>
|
||||
Reconnect
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<DmsLogEvents socket={socket} logs={logs}/>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export const determineDmsType = (bodyshop) => {
|
||||
if (bodyshop.cdk_dealerid) return "cdk";
|
||||
else {
|
||||
return "pbs";
|
||||
}
|
||||
if (bodyshop.cdk_dealerid) return "cdk";
|
||||
else {
|
||||
return "pbs";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
notification,
|
||||
Result,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
} from "antd";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Button, Card, Col, notification, Result, Row, Select, Space,} from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect, useRef, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import SocketIO from "socket.io-client";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import DmsAllocationsSummary from "../../components/dms-allocations-summary/dms-allocations-summary.component";
|
||||
@@ -22,200 +13,197 @@ import DmsCustomerSelector from "../../components/dms-customer-selector/dms-cust
|
||||
import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component";
|
||||
import DmsPostForm from "../../components/dms-post-form/dms-post-form.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {OwnerNameDisplayFunction} from "../../components/owner-name-display/owner-name-display.component";
|
||||
import {auth} from "../../firebase/firebase.utils";
|
||||
import {QUERY_JOB_EXPORT_DMS} from "../../graphql/jobs.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
|
||||
|
||||
export const socket = SocketIO(
|
||||
// process.env.NODE_ENV === "production"
|
||||
// ? process.env.REACT_APP_AXIOS_BASE_API_URL
|
||||
// : window.location.origin,
|
||||
"http://localhost:4000", // for dev testing,
|
||||
{
|
||||
path: "/ws",
|
||||
withCredentials: true,
|
||||
auth: async (callback) => {
|
||||
const token = auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
callback({ token });
|
||||
},
|
||||
}
|
||||
// process.env.NODE_ENV === "production"
|
||||
// ? process.env.REACT_APP_AXIOS_BASE_API_URL
|
||||
// : window.location.origin,
|
||||
"http://localhost:4000", // for dev testing,
|
||||
{
|
||||
path: "/ws",
|
||||
withCredentials: true,
|
||||
auth: async (callback) => {
|
||||
const token = auth.currentUser && (await auth.currentUser.getIdToken());
|
||||
callback({token});
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
const [logLevel, setLogLevel] = useState("DEBUG");
|
||||
const history = useHistory();
|
||||
const [logs, setLogs] = useState([]);
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const { jobId } = search;
|
||||
export function DmsContainer({bodyshop, setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
const [logLevel, setLogLevel] = useState("DEBUG");
|
||||
const history = useNavigate();
|
||||
const [logs, setLogs] = useState([]);
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const {jobId} = search;
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_JOB_EXPORT_DMS, {
|
||||
variables: { id: jobId },
|
||||
skip: !jobId,
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const logsRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.dms");
|
||||
setSelectedHeader("dms");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/receivables",
|
||||
label: t("titles.bc.accounting-receivables"),
|
||||
},
|
||||
{
|
||||
link: "/manage/dms",
|
||||
label: t("titles.bc.dms"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
useEffect(() => {
|
||||
socket.on("connect", () => socket.emit("set-log-level", logLevel));
|
||||
socket.on("reconnect", () => {
|
||||
setLogs((logs) => {
|
||||
return [
|
||||
...logs,
|
||||
{
|
||||
timestamp: new Date(),
|
||||
level: "WARNING",
|
||||
message: "Reconnected to CDK Export Service",
|
||||
},
|
||||
];
|
||||
});
|
||||
});
|
||||
socket.on("connect_error", (err) => {
|
||||
console.log(`connect_error due to ${err}`, err);
|
||||
notification.error({ message: err.message });
|
||||
});
|
||||
socket.on("log-event", (payload) => {
|
||||
setLogs((logs) => {
|
||||
return [...logs, payload];
|
||||
});
|
||||
});
|
||||
socket.on("export-success", (payload) => {
|
||||
notification.success({
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
history.push("/manage/accounting/receivables");
|
||||
const {loading, error, data} = useQuery(QUERY_JOB_EXPORT_DMS, {
|
||||
variables: {id: jobId},
|
||||
skip: !jobId,
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const logsRef = useRef(null);
|
||||
|
||||
if (socket.disconnected) socket.connect();
|
||||
return () => {
|
||||
socket.removeAllListeners();
|
||||
socket.disconnect();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.dms");
|
||||
setSelectedHeader("dms");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/receivables",
|
||||
label: t("titles.bc.accounting-receivables"),
|
||||
},
|
||||
{
|
||||
link: "/manage/dms",
|
||||
label: t("titles.bc.dms"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
useEffect(() => {
|
||||
socket.on("connect", () => socket.emit("set-log-level", logLevel));
|
||||
socket.on("reconnect", () => {
|
||||
setLogs((logs) => {
|
||||
return [
|
||||
...logs,
|
||||
{
|
||||
timestamp: new Date(),
|
||||
level: "WARNING",
|
||||
message: "Reconnected to CDK Export Service",
|
||||
},
|
||||
];
|
||||
});
|
||||
});
|
||||
socket.on("connect_error", (err) => {
|
||||
console.log(`connect_error due to ${err}`, err);
|
||||
notification.error({message: err.message});
|
||||
});
|
||||
socket.on("log-event", (payload) => {
|
||||
setLogs((logs) => {
|
||||
return [...logs, payload];
|
||||
});
|
||||
});
|
||||
socket.on("export-success", (payload) => {
|
||||
notification.success({
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
history("/manage/accounting/receivables");
|
||||
});
|
||||
|
||||
if (
|
||||
!jobId ||
|
||||
!(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) ||
|
||||
!(data && data.jobs_by_pk)
|
||||
)
|
||||
return <Result status="404" title={t("general.errors.notfound")} />;
|
||||
if (socket.disconnected) socket.connect();
|
||||
return () => {
|
||||
socket.removeAllListeners();
|
||||
socket.disconnect();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (data.jobs_by_pk && data.jobs_by_pk.date_exported)
|
||||
return <Result status="warning" title={t("dms.errors.alreadyexported")} />;
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col md={24} lg={10}>
|
||||
<DmsAllocationsSummary
|
||||
title={
|
||||
<span>
|
||||
if (
|
||||
!jobId ||
|
||||
!(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) ||
|
||||
!(data && data.jobs_by_pk)
|
||||
)
|
||||
return <Result status="404" title={t("general.errors.notfound")}/>;
|
||||
|
||||
if (data.jobs_by_pk && data.jobs_by_pk.date_exported)
|
||||
return <Result status="warning" title={t("dms.errors.alreadyexported")}/>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col md={24} lg={10}>
|
||||
<DmsAllocationsSummary
|
||||
title={
|
||||
<span>
|
||||
<Link to={`/manage/jobs/${data && data.jobs_by_pk.id}`}>{`${
|
||||
data && data.jobs_by_pk && data.jobs_by_pk.ro_number
|
||||
data && data.jobs_by_pk && data.jobs_by_pk.ro_number
|
||||
}`}</Link>
|
||||
{` | ${OwnerNameDisplayFunction(data.jobs_by_pk)} | ${
|
||||
data.jobs_by_pk.v_model_yr || ""
|
||||
} ${data.jobs_by_pk.v_make_desc || ""} ${
|
||||
data.jobs_by_pk.v_model_desc || ""
|
||||
}`}
|
||||
{` | ${OwnerNameDisplayFunction(data.jobs_by_pk)} | ${
|
||||
data.jobs_by_pk.v_model_yr || ""
|
||||
} ${data.jobs_by_pk.v_make_desc || ""} ${
|
||||
data.jobs_by_pk.v_model_desc || ""
|
||||
}`}
|
||||
</span>
|
||||
}
|
||||
socket={socket}
|
||||
jobId={jobId}
|
||||
/>
|
||||
</Col>
|
||||
<Col md={24} lg={14}>
|
||||
<DmsPostForm
|
||||
socket={socket}
|
||||
jobId={jobId}
|
||||
job={data && data.jobs_by_pk}
|
||||
logsRef={logsRef}
|
||||
/>
|
||||
</Col>
|
||||
}
|
||||
socket={socket}
|
||||
jobId={jobId}
|
||||
/>
|
||||
</Col>
|
||||
<Col md={24} lg={14}>
|
||||
<DmsPostForm
|
||||
socket={socket}
|
||||
jobId={jobId}
|
||||
job={data && data.jobs_by_pk}
|
||||
logsRef={logsRef}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<DmsCustomerSelector />
|
||||
<DmsCustomerSelector/>
|
||||
|
||||
<Col span={24}>
|
||||
<div ref={logsRef}>
|
||||
<Card
|
||||
title={t("jobs.labels.dms.logs")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Select
|
||||
placeholder="Log Level"
|
||||
value={logLevel}
|
||||
onChange={(value) => {
|
||||
setLogLevel(value);
|
||||
socket.emit("set-log-level", value);
|
||||
}}
|
||||
>
|
||||
<Select.Option key="TRACE">TRACE</Select.Option>
|
||||
<Select.Option key="DEBUG">DEBUG</Select.Option>
|
||||
<Select.Option key="INFO">INFO</Select.Option>
|
||||
<Select.Option key="WARNING">WARNING</Select.Option>
|
||||
<Select.Option key="ERROR">ERROR</Select.Option>
|
||||
</Select>
|
||||
<Button onClick={() => setLogs([])}>Clear Logs</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setLogs([]);
|
||||
socket.disconnect();
|
||||
socket.connect();
|
||||
}}
|
||||
>
|
||||
Reconnect
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<DmsLogEvents socket={socket} logs={logs} />
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
<Col span={24}>
|
||||
<div ref={logsRef}>
|
||||
<Card
|
||||
title={t("jobs.labels.dms.logs")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Select
|
||||
placeholder="Log Level"
|
||||
value={logLevel}
|
||||
onChange={(value) => {
|
||||
setLogLevel(value);
|
||||
socket.emit("set-log-level", value);
|
||||
}}
|
||||
>
|
||||
<Select.Option key="TRACE">TRACE</Select.Option>
|
||||
<Select.Option key="DEBUG">DEBUG</Select.Option>
|
||||
<Select.Option key="INFO">INFO</Select.Option>
|
||||
<Select.Option key="WARNING">WARNING</Select.Option>
|
||||
<Select.Option key="ERROR">ERROR</Select.Option>
|
||||
</Select>
|
||||
<Button onClick={() => setLogs([])}>Clear Logs</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setLogs([]);
|
||||
socket.disconnect();
|
||||
socket.connect();
|
||||
}}
|
||||
>
|
||||
Reconnect
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<DmsLogEvents socket={socket} logs={logs}/>
|
||||
</Card>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const determineDmsType = (bodyshop) => {
|
||||
if (bodyshop.cdk_dealerid) return "cdk";
|
||||
else {
|
||||
return "pbs";
|
||||
}
|
||||
if (bodyshop.cdk_dealerid) return "cdk";
|
||||
else {
|
||||
return "pbs";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,197 +1,197 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
|
||||
import {SyncOutlined} from "@ant-design/icons";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Button, Card, Checkbox, Input, Space, Table, Typography} from "antd";
|
||||
import _ from "lodash";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import {QUERY_EXPORT_LOG_PAGINATED} from "../../graphql/accounting.queries";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {DateTimeFormatter} from "../../utils/DateFormatter";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function ExportLogsPageComponent({ bodyshop }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder, search } = searchParams;
|
||||
const history = useHistory();
|
||||
export function ExportLogsPageComponent({bodyshop}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder, search} = searchParams;
|
||||
const history = useNavigate();
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_EXPORT_LOG_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
search: search || "",
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "created_at"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_EXPORT_LOG_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
search: search || "",
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "created_at"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {t} = useTranslation();
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
searchParams.page = pagination.current;
|
||||
searchParams.sortcolumn = sorter.columnKey;
|
||||
searchParams.sortorder = sorter.order;
|
||||
if (filters.status) {
|
||||
searchParams.statusFilters = JSON.stringify(
|
||||
_.flattenDeep(filters.status)
|
||||
);
|
||||
} else {
|
||||
delete searchParams.statusFilters;
|
||||
}
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
};
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
searchParams.page = pagination.current;
|
||||
searchParams.sortcolumn = sorter.columnKey;
|
||||
searchParams.sortorder = sorter.order;
|
||||
if (filters.status) {
|
||||
searchParams.statusFilters = JSON.stringify(
|
||||
_.flattenDeep(filters.status)
|
||||
);
|
||||
} else {
|
||||
delete searchParams.statusFilters;
|
||||
}
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("general.labels.created_at"),
|
||||
dataIndex: "created_at",
|
||||
key: "created_at",
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.user_email"),
|
||||
dataIndex: "useremail",
|
||||
key: "useremail",
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
const columns = [
|
||||
{
|
||||
title: t("general.labels.created_at"),
|
||||
dataIndex: "created_at",
|
||||
key: "created_at",
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("employees.fields.user_email"),
|
||||
dataIndex: "useremail",
|
||||
key: "useremail",
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
|
||||
render: (text, record) =>
|
||||
record.job && (
|
||||
<Link to={`/manage/jobs/${record.job.id}`}>
|
||||
{(record.job && record.job.ro_number) || t("general.labels.na")}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: "invoice_number",
|
||||
key: "invoice_number",
|
||||
render: (text, record) =>
|
||||
record.bill && (
|
||||
<Link to={"/manage/bills?billid=" + (record.bill && record.bill.id)}>
|
||||
{record.bill && record.bill.invoice_number}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.paymentnum"),
|
||||
dataIndex: "paymentnum",
|
||||
key: "paymentnum",
|
||||
render: (text, record) =>
|
||||
record.payment && (
|
||||
<Link
|
||||
to={
|
||||
"/manage/payments?search=" +
|
||||
(record.payment && record.payment.paymentnum)
|
||||
render: (text, record) =>
|
||||
record.job && (
|
||||
<Link to={`/manage/jobs/${record.job.id}`}>
|
||||
{(record.job && record.job.ro_number) || t("general.labels.na")}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.invoice_number"),
|
||||
dataIndex: "invoice_number",
|
||||
key: "invoice_number",
|
||||
render: (text, record) =>
|
||||
record.bill && (
|
||||
<Link to={"/manage/bills?billid=" + (record.bill && record.bill.id)}>
|
||||
{record.bill && record.bill.invoice_number}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("payments.fields.paymentnum"),
|
||||
dataIndex: "paymentnum",
|
||||
key: "paymentnum",
|
||||
render: (text, record) =>
|
||||
record.payment && (
|
||||
<Link
|
||||
to={
|
||||
"/manage/payments?search=" +
|
||||
(record.payment && record.payment.paymentnum)
|
||||
}
|
||||
>
|
||||
{record.payment && record.payment.paymentnum}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("general.labels.successful"),
|
||||
dataIndex: "successful",
|
||||
key: "successful",
|
||||
render: (text, record) => (
|
||||
<Checkbox disabled checked={record.successful}/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("general.labels.message"),
|
||||
dataIndex: "message",
|
||||
key: "message",
|
||||
render: (text, record) =>
|
||||
record.message && (
|
||||
<div>
|
||||
<ul>
|
||||
{JSON.parse(record.message).map((m, idx) => (
|
||||
<li key={idx}>{m}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
{searchParams.search && (
|
||||
<>
|
||||
<Typography.Title level={4}>
|
||||
{t("general.labels.searchresults", {
|
||||
search: searchParams.search,
|
||||
})}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
onClick={() => {
|
||||
delete searchParams.search;
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
}}
|
||||
>
|
||||
{t("general.actions.clear")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={searchParams.search || t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
searchParams.search = value;
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{record.payment && record.payment.paymentnum}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("general.labels.successful"),
|
||||
dataIndex: "successful",
|
||||
key: "successful",
|
||||
render: (text, record) => (
|
||||
<Checkbox disabled checked={record.successful} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("general.labels.message"),
|
||||
dataIndex: "message",
|
||||
key: "message",
|
||||
render: (text, record) =>
|
||||
record.message && (
|
||||
<div>
|
||||
<ul>
|
||||
{JSON.parse(record.message).map((m, idx) => (
|
||||
<li key={idx}>{m}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
{searchParams.search && (
|
||||
<>
|
||||
<Typography.Title level={4}>
|
||||
{t("general.labels.searchresults", {
|
||||
search: searchParams.search,
|
||||
})}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
onClick={() => {
|
||||
delete searchParams.search;
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: data && data.search_exportlog_aggregate.aggregate.count,
|
||||
}}
|
||||
>
|
||||
{t("general.actions.clear")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={searchParams.search || t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
searchParams.search = value;
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: data && data.search_exportlog_aggregate.aggregate.count,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={data && data.search_exportlog}
|
||||
style={{ height: "100%" }}
|
||||
scroll={{ x: true }}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={data && data.search_exportlog}
|
||||
style={{height: "100%"}}
|
||||
scroll={{x: true}}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(ExportLogsPageComponent);
|
||||
|
||||
@@ -1,36 +1,34 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import ExportLogsPage from "./export-logs.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ExportsLogPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function ExportsLogPageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.export-logs");
|
||||
setSelectedHeader("export-logs");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/exportlogs",
|
||||
label: t("titles.bc.export-logs"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.export-logs");
|
||||
setSelectedHeader("export-logs");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/accounting/exportlogs",
|
||||
label: t("titles.bc.export-logs"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="accounting:exportlogs">
|
||||
<ExportLogsPage />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="accounting:exportlogs">
|
||||
<ExportLogsPage/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ExportsLogPageContainer);
|
||||
|
||||
@@ -2,9 +2,9 @@ import React from "react";
|
||||
import HelpRescue from "../../components/help-rescue/help-rescue.component";
|
||||
|
||||
export default function HelpPage() {
|
||||
return (
|
||||
<div>
|
||||
<HelpRescue />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<HelpRescue/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import InventoryList from "../../components/inventory-list/inventory-list.container";
|
||||
import InventoryUpsertModalContainer from "../../components/inventory-upsert-modal/inventory-upsert-modal.container";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function InventoryPage({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function InventoryPage({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.inventory");
|
||||
setSelectedHeader("inventory");
|
||||
setBreadcrumbs([{ link: "/manage/jobs", label: t("titles.bc.inventory") }]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.inventory");
|
||||
setSelectedHeader("inventory");
|
||||
setBreadcrumbs([{link: "/manage/jobs", label: t("titles.bc.inventory")}]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="inventory:list">
|
||||
<InventoryUpsertModalContainer />
|
||||
<InventoryList />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="inventory:list">
|
||||
<InventoryUpsertModalContainer/>
|
||||
<InventoryList/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(InventoryPage);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Card, Col, Result, Row, Space, Typography } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Card, Col, Result, Row, Space, Typography} from "antd";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobCalculateTotals from "../../components/job-calculate-totals/job-calculate-totals.component";
|
||||
import ScoreboardAddButton from "../../components/job-scoreboard-add-button/job-scoreboard-add-button.component";
|
||||
@@ -11,125 +11,125 @@ import JobsAdminClass from "../../components/jobs-admin-class/jobs-admin-class.c
|
||||
import JobsAdminDatesChange from "../../components/jobs-admin-dates/jobs-admin-dates.component";
|
||||
import JobsAdminDeleteIntake from "../../components/jobs-admin-delete-intake/jobs-admin-delete-intake.component";
|
||||
import JobsAdminMarkReexport from "../../components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component";
|
||||
import JobAdminOwnerReassociate from "../../components/jobs-admin-owner-reassociate/jobs-admin-owner-reassociate.component";
|
||||
import JobAdminOwnerReassociate
|
||||
from "../../components/jobs-admin-owner-reassociate/jobs-admin-owner-reassociate.component";
|
||||
import JobsAdminUnvoid from "../../components/jobs-admin-unvoid/jobs-admin-unvoid.component";
|
||||
import JobAdminVehicleReassociate from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
|
||||
import JobAdminVehicleReassociate
|
||||
from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
|
||||
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {GET_JOB_BY_PK} from "../../graphql/jobs.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
const colSpan = {
|
||||
sm: { span: 24 },
|
||||
md: { span: 12 },
|
||||
lg: { span: 8 },
|
||||
xl: { span: 6 },
|
||||
sm: {span: 24},
|
||||
md: {span: 12},
|
||||
lg: {span: 8},
|
||||
xl: {span: 6},
|
||||
};
|
||||
|
||||
const cardStyle = {
|
||||
height: "100%",
|
||||
height: "100%",
|
||||
};
|
||||
|
||||
export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { jobId } = useParams();
|
||||
const { loading, error, data } = useQuery(GET_JOB_BY_PK, {
|
||||
variables: { id: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
setSelectedHeader("activejobs");
|
||||
document.title = t("titles.jobs-admin", {
|
||||
ro_number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
export function JobsCloseContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {jobId} = useParams();
|
||||
const {loading, error, data} = useQuery(GET_JOB_BY_PK, {
|
||||
variables: {id: jobId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
setSelectedHeader("activejobs");
|
||||
document.title = t("titles.jobs-admin", {
|
||||
ro_number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
});
|
||||
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: `/manage/jobs/`,
|
||||
label: t("titles.bc.jobs"),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/admin`,
|
||||
label: t("titles.bc.jobs-admin"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, jobId, data, setSelectedHeader]);
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: `/manage/jobs/`,
|
||||
label: t("titles.bc.jobs"),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/admin`,
|
||||
label: t("titles.bc.jobs-admin"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, jobId, data, setSelectedHeader]);
|
||||
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
if (!!!data.jobs_by_pk) return <NotFound/>;
|
||||
if (!data.jobs_by_pk.job_totals)
|
||||
return (
|
||||
<Result
|
||||
title={t("jobs.errors.nofinancial")}
|
||||
extra={<JobCalculateTotals job={data.jobs_by_pk}/>}
|
||||
/>
|
||||
);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (!!!data.jobs_by_pk) return <NotFound />;
|
||||
if (!data.jobs_by_pk.job_totals)
|
||||
return (
|
||||
<Result
|
||||
title={t("jobs.errors.nofinancial")}
|
||||
extra={<JobCalculateTotals job={data.jobs_by_pk} />}
|
||||
/>
|
||||
<RbacWrapper action="jobs:admin">
|
||||
<Typography.Title level={4} style={{color: "tomato"}}>
|
||||
{t("jobs.labels.adminwarning")}
|
||||
</Typography.Title>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<Space wrap>
|
||||
<ScoreboardAddButton
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
disabled={
|
||||
(data && data.jobs_by_pk.voided) ||
|
||||
(data.jobs_by_pk && !data.jobs_by_pk.converted)
|
||||
}
|
||||
/>
|
||||
<JobsAdminDeleteIntake job={data ? data.jobs_by_pk : {}}/>
|
||||
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}}/>
|
||||
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}}/>
|
||||
<JobsAdminStatus job={data ? data.jobs_by_pk : {}}/>
|
||||
</Space>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<JobsAdminClass job={data ? data.jobs_by_pk : {}}/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<JobAdminOwnerReassociate job={data ? data.jobs_by_pk : {}}/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<JobAdminVehicleReassociate job={data ? data.jobs_by_pk : {}}/>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<Card style={cardStyle}>
|
||||
<JobsAdminDatesChange job={data ? data.jobs_by_pk : {}}/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:admin">
|
||||
<Typography.Title level={4} style={{ color: "tomato" }}>
|
||||
{t("jobs.labels.adminwarning")}
|
||||
</Typography.Title>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<Space wrap>
|
||||
<ScoreboardAddButton
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
disabled={
|
||||
(data && data.jobs_by_pk.voided) ||
|
||||
(data.jobs_by_pk && !data.jobs_by_pk.converted)
|
||||
}
|
||||
/>
|
||||
<JobsAdminDeleteIntake job={data ? data.jobs_by_pk : {}} />
|
||||
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}} />
|
||||
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}} />
|
||||
<JobsAdminStatus job={data ? data.jobs_by_pk : {}} />
|
||||
</Space>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<JobsAdminClass job={data ? data.jobs_by_pk : {}} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<JobAdminOwnerReassociate job={data ? data.jobs_by_pk : {}} />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<Card style={cardStyle}>
|
||||
<JobAdminVehicleReassociate job={data ? data.jobs_by_pk : {}} />
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<Card style={cardStyle}>
|
||||
<JobsAdminDatesChange job={data ? data.jobs_by_pk : {}} />
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(JobsCloseContainer);
|
||||
|
||||
@@ -1,77 +1,74 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobsListPaginated from "../../components/jobs-list-paginated/jobs-list-paginated.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED} from "../../graphql/jobs.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//bodyshop: selectBodyshop,
|
||||
//bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder, statusFilters } = searchParams;
|
||||
export function AllJobs({setBreadcrumbs, setSelectedHeader}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder, statusFilters} = searchParams;
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}),
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "ro_number"]:
|
||||
sortorder && sortorder !== "false"
|
||||
? (sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc")
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
...(statusFilters ? {statusList: JSON.parse(statusFilters)} : {}),
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "ro_number"]:
|
||||
sortorder && sortorder !== "false"
|
||||
? (sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc")
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-all");
|
||||
setSelectedHeader("alljobs");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/jobs/all", label: t("titles.bc.jobs-all") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-all");
|
||||
setSelectedHeader("alljobs");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/jobs/all", label: t("titles.bc.jobs-all")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<RbacWrapper action="jobs:list-all">
|
||||
<JobsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.jobs_aggregate.aggregate.count : 0}
|
||||
jobs={data ? data.jobs : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
return (
|
||||
<RbacWrapper action="jobs:list-all">
|
||||
<JobsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.jobs_aggregate.aggregate.count : 0}
|
||||
jobs={data ? data.jobs : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AllJobs);
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
import { Button, PageHeader } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {Button} from "antd";
|
||||
import {PageHeader} from "@ant-design/pro-layout";
|
||||
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobsAvailableTableContainer from "../../components/jobs-available-table/jobs-available-table.container";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectPartnerVersion} from "../../redux/application/application.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
partnerVersion: selectPartnerVersion,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function JobsAvailablePageContainer({
|
||||
partnerVersion,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
partnerVersion,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobsavailable");
|
||||
setSelectedHeader("availablejobs");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/available", label: t("titles.bc.availablejobs") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobsavailable");
|
||||
setSelectedHeader("availablejobs");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/available", label: t("titles.bc.availablejobs")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:available-list">
|
||||
<div>
|
||||
<PageHeader
|
||||
title={t("titles.bc.availablejobs")}
|
||||
extra={
|
||||
<Link to="/manage/jobs/new">
|
||||
<Button>{t("jobs.actions.manualnew")}</Button>
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
{!partnerVersion && (
|
||||
<AlertComponent
|
||||
type="warning"
|
||||
message={t("general.messages.partnernotrunning")}
|
||||
/>
|
||||
)}
|
||||
<JobsAvailableTableContainer />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="jobs:available-list">
|
||||
<div>
|
||||
<PageHeader
|
||||
title={t("titles.bc.availablejobs")}
|
||||
extra={
|
||||
<Link to="/manage/jobs/new">
|
||||
<Button>{t("jobs.actions.manualnew")}</Button>
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
{!partnerVersion && (
|
||||
<AlertComponent
|
||||
type="warning"
|
||||
message={t("general.messages.partnernotrunning")}
|
||||
/>
|
||||
)}
|
||||
<JobsAvailableTableContainer/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsAvailablePageContainer);
|
||||
|
||||
@@ -1,126 +1,125 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Col, Row, Typography } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Col, Row, Typography} from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobChecklistForm from "../../components/job-checklist/components/job-checklist-form/job-checklist-form.component";
|
||||
import JobChecklistForm
|
||||
from "../../components/job-checklist/components/job-checklist-form/job-checklist-form.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_JOB_CHECKLISTS } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {QUERY_JOB_CHECKLISTS} from "../../graphql/jobs.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function JobsChecklistViewContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { jobId } = useParams();
|
||||
const { loading, error, data } = useQuery(QUERY_JOB_CHECKLISTS, {
|
||||
variables: { id: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const {jobId} = useParams();
|
||||
const {loading, error, data} = useQuery(QUERY_JOB_CHECKLISTS, {
|
||||
variables: {id: jobId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-checklist");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/jobs", label: t("titles.bc.jobs") },
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: (data && data.jobs_by_pk && data.jobs_by_pk.ro_number) || "",
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/checklist`,
|
||||
label: t("titles.bc.jobs-checklist"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, jobId, data, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-checklist");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/jobs", label: t("titles.bc.jobs")},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: (data && data.jobs_by_pk && data.jobs_by_pk.ro_number) || "",
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/checklist`,
|
||||
label: t("titles.bc.jobs-checklist"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, jobId, data, setSelectedHeader]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
//The Form is the actual config to use.
|
||||
//The Form is the actual config to use.
|
||||
|
||||
const CompletedBy = ({ checklist }) => (
|
||||
<div>
|
||||
<div>
|
||||
{t("jobs.labels.checklistcompletedby", {
|
||||
by: checklist.completed_by,
|
||||
at: moment(checklist.completed_at).format("MM/DD/YYYY @ h:mm a"),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const CompletedBy = ({checklist}) => (
|
||||
<div>
|
||||
<div>
|
||||
{t("jobs.labels.checklistcompletedby", {
|
||||
by: checklist.completed_by,
|
||||
at: dayjs(checklist.completed_at).format("MM/DD/YYYY @ h:mm a"),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:checklist-view">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<Typography.Title level={4}>
|
||||
{t("jobs.labels.intakechecklist")}
|
||||
</Typography.Title>
|
||||
{data.jobs_by_pk.intakechecklist &&
|
||||
data.jobs_by_pk.intakechecklist.form && (
|
||||
<>
|
||||
<JobChecklistForm
|
||||
formItems={
|
||||
data.jobs_by_pk.intakechecklist &&
|
||||
data.jobs_by_pk.intakechecklist.form
|
||||
}
|
||||
type="intake"
|
||||
job={data.jobs_by_pk}
|
||||
readOnly
|
||||
/>
|
||||
<CompletedBy checklist={data.jobs_by_pk.intakechecklist} />
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Typography.Title level={4}>
|
||||
{t("jobs.labels.deliverchecklist")}
|
||||
</Typography.Title>
|
||||
{data.jobs_by_pk.deliverchecklist &&
|
||||
data.jobs_by_pk.deliverchecklist.form && (
|
||||
<>
|
||||
<JobChecklistForm
|
||||
formItems={
|
||||
data.jobs_by_pk.deliverchecklist &&
|
||||
data.jobs_by_pk.deliverchecklist.form
|
||||
}
|
||||
type="deliver"
|
||||
job={data.jobs_by_pk}
|
||||
readOnly
|
||||
/>
|
||||
<CompletedBy checklist={data.jobs_by_pk.deliverchecklist} />
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="jobs:checklist-view">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<Typography.Title level={4}>
|
||||
{t("jobs.labels.intakechecklist")}
|
||||
</Typography.Title>
|
||||
{data.jobs_by_pk.intakechecklist &&
|
||||
data.jobs_by_pk.intakechecklist.form && (
|
||||
<>
|
||||
<JobChecklistForm
|
||||
formItems={
|
||||
data.jobs_by_pk.intakechecklist &&
|
||||
data.jobs_by_pk.intakechecklist.form
|
||||
}
|
||||
type="intake"
|
||||
job={data.jobs_by_pk}
|
||||
readOnly
|
||||
/>
|
||||
<CompletedBy checklist={data.jobs_by_pk.intakechecklist}/>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Typography.Title level={4}>
|
||||
{t("jobs.labels.deliverchecklist")}
|
||||
</Typography.Title>
|
||||
{data.jobs_by_pk.deliverchecklist &&
|
||||
data.jobs_by_pk.deliverchecklist.form && (
|
||||
<>
|
||||
<JobChecklistForm
|
||||
formItems={
|
||||
data.jobs_by_pk.deliverchecklist &&
|
||||
data.jobs_by_pk.deliverchecklist.form
|
||||
}
|
||||
type="deliver"
|
||||
job={data.jobs_by_pk}
|
||||
readOnly
|
||||
/>
|
||||
<CompletedBy checklist={data.jobs_by_pk.deliverchecklist}/>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsChecklistViewContainer);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,88 +1,85 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Result } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Result} from "antd";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobCalculateTotals from "../../components/job-calculate-totals/job-calculate-totals.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_JOB_CLOSE_DETAILS } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setJobReadOnly,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {QUERY_JOB_CLOSE_DETAILS} from "../../graphql/jobs.queries";
|
||||
import {setBreadcrumbs, setJobReadOnly, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import IsJobReadOnly from "../../utils/jobReadOnly";
|
||||
import JobsCloseComponent from "./jobs-close.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setJobReadOnly: (bool) => dispatch(setJobReadOnly(bool)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setJobReadOnly: (bool) => dispatch(setJobReadOnly(bool)),
|
||||
});
|
||||
|
||||
export function JobsCloseContainer({
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
setJobReadOnly,
|
||||
}) {
|
||||
const { jobId } = useParams();
|
||||
const { loading, error, data } = useQuery(QUERY_JOB_CLOSE_DETAILS, {
|
||||
variables: { id: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
if (data && data.jobs_by_pk) {
|
||||
setJobReadOnly(IsJobReadOnly(data.jobs_by_pk));
|
||||
}
|
||||
}, [data, setJobReadOnly]);
|
||||
useEffect(() => {
|
||||
setSelectedHeader("activejobs");
|
||||
document.title = t("titles.jobs-close", {
|
||||
number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
setJobReadOnly,
|
||||
}) {
|
||||
const {jobId} = useParams();
|
||||
const {loading, error, data} = useQuery(QUERY_JOB_CLOSE_DETAILS, {
|
||||
variables: {id: jobId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
if (data && data.jobs_by_pk) {
|
||||
setJobReadOnly(IsJobReadOnly(data.jobs_by_pk));
|
||||
}
|
||||
}, [data, setJobReadOnly]);
|
||||
useEffect(() => {
|
||||
setSelectedHeader("activejobs");
|
||||
document.title = t("titles.jobs-close", {
|
||||
number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
});
|
||||
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/`,
|
||||
label: t("titles.bc.jobs"),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/close`,
|
||||
label: t("titles.bc.jobs-close"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, jobId, data, setSelectedHeader]);
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/`,
|
||||
label: t("titles.bc.jobs"),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: data ? data.jobs_by_pk && data.jobs_by_pk.ro_number : null,
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/close`,
|
||||
label: t("titles.bc.jobs-close"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, jobId, data, setSelectedHeader]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (!!!data.jobs_by_pk) return <NotFound />;
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
if (!!!data.jobs_by_pk) return <NotFound/>;
|
||||
|
||||
if (!data.jobs_by_pk.job_totals)
|
||||
return (
|
||||
<Result
|
||||
title={t("jobs.errors.nofinancial")}
|
||||
extra={<JobCalculateTotals job={data.jobs_by_pk}/>}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!data.jobs_by_pk.job_totals)
|
||||
return (
|
||||
<Result
|
||||
title={t("jobs.errors.nofinancial")}
|
||||
extra={<JobCalculateTotals job={data.jobs_by_pk} />}
|
||||
/>
|
||||
<RbacWrapper action="jobs:close">
|
||||
<div>
|
||||
<JobsCloseComponent job={data ? data.jobs_by_pk : {}}/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:close">
|
||||
<div>
|
||||
<JobsCloseComponent job={data ? data.jobs_by_pk : {}} />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(JobsCloseContainer);
|
||||
|
||||
@@ -1,155 +1,158 @@
|
||||
import { Button, PageHeader, Result, Space, Steps } from "antd";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import {Button, Result, Space, Steps} from "antd";
|
||||
import {PageHeader} from "@ant-design/pro-layout";
|
||||
|
||||
import React, {useContext, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {Link} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobsCreateJobsInfo from "../../components/jobs-create-jobs-info/jobs-create-jobs-info.component";
|
||||
import JobsCreateOwnerInfoContainer from "../../components/jobs-create-owner-info/jobs-create-owner-info.container";
|
||||
import JobsCreateVehicleInfoContainer from "../../components/jobs-create-vehicle-info/jobs-create-vehicle-info.container";
|
||||
import JobsCreateVehicleInfoContainer
|
||||
from "../../components/jobs-create-vehicle-info/jobs-create-vehicle-info.container";
|
||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||
|
||||
export default function JobsCreateComponent({ form }) {
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [errorMessage, setErrorMessage] = useState(null);
|
||||
const [state] = useContext(JobCreateContext);
|
||||
export default function JobsCreateComponent({form}) {
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [errorMessage, setErrorMessage] = useState(null);
|
||||
const [state] = useContext(JobCreateContext);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const steps = [
|
||||
{
|
||||
title: t("jobs.labels.create.vehicleinfo"),
|
||||
content: <JobsCreateVehicleInfoContainer form={form} />,
|
||||
validation:
|
||||
!!state.vehicle.new ||
|
||||
!!state.vehicle.selectedid ||
|
||||
!!state.vehicle.none,
|
||||
error: t("vehicles.errors.selectexistingornew"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.create.ownerinfo"),
|
||||
content: <JobsCreateOwnerInfoContainer />,
|
||||
validation: !!state.owner.new || !!state.owner.selectedid,
|
||||
error: t("owners.errors.selectexistingornew"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.create.jobinfo"),
|
||||
content: <JobsCreateJobsInfo form={form} selected={pageIndex === 2} />,
|
||||
},
|
||||
];
|
||||
const {t} = useTranslation();
|
||||
const steps = [
|
||||
{
|
||||
title: t("jobs.labels.create.vehicleinfo"),
|
||||
content: <JobsCreateVehicleInfoContainer form={form}/>,
|
||||
validation:
|
||||
!!state.vehicle.new ||
|
||||
!!state.vehicle.selectedid ||
|
||||
!!state.vehicle.none,
|
||||
error: t("vehicles.errors.selectexistingornew"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.create.ownerinfo"),
|
||||
content: <JobsCreateOwnerInfoContainer/>,
|
||||
validation: !!state.owner.new || !!state.owner.selectedid,
|
||||
error: t("owners.errors.selectexistingornew"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.labels.create.jobinfo"),
|
||||
content: <JobsCreateJobsInfo form={form} selected={pageIndex === 2}/>,
|
||||
},
|
||||
];
|
||||
|
||||
const next = () => {
|
||||
setPageIndex(pageIndex + 1);
|
||||
};
|
||||
const prev = () => {
|
||||
setPageIndex(pageIndex - 1);
|
||||
};
|
||||
const { Step } = Steps;
|
||||
const next = () => {
|
||||
setPageIndex(pageIndex + 1);
|
||||
};
|
||||
const prev = () => {
|
||||
setPageIndex(pageIndex - 1);
|
||||
};
|
||||
const {Step} = Steps;
|
||||
|
||||
const ProgressButtons = ({ top }) => {
|
||||
return (
|
||||
<PageHeader
|
||||
extra={
|
||||
<Space wrap>
|
||||
{pageIndex > 0 && <Button onClick={() => prev()}>Previous</Button>}
|
||||
{pageIndex < steps.length - 1 && (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((r) => {
|
||||
if (steps[pageIndex].validation) {
|
||||
setErrorMessage(null);
|
||||
next();
|
||||
} else {
|
||||
setErrorMessage(steps[pageIndex].error);
|
||||
}
|
||||
})
|
||||
.catch((error) => console.log("error", error));
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
{pageIndex === steps.length - 1 && (
|
||||
<Button type="primary" htmlType="submit">
|
||||
Done
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{top && (
|
||||
<Steps current={pageIndex}>
|
||||
{steps.map((item, idx) => (
|
||||
<Step
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontWeight: idx === pageIndex && "bolder",
|
||||
}}
|
||||
onClick={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((r) => {
|
||||
if (steps[pageIndex].validation) {
|
||||
setErrorMessage(null);
|
||||
setPageIndex(idx);
|
||||
} else {
|
||||
setErrorMessage(steps[pageIndex].error);
|
||||
}
|
||||
})
|
||||
.catch((error) => console.log("error", error));
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Steps>
|
||||
)}
|
||||
</PageHeader>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{state.created ? (
|
||||
<div>
|
||||
<Result
|
||||
status="success"
|
||||
title={t("jobs.successes.creatednoclick")}
|
||||
extra={[
|
||||
<Link to={`/manage/jobs/${state.newJobId}`} key="gotojob">
|
||||
<Button type="primary">{t("jobs.actions.gotojob")}</Button>
|
||||
</Link>,
|
||||
<Link to={`/manage/jobs/`} key="gotojoblist">
|
||||
<Button>{t("menus.header.activejobs")}</Button>
|
||||
</Link>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<ProgressButtons top />
|
||||
|
||||
{errorMessage ? (
|
||||
<div>
|
||||
<AlertComponent message={errorMessage} type="error" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{steps.map((item, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
style={{
|
||||
display: idx === pageIndex ? "" : "none",
|
||||
margin: "1rem",
|
||||
}}
|
||||
const ProgressButtons = ({top}) => {
|
||||
return (
|
||||
<PageHeader
|
||||
extra={
|
||||
<Space wrap>
|
||||
{pageIndex > 0 && <Button onClick={() => prev()}>Previous</Button>}
|
||||
{pageIndex < steps.length - 1 && (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((r) => {
|
||||
if (steps[pageIndex].validation) {
|
||||
setErrorMessage(null);
|
||||
next();
|
||||
} else {
|
||||
setErrorMessage(steps[pageIndex].error);
|
||||
}
|
||||
})
|
||||
.catch((error) => console.log("error", error));
|
||||
}}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
{pageIndex === steps.length - 1 && (
|
||||
<Button type="primary" htmlType="submit">
|
||||
Done
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{item.content}
|
||||
</div>
|
||||
))}
|
||||
<ProgressButtons />
|
||||
{top && (
|
||||
<Steps current={pageIndex}>
|
||||
{steps.map((item, idx) => (
|
||||
<Step
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontWeight: idx === pageIndex && "bolder",
|
||||
}}
|
||||
onClick={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((r) => {
|
||||
if (steps[pageIndex].validation) {
|
||||
setErrorMessage(null);
|
||||
setPageIndex(idx);
|
||||
} else {
|
||||
setErrorMessage(steps[pageIndex].error);
|
||||
}
|
||||
})
|
||||
.catch((error) => console.log("error", error));
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Steps>
|
||||
)}
|
||||
</PageHeader>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{state.created ? (
|
||||
<div>
|
||||
<Result
|
||||
status="success"
|
||||
title={t("jobs.successes.creatednoclick")}
|
||||
extra={[
|
||||
<Link to={`/manage/jobs/${state.newJobId}`} key="gotojob">
|
||||
<Button type="primary">{t("jobs.actions.gotojob")}</Button>
|
||||
</Link>,
|
||||
<Link to={`/manage/jobs/`} key="gotojoblist">
|
||||
<Button>{t("menus.header.activejobs")}</Button>
|
||||
</Link>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<ProgressButtons top/>
|
||||
|
||||
{errorMessage ? (
|
||||
<div>
|
||||
<AlertComponent message={errorMessage} type="error"/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{steps.map((item, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
style={{
|
||||
display: idx === pageIndex ? "" : "none",
|
||||
margin: "1rem",
|
||||
}}
|
||||
>
|
||||
{item.content}
|
||||
</div>
|
||||
))}
|
||||
<ProgressButtons/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,194 +1,193 @@
|
||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||
import { Form, notification } from "antd";
|
||||
import {useLazyQuery, useMutation} from "@apollo/client";
|
||||
import {Form, notification} from "antd";
|
||||
import _ from "lodash";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
|
||||
import { QUERY_OWNER_FOR_JOB_CREATION } from "../../graphql/owners.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {INSERT_NEW_JOB} from "../../graphql/jobs.queries";
|
||||
import {QUERY_OWNER_FOR_JOB_CREATION} from "../../graphql/owners.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import JobsCreateComponent from "./jobs-create.component";
|
||||
import JobCreateContext from "./jobs-create.context";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
const contextState = useState({
|
||||
vehicle: {
|
||||
new: false,
|
||||
search: "",
|
||||
selectedid: null,
|
||||
vehicleObj: null,
|
||||
none: false,
|
||||
},
|
||||
owner: { new: false, search: "", selectedid: null },
|
||||
job: null,
|
||||
created: false,
|
||||
error: null,
|
||||
newJobId: null,
|
||||
newJobEstNum: null,
|
||||
});
|
||||
const [form] = Form.useForm();
|
||||
const [state, setState] = contextState;
|
||||
const [insertJob] = useMutation(INSERT_NEW_JOB);
|
||||
const [loadOwner, RemoteOwnerData] = useLazyQuery(
|
||||
QUERY_OWNER_FOR_JOB_CREATION
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!!state.owner.selectedid) {
|
||||
loadOwner({
|
||||
variables: { id: state.owner.selectedid },
|
||||
});
|
||||
}
|
||||
}, [state.owner.selectedid, loadOwner]);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-create");
|
||||
setSelectedHeader("newjob");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/available", label: t("titles.bc.availablejobs") },
|
||||
{
|
||||
link: "/manage/jobs/new",
|
||||
label: t("titles.bc.jobs-new"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
const runInsertJob = (job) => {
|
||||
insertJob({ variables: { job: job } })
|
||||
.then((resp) => {
|
||||
setState({
|
||||
...state,
|
||||
created: true,
|
||||
error: null,
|
||||
newJobId: resp.data.insert_jobs.returning[0].id,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.creating", { error: error }),
|
||||
});
|
||||
setState({ ...state, error: error });
|
||||
});
|
||||
};
|
||||
|
||||
const handleFinish = (values) => {
|
||||
let job = Object.assign(
|
||||
{},
|
||||
values,
|
||||
{ date_open: new Date() },
|
||||
{ date_estimated: new Date() },
|
||||
{
|
||||
vehicle:
|
||||
state.vehicle.selectedid || state.vehicle.none
|
||||
? null
|
||||
: values.vehicle,
|
||||
vehicleid: state.vehicle.selectedid || null,
|
||||
},
|
||||
{
|
||||
owner: state.owner.selectedid ? null : values.owner,
|
||||
ownerid: state.owner.selectedid || null,
|
||||
},
|
||||
{
|
||||
status: bodyshop.md_ro_statuses.default_imported || "Open*",
|
||||
shopid: bodyshop.id,
|
||||
}
|
||||
function JobsCreateContainer({bodyshop, setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
const contextState = useState({
|
||||
vehicle: {
|
||||
new: false,
|
||||
search: "",
|
||||
selectedid: null,
|
||||
vehicleObj: null,
|
||||
none: false,
|
||||
},
|
||||
owner: {new: false, search: "", selectedid: null},
|
||||
job: null,
|
||||
created: false,
|
||||
error: null,
|
||||
newJobId: null,
|
||||
newJobEstNum: null,
|
||||
});
|
||||
const [form] = Form.useForm();
|
||||
const [state, setState] = contextState;
|
||||
const [insertJob] = useMutation(INSERT_NEW_JOB);
|
||||
const [loadOwner, RemoteOwnerData] = useLazyQuery(
|
||||
QUERY_OWNER_FOR_JOB_CREATION
|
||||
);
|
||||
|
||||
let ownerData;
|
||||
if (!!!job.ownerid) {
|
||||
ownerData = job.owner.data;
|
||||
ownerData.shopid = bodyshop.id;
|
||||
delete ownerData.allow_text_message;
|
||||
delete ownerData.preferred_contact;
|
||||
delete job.ownerid;
|
||||
} else {
|
||||
ownerData = _.cloneDeep(RemoteOwnerData.data.owners_by_pk);
|
||||
delete ownerData.id;
|
||||
delete ownerData.__typename;
|
||||
}
|
||||
if (!state.vehicle.none) {
|
||||
if (!!!job.vehicleid) {
|
||||
delete job.vehicleid;
|
||||
job.vehicle.data.shopid = bodyshop.id;
|
||||
job.plate_no = job.vehicle.data.plate_no;
|
||||
job.plate_st = job.vehicle.data.plate_st;
|
||||
job.v_vin = job.vehicle.data.v_vin;
|
||||
job.v_model_yr = job.vehicle.data.v_model_yr;
|
||||
job.v_model_desc = job.vehicle.data.v_model_desc;
|
||||
job.v_make_desc = job.vehicle.data.v_make_desc;
|
||||
job.v_color = job.vehicle.data.v_color;
|
||||
} else {
|
||||
job.plate_no = state.vehicle.vehicleObj.plate_no;
|
||||
job.plate_st = state.vehicle.vehicleObj.plate_st;
|
||||
job.v_vin = state.vehicle.vehicleObj.v_vin;
|
||||
job.v_model_yr = state.vehicle.vehicleObj.v_model_yr;
|
||||
job.v_model_desc = state.vehicle.vehicleObj.v_model_desc;
|
||||
job.v_make_desc = state.vehicle.vehicleObj.v_make_desc;
|
||||
job.v_color = state.vehicle.vehicleObj.v_color;
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!!state.owner.selectedid) {
|
||||
loadOwner({
|
||||
variables: {id: state.owner.selectedid},
|
||||
});
|
||||
}
|
||||
}, [state.owner.selectedid, loadOwner]);
|
||||
|
||||
job = { ...job, ...ownerData };
|
||||
|
||||
if (job.owner === null) delete job.owner;
|
||||
if (job.vehicle === null) delete job.vehicle;
|
||||
|
||||
runInsertJob(job);
|
||||
};
|
||||
|
||||
return (
|
||||
<JobCreateContext.Provider value={contextState}>
|
||||
<RbacWrapper action="jobs:create">
|
||||
<Form
|
||||
scrollToFirstError
|
||||
form={form}
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
autoComplete={"off"}
|
||||
initialValues={{
|
||||
tax_tow_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_str_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_paint_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_shop_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_sub_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_lbr_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_levies_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100,
|
||||
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100,
|
||||
cieca_pft: {
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty1,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty2,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty3,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty4,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty5,
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-create");
|
||||
setSelectedHeader("newjob");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/available", label: t("titles.bc.availablejobs")},
|
||||
{
|
||||
link: "/manage/jobs/new",
|
||||
label: t("titles.bc.jobs-new"),
|
||||
},
|
||||
materials: bodyshop.md_responsibility_centers.cieca_pfm,
|
||||
cieca_pfl: bodyshop.md_responsibility_centers.cieca_pfl,
|
||||
parts_tax_rates: bodyshop.md_responsibility_centers.parts_tax_rates,
|
||||
}}
|
||||
>
|
||||
<JobsCreateComponent form={form} />
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
</JobCreateContext.Provider>
|
||||
);
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
const runInsertJob = (job) => {
|
||||
insertJob({variables: {job: job}})
|
||||
.then((resp) => {
|
||||
setState({
|
||||
...state,
|
||||
created: true,
|
||||
error: null,
|
||||
newJobId: resp.data.insert_jobs.returning[0].id,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.creating", {error: error}),
|
||||
});
|
||||
setState({...state, error: error});
|
||||
});
|
||||
};
|
||||
|
||||
const handleFinish = (values) => {
|
||||
let job = Object.assign(
|
||||
{},
|
||||
values,
|
||||
{date_open: new Date()},
|
||||
{date_estimated: new Date()},
|
||||
{
|
||||
vehicle:
|
||||
state.vehicle.selectedid || state.vehicle.none
|
||||
? null
|
||||
: values.vehicle,
|
||||
vehicleid: state.vehicle.selectedid || null,
|
||||
},
|
||||
{
|
||||
owner: state.owner.selectedid ? null : values.owner,
|
||||
ownerid: state.owner.selectedid || null,
|
||||
},
|
||||
{
|
||||
status: bodyshop.md_ro_statuses.default_imported || "Open*",
|
||||
shopid: bodyshop.id,
|
||||
}
|
||||
);
|
||||
|
||||
let ownerData;
|
||||
if (!!!job.ownerid) {
|
||||
ownerData = job.owner.data;
|
||||
ownerData.shopid = bodyshop.id;
|
||||
delete ownerData.allow_text_message;
|
||||
delete ownerData.preferred_contact;
|
||||
delete job.ownerid;
|
||||
} else {
|
||||
ownerData = _.cloneDeep(RemoteOwnerData.data.owners_by_pk);
|
||||
delete ownerData.id;
|
||||
delete ownerData.__typename;
|
||||
}
|
||||
if (!state.vehicle.none) {
|
||||
if (!!!job.vehicleid) {
|
||||
delete job.vehicleid;
|
||||
job.vehicle.data.shopid = bodyshop.id;
|
||||
job.plate_no = job.vehicle.data.plate_no;
|
||||
job.plate_st = job.vehicle.data.plate_st;
|
||||
job.v_vin = job.vehicle.data.v_vin;
|
||||
job.v_model_yr = job.vehicle.data.v_model_yr;
|
||||
job.v_model_desc = job.vehicle.data.v_model_desc;
|
||||
job.v_make_desc = job.vehicle.data.v_make_desc;
|
||||
job.v_color = job.vehicle.data.v_color;
|
||||
} else {
|
||||
job.plate_no = state.vehicle.vehicleObj.plate_no;
|
||||
job.plate_st = state.vehicle.vehicleObj.plate_st;
|
||||
job.v_vin = state.vehicle.vehicleObj.v_vin;
|
||||
job.v_model_yr = state.vehicle.vehicleObj.v_model_yr;
|
||||
job.v_model_desc = state.vehicle.vehicleObj.v_model_desc;
|
||||
job.v_make_desc = state.vehicle.vehicleObj.v_make_desc;
|
||||
job.v_color = state.vehicle.vehicleObj.v_color;
|
||||
}
|
||||
}
|
||||
|
||||
job = {...job, ...ownerData};
|
||||
|
||||
if (job.owner === null) delete job.owner;
|
||||
if (job.vehicle === null) delete job.vehicle;
|
||||
|
||||
runInsertJob(job);
|
||||
};
|
||||
|
||||
return (
|
||||
<JobCreateContext.Provider value={contextState}>
|
||||
<RbacWrapper action="jobs:create">
|
||||
<Form
|
||||
scrollToFirstError
|
||||
form={form}
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
autoComplete={"off"}
|
||||
initialValues={{
|
||||
tax_tow_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_str_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_paint_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_shop_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_sub_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_lbr_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
tax_levies_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100,
|
||||
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100,
|
||||
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100,
|
||||
cieca_pft: {
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty1,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty2,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty3,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty4,
|
||||
...bodyshop.md_responsibility_centers.taxes.tax_ty5,
|
||||
},
|
||||
materials: bodyshop.md_responsibility_centers.cieca_pfm,
|
||||
cieca_pfl: bodyshop.md_responsibility_centers.cieca_pfl,
|
||||
parts_tax_rates: bodyshop.md_responsibility_centers.parts_tax_rates,
|
||||
}}
|
||||
>
|
||||
<JobsCreateComponent form={form}/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
</JobCreateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsCreateContainer);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import React from "react";
|
||||
|
||||
const JobCreateContext = React.createContext(null);
|
||||
export default JobCreateContext;
|
||||
|
||||
@@ -1,82 +1,79 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_DELIVER_CHECKLIST } from "../../graphql/bodyshop.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {QUERY_DELIVER_CHECKLIST} from "../../graphql/bodyshop.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import JobchecklistComponent from "../../components/job-checklist/job-checklist.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function JobsDeliverContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { jobId } = useParams();
|
||||
const { loading, error, data } = useQuery(QUERY_DELIVER_CHECKLIST, {
|
||||
variables: { shopId: bodyshop.id, jobId: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const {jobId} = useParams();
|
||||
const {loading, error, data} = useQuery(QUERY_DELIVER_CHECKLIST, {
|
||||
variables: {shopId: bodyshop.id, jobId: jobId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-deliver");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/jobs", label: t("titles.bc.jobs") },
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: (data && data.jobs_by_pk && data.jobs_by_pk.ro_number) || "",
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/deliver`,
|
||||
label: t("titles.bc.jobs-deliver"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, jobId, data, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-deliver");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/jobs", label: t("titles.bc.jobs")},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number: (data && data.jobs_by_pk && data.jobs_by_pk.ro_number) || "",
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/deliver`,
|
||||
label: t("titles.bc.jobs-deliver"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, jobId, data, setSelectedHeader]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (data && !!!data.bodyshops_by_pk.deliverchecklist)
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
if (data && !!!data.bodyshops_by_pk.deliverchecklist)
|
||||
return (
|
||||
<AlertComponent message={t("deliver.errors.nochecklist")} type="error"/>
|
||||
);
|
||||
return (
|
||||
<AlertComponent message={t("deliver.errors.nochecklist")} type="error" />
|
||||
<RbacWrapper action="jobs:deliver">
|
||||
<div>
|
||||
<JobchecklistComponent
|
||||
type="deliver"
|
||||
checklistConfig={
|
||||
(data && data.bodyshops_by_pk.deliverchecklist) || {}
|
||||
}
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="jobs:deliver">
|
||||
<div>
|
||||
<JobchecklistComponent
|
||||
type="deliver"
|
||||
checklistConfig={
|
||||
(data && data.bodyshops_by_pk.deliverchecklist) || {}
|
||||
}
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsDeliverContainer);
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
import Icon, {
|
||||
BarsOutlined,
|
||||
CalendarFilled,
|
||||
DollarCircleOutlined,
|
||||
FileImageFilled,
|
||||
HistoryOutlined,
|
||||
PrinterFilled,
|
||||
SyncOutlined,
|
||||
ToolFilled,
|
||||
BarsOutlined,
|
||||
CalendarFilled,
|
||||
DollarCircleOutlined,
|
||||
FileImageFilled,
|
||||
HistoryOutlined,
|
||||
PrinterFilled,
|
||||
SyncOutlined,
|
||||
ToolFilled,
|
||||
} from "@ant-design/icons";
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Form,
|
||||
PageHeader,
|
||||
Space,
|
||||
Tabs,
|
||||
notification,
|
||||
} from "antd";
|
||||
import {Button, Divider, Form, notification, Space, Tabs,} from "antd";
|
||||
import {PageHeader} from "@ant-design/pro-layout";
|
||||
|
||||
import Axios from "axios";
|
||||
import moment from "moment";
|
||||
import dayjs from "../../utils/day";
|
||||
import queryString from "query-string";
|
||||
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 React, {useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {FaHardHat, FaRegStickyNote, FaShieldAlt} from "react-icons/fa";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
|
||||
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
||||
@@ -43,409 +37,364 @@ import JobsDetailPliContainer from "../../components/jobs-detail-pli/jobs-detail
|
||||
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 JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||
import JobsDocumentsLocalGallery
|
||||
from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
|
||||
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
|
||||
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {insertAuditTrail} from "../../redux/application/application.actions";
|
||||
import {selectJobReadOnly} from "../../redux/application/application.selectors";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
import _ from "lodash";
|
||||
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
|
||||
import { DateTimeFormat } from "./../../utils/DateFormatter";
|
||||
import {DateTimeFormat} from "./../../utils/DateFormatter";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
jobRO: selectJobReadOnly,
|
||||
bodyshop: selectBodyshop,
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPrintCenterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
setPrintCenterContext: (context) =>
|
||||
dispatch(setModalContext({context: context, modal: "printCenter"})),
|
||||
insertAuditTrail: ({jobid, operation}) =>
|
||||
dispatch(insertAuditTrail({jobid, operation})),
|
||||
});
|
||||
|
||||
export function JobsDetailPage({
|
||||
bodyshop,
|
||||
setPrintCenterContext,
|
||||
jobRO,
|
||||
job,
|
||||
mutationUpdateJob,
|
||||
handleSubmit,
|
||||
insertAuditTrail,
|
||||
refetch,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const history = useHistory();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const search = queryString.parse(useLocation().search);
|
||||
bodyshop,
|
||||
setPrintCenterContext,
|
||||
jobRO,
|
||||
job,
|
||||
mutationUpdateJob,
|
||||
handleSubmit,
|
||||
insertAuditTrail,
|
||||
refetch,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const history = useNavigate();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const formItemLayout = {
|
||||
layout: "vertical",
|
||||
};
|
||||
|
||||
const formItemLayout = {
|
||||
layout: "vertical",
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
//form.setFieldsValue(transormJobToForm(job));
|
||||
form.resetFields();
|
||||
}, [form, job]);
|
||||
|
||||
//useKeyboardSaveShortcut(form.submit);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
|
||||
const result = await mutationUpdateJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
...UndefinedToNull(values, [
|
||||
"alt_transport",
|
||||
"category",
|
||||
"referral_source",
|
||||
]),
|
||||
// The union and spread is required to keep values coming in from the estimating system that aren't displayed.
|
||||
parts_tax_rates: _.union(
|
||||
Object.keys(job.parts_tax_rates),
|
||||
Object.keys(values.parts_tax_rates)
|
||||
).reduce((acc, val) => {
|
||||
acc[val] = {
|
||||
...job.parts_tax_rates[val],
|
||||
...values.parts_tax_rates[val],
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
materials: _.union(
|
||||
Object.keys(job.materials),
|
||||
Object.keys(values.materials)
|
||||
).reduce((acc, val) => {
|
||||
acc[val] = {
|
||||
...job.materials[val],
|
||||
...values.materials[val],
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
cieca_pfl: _.union(
|
||||
Object.keys(job.cieca_pfl),
|
||||
Object.keys(values.cieca_pfl)
|
||||
).reduce((acc, val) => {
|
||||
acc[val] = {
|
||||
...job.cieca_pfl[val],
|
||||
...values.cieca_pfl[val],
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
cieca_pfo: { ...job.cieca_pfo, ...values.cieca_pfo },
|
||||
},
|
||||
},
|
||||
});
|
||||
try {
|
||||
const newTotals = await Axios.post("/job/totalsssu", {
|
||||
id: job.id,
|
||||
});
|
||||
|
||||
if (newTotals.status !== 200 || result.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.totalscalc"),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.savetitle"),
|
||||
});
|
||||
const changedAuditFields = form.getFieldsValue(
|
||||
[
|
||||
"scheduled_in",
|
||||
"actual_in",
|
||||
"scheduled_completion",
|
||||
"actual_completion",
|
||||
"scheduled_delivery",
|
||||
"actual_delivery",
|
||||
"date_invoiced",
|
||||
"ins_co_nm",
|
||||
"ded_amt",
|
||||
"ded_status",
|
||||
"date_exported",
|
||||
"special_coverage_policy",
|
||||
"ca_gst_registrant",
|
||||
"ca_bc_pvrt",
|
||||
"scheduled_in",
|
||||
"rate_la1",
|
||||
"rate_la2",
|
||||
"rate_la3",
|
||||
"rate_la4",
|
||||
"rate_laa",
|
||||
"rate_lab",
|
||||
"rate_lad",
|
||||
"rate_lae",
|
||||
"rate_laf",
|
||||
"rate_lag",
|
||||
"rate_lam",
|
||||
"rate_lar",
|
||||
"rate_las",
|
||||
"rate_lau",
|
||||
"rate_ma2s",
|
||||
"rate_ma2t",
|
||||
"rate_ma3s",
|
||||
"rate_mabl",
|
||||
"rate_macs",
|
||||
"rate_mapa",
|
||||
"rate_mahw",
|
||||
"rate_mash",
|
||||
"rate_matd",
|
||||
],
|
||||
(meta) => meta && meta.touched
|
||||
);
|
||||
|
||||
Object.keys(changedAuditFields).forEach((key) => {
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobfieldchange(
|
||||
key,
|
||||
changedAuditFields[key] instanceof moment
|
||||
? DateTimeFormat(changedAuditFields[key])
|
||||
: changedAuditFields[key]
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
await refetch();
|
||||
form.setFieldsValue(transormJobToForm(job));
|
||||
useEffect(() => {
|
||||
//form.setFieldsValue(transormJobToForm(job));
|
||||
form.resetFields();
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.totalscalc"),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [form, job]);
|
||||
|
||||
const menuExtra = (
|
||||
<Space wrap>
|
||||
<Button
|
||||
onClick={() => {
|
||||
refetch();
|
||||
}}
|
||||
key="refresh"
|
||||
>
|
||||
<SyncOutlined />
|
||||
{t("general.labels.refresh")}
|
||||
</Button>
|
||||
<JobsChangeStatus job={job} />
|
||||
<JobSyncButton job={job} />
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
id: job.id,
|
||||
job: job,
|
||||
type: "job",
|
||||
//useKeyboardSaveShortcut(form.submit);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
|
||||
const result = await mutationUpdateJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
...UndefinedToNull(values, [
|
||||
"alt_transport",
|
||||
"category",
|
||||
"referral_source",
|
||||
]),
|
||||
// The union and spread is required to keep values coming in from the estimating system that aren't displayed.
|
||||
parts_tax_rates: _.union(
|
||||
Object.keys(job.parts_tax_rates),
|
||||
Object.keys(values.parts_tax_rates)
|
||||
).reduce((acc, val) => {
|
||||
acc[val] = {
|
||||
...job.parts_tax_rates[val],
|
||||
...values.parts_tax_rates[val],
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
materials: _.union(
|
||||
Object.keys(job.materials),
|
||||
Object.keys(values.materials)
|
||||
).reduce((acc, val) => {
|
||||
acc[val] = {
|
||||
...job.materials[val],
|
||||
...values.materials[val],
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
cieca_pfl: _.union(
|
||||
Object.keys(job.cieca_pfl),
|
||||
Object.keys(values.cieca_pfl)
|
||||
).reduce((acc, val) => {
|
||||
acc[val] = {
|
||||
...job.cieca_pfl[val],
|
||||
...values.cieca_pfl[val],
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
cieca_pfo: {...job.cieca_pfo, ...values.cieca_pfo},
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
key="printing"
|
||||
>
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
<JobsConvertButton
|
||||
job={job}
|
||||
refetch={refetch}
|
||||
parentFormIsFieldsTouched={form.isFieldsTouched}
|
||||
/>
|
||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
disabled={jobRO}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
});
|
||||
try {
|
||||
const newTotals = await Axios.post("/job/totalsssu", {
|
||||
id: job.id,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ScheduleJobModalContainer />
|
||||
<JobReconciliationModal />
|
||||
<JobLineUpsertModalContainer />
|
||||
<NoteUpsertModalComponent />
|
||||
<Form
|
||||
form={form}
|
||||
name="JobDetailForm"
|
||||
onFinish={handleFinish}
|
||||
{...formItemLayout}
|
||||
autoComplete={"off"}
|
||||
initialValues={transormJobToForm(job)}
|
||||
>
|
||||
<PageHeader
|
||||
// onBack={() => window.history.back()}
|
||||
title={job.ro_number || t("general.labels.na")}
|
||||
extra={menuExtra}
|
||||
/>
|
||||
<JobsDetailHeader job={job} />
|
||||
<Divider type="horizontal" />
|
||||
<JobProfileDataWarning job={job} />
|
||||
<FormFieldsChanged form={form} />
|
||||
<Tabs
|
||||
defaultActiveKey={search.tab}
|
||||
onChange={(key) => history.push({ search: `?tab=${key}` })}
|
||||
tabBarStyle={{ fontWeight: "bold", borderBottom: "10px" }}
|
||||
>
|
||||
<Tabs.TabPane
|
||||
forceRender
|
||||
tab={
|
||||
<span>
|
||||
<Icon component={FaShieldAlt} />
|
||||
{t("menus.jobsdetail.general")}
|
||||
</span>
|
||||
if (newTotals.status !== 200 || result.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.totalscalc"),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.savetitle"),
|
||||
});
|
||||
const changedAuditFields = form.getFieldsValue(
|
||||
[
|
||||
"scheduled_in",
|
||||
"actual_in",
|
||||
"scheduled_completion",
|
||||
"actual_completion",
|
||||
"scheduled_delivery",
|
||||
"actual_delivery",
|
||||
"date_invoiced",
|
||||
"ins_co_nm",
|
||||
"ded_amt",
|
||||
"ded_status",
|
||||
"date_exported",
|
||||
"special_coverage_policy",
|
||||
"ca_gst_registrant",
|
||||
"ca_bc_pvrt",
|
||||
"scheduled_in",
|
||||
"rate_la1",
|
||||
"rate_la2",
|
||||
"rate_la3",
|
||||
"rate_la4",
|
||||
"rate_laa",
|
||||
"rate_lab",
|
||||
"rate_lad",
|
||||
"rate_lae",
|
||||
"rate_laf",
|
||||
"rate_lag",
|
||||
"rate_lam",
|
||||
"rate_lar",
|
||||
"rate_las",
|
||||
"rate_lau",
|
||||
"rate_ma2s",
|
||||
"rate_ma2t",
|
||||
"rate_ma3s",
|
||||
"rate_mabl",
|
||||
"rate_macs",
|
||||
"rate_mapa",
|
||||
"rate_mahw",
|
||||
"rate_mash",
|
||||
"rate_matd",
|
||||
],
|
||||
(meta) => meta && meta.touched
|
||||
);
|
||||
|
||||
Object.keys(changedAuditFields).forEach((key) => {
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobfieldchange(
|
||||
key,
|
||||
changedAuditFields[key] instanceof dayjs
|
||||
? DateTimeFormat(changedAuditFields[key])
|
||||
: changedAuditFields[key]
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
await refetch();
|
||||
form.setFieldsValue(transormJobToForm(job));
|
||||
form.resetFields();
|
||||
}
|
||||
key="general"
|
||||
>
|
||||
<JobsDetailGeneral job={job} form={form} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
forceRender
|
||||
tab={
|
||||
<span>
|
||||
<BarsOutlined />
|
||||
{t("menus.jobsdetail.repairdata")}
|
||||
</span>
|
||||
}
|
||||
key="repairdata"
|
||||
>
|
||||
<JobsLinesContainer
|
||||
job={job}
|
||||
joblines={job.joblines}
|
||||
refetch={refetch}
|
||||
form={form}
|
||||
} catch (error) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.totalscalc"),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const menuExtra = (
|
||||
<Space wrap>
|
||||
<Button
|
||||
onClick={() => {
|
||||
refetch();
|
||||
}}
|
||||
key="refresh"
|
||||
>
|
||||
<SyncOutlined/>
|
||||
{t("general.labels.refresh")}
|
||||
</Button>
|
||||
<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}
|
||||
parentFormIsFieldsTouched={form.isFieldsTouched}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
forceRender
|
||||
tab={
|
||||
<span>
|
||||
<DollarCircleOutlined />
|
||||
{t("menus.jobsdetail.rates")}
|
||||
</span>
|
||||
}
|
||||
key="rates"
|
||||
>
|
||||
<JobsDetailRates job={job} form={form} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<DollarCircleOutlined />
|
||||
{t("menus.jobsdetail.totals")}
|
||||
</span>
|
||||
}
|
||||
key="totals"
|
||||
>
|
||||
<JobsDetailTotals job={job} refetch={refetch} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<ToolFilled />
|
||||
{t("menus.jobsdetail.partssublet")}
|
||||
</span>
|
||||
}
|
||||
key="partssublet"
|
||||
>
|
||||
<JobsDetailPliContainer job={job} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<Icon component={FaHardHat} />
|
||||
{t("menus.jobsdetail.labor")}
|
||||
</span>
|
||||
}
|
||||
key="labor"
|
||||
>
|
||||
<JobsDetailLaborContainer job={job} jobId={job.id} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
forceRender
|
||||
tab={
|
||||
<span>
|
||||
<CalendarFilled />
|
||||
{t("menus.jobsdetail.dates")}
|
||||
</span>
|
||||
}
|
||||
key="dates"
|
||||
>
|
||||
<JobsDetailDatesComponent job={job} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<FileImageFilled />
|
||||
{t("jobs.labels.documents")}
|
||||
</span>
|
||||
}
|
||||
key="documents"
|
||||
>
|
||||
{bodyshop.uselocalmediaserver ? (
|
||||
<JobsDocumentsLocalGallery job={job} />
|
||||
) : (
|
||||
<JobsDocumentsGalleryContainer jobId={job.id} />
|
||||
)}
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<Icon component={FaRegStickyNote} />
|
||||
{t("jobs.labels.notes")}
|
||||
</span>
|
||||
}
|
||||
key="notes"
|
||||
>
|
||||
<JobNotesContainer jobId={job.id} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<HistoryOutlined />
|
||||
{t("jobs.labels.audit")}
|
||||
</span>
|
||||
}
|
||||
key="audit"
|
||||
>
|
||||
<JobAuditTrail jobId={job.id} />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch}/>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
disabled={jobRO}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ScheduleJobModalContainer/>
|
||||
<JobReconciliationModal/>
|
||||
<JobLineUpsertModalContainer/>
|
||||
<NoteUpsertModalComponent/>
|
||||
<Form
|
||||
form={form}
|
||||
name="JobDetailForm"
|
||||
onFinish={handleFinish}
|
||||
{...formItemLayout}
|
||||
autoComplete={"off"}
|
||||
initialValues={transormJobToForm(job)}
|
||||
>
|
||||
<PageHeader
|
||||
// onBack={() => window.history.back()}
|
||||
title={job.ro_number || t("general.labels.na")}
|
||||
extra={menuExtra}
|
||||
/>
|
||||
<JobsDetailHeader job={job}/>
|
||||
<Divider type="horizontal"/>
|
||||
<JobProfileDataWarning job={job}/>
|
||||
<FormFieldsChanged form={form}/>
|
||||
<Tabs
|
||||
defaultActiveKey={search.tab}
|
||||
onChange={(key) => history({search: `?tab=${key}`})}
|
||||
tabBarStyle={{fontWeight: "bold", borderBottom: "10px"}}
|
||||
items={[
|
||||
{
|
||||
key: "general",
|
||||
icon: <Icon component={FaShieldAlt}/>,
|
||||
label: t("menus.jobsdetail.general"),
|
||||
forceRender: true,
|
||||
children: <JobsDetailGeneral job={job} form={form}/>,
|
||||
},
|
||||
{
|
||||
key: "repairdata",
|
||||
icon: <BarsOutlined/>,
|
||||
label: t("menus.jobsdetail.repairdata"),
|
||||
forceRender: true,
|
||||
children: (
|
||||
<JobsLinesContainer
|
||||
job={job}
|
||||
joblines={job.joblines}
|
||||
refetch={refetch}
|
||||
form={form}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "rates",
|
||||
icon: <DollarCircleOutlined/>,
|
||||
label: t("menus.jobsdetail.rates"),
|
||||
forceRender: true,
|
||||
children: <JobsDetailRates job={job} form={form}/>,
|
||||
},
|
||||
{
|
||||
key: "totals",
|
||||
icon: <DollarCircleOutlined/>,
|
||||
label: t("menus.jobsdetail.totals"),
|
||||
children: <JobsDetailTotals job={job} refetch={refetch}/>,
|
||||
},
|
||||
{
|
||||
key: "partssublet",
|
||||
icon: <ToolFilled/>,
|
||||
label: t("menus.jobsdetail.partssublet"),
|
||||
children: <JobsDetailPliContainer job={job}/>,
|
||||
},
|
||||
{
|
||||
key: "labor",
|
||||
icon: <Icon component={FaHardHat}/>,
|
||||
label: t("menus.jobsdetail.labor"),
|
||||
children: <JobsDetailLaborContainer job={job} jobId={job.id}/>,
|
||||
},
|
||||
{
|
||||
key: "dates",
|
||||
icon: <CalendarFilled/>,
|
||||
label: t("menus.jobsdetail.dates"),
|
||||
forceRender: true,
|
||||
children: <JobsDetailDatesComponent job={job}/>,
|
||||
},
|
||||
{
|
||||
key: "documents",
|
||||
icon: <FileImageFilled/>,
|
||||
label: t("jobs.labels.documents"),
|
||||
children: bodyshop.uselocalmediaserver ? (
|
||||
<JobsDocumentsLocalGallery job={job}/>
|
||||
) : (
|
||||
<JobsDocumentsGalleryContainer jobId={job.id}/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "notes",
|
||||
icon: <Icon component={FaRegStickyNote}/>,
|
||||
label: t("jobs.labels.notes"),
|
||||
children: <JobNotesContainer jobId={job.id}/>,
|
||||
},
|
||||
{
|
||||
key: "audit",
|
||||
icon: <HistoryOutlined/>,
|
||||
label: t("jobs.labels.audit"),
|
||||
children: <JobAuditTrail jobId={job.id}/>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailPage);
|
||||
|
||||
const transormJobToForm = (job) => {
|
||||
Object.keys(job.parts_tax_rates).forEach((parttype) => {
|
||||
Object.keys(job.parts_tax_rates[parttype]).forEach((key) => {
|
||||
if (key.includes("tx_in")) {
|
||||
if (
|
||||
job.parts_tax_rates[parttype][key] === "Y" ||
|
||||
job.parts_tax_rates[parttype][key] === true
|
||||
) {
|
||||
job.parts_tax_rates[parttype][key] = true;
|
||||
} else {
|
||||
job.parts_tax_rates[parttype][key] = false;
|
||||
}
|
||||
}
|
||||
Object.keys(job.parts_tax_rates).forEach((parttype) => {
|
||||
Object.keys(job.parts_tax_rates[parttype]).forEach((key) => {
|
||||
if (key.includes("tx_in")) {
|
||||
if (
|
||||
job.parts_tax_rates[parttype][key] === "Y" ||
|
||||
job.parts_tax_rates[parttype][key] === true
|
||||
) {
|
||||
job.parts_tax_rates[parttype][key] = true;
|
||||
} else {
|
||||
job.parts_tax_rates[parttype][key] = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
...job,
|
||||
loss_date: job.loss_date ? moment(job.loss_date) : null,
|
||||
date_estimated: job.date_estimated ? moment(job.date_estimated) : null,
|
||||
};
|
||||
return {
|
||||
...job,
|
||||
loss_date: job.loss_date ? dayjs(job.loss_date) : null,
|
||||
date_estimated: job.date_estimated ? dayjs(job.date_estimated) : null,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,121 +1,121 @@
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useMutation, useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from 'react-router-dom';
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import SpinComponent from "../../components/loading-spinner/loading-spinner.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
import {OwnerNameDisplayFunction} from "../../components/owner-name-display/owner-name-display.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { GET_JOB_BY_PK, UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import {GET_JOB_BY_PK, UPDATE_JOB} from "../../graphql/jobs.queries";
|
||||
import {
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
setJobReadOnly,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {CreateRecentItem} from "../../utils/create-recent-item";
|
||||
import IsJobReadOnly from "../../utils/jobReadOnly";
|
||||
import JobsDetailPage from "./jobs-detail.page.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setJobReadOnly: (bool) => dispatch(setJobReadOnly(bool)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setJobReadOnly: (bool) => dispatch(setJobReadOnly(bool)),
|
||||
});
|
||||
|
||||
function JobsDetailPageContainer({
|
||||
bodyshop,
|
||||
match,
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
setJobReadOnly,
|
||||
}) {
|
||||
const { jobId } = match.params;
|
||||
const { t } = useTranslation();
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
setJobReadOnly,
|
||||
}) {
|
||||
const {jobId} = useParams();
|
||||
const {t} = useTranslation();
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(GET_JOB_BY_PK, {
|
||||
variables: { id: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const [mutationUpdateJob] = useMutation(UPDATE_JOB);
|
||||
const {loading, error, data, refetch} = useQuery(GET_JOB_BY_PK, {
|
||||
variables: {id: jobId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const [mutationUpdateJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedHeader("activejobs");
|
||||
document.title = loading
|
||||
? t("titles.app")
|
||||
: error
|
||||
? t("titles.app")
|
||||
: t("titles.jobsdetail", {
|
||||
ro_number:
|
||||
(data.jobs_by_pk && data.jobs_by_pk.ro_number) ||
|
||||
t("general.labels.na"),
|
||||
});
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/jobs", label: t("titles.bc.jobs") },
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number:
|
||||
(data && data.jobs_by_pk && data.jobs_by_pk.ro_number) ||
|
||||
t("general.labels.na"),
|
||||
}),
|
||||
},
|
||||
useEffect(() => {
|
||||
setSelectedHeader("activejobs");
|
||||
document.title = loading
|
||||
? t("titles.app")
|
||||
: error
|
||||
? t("titles.app")
|
||||
: t("titles.jobsdetail", {
|
||||
ro_number:
|
||||
(data.jobs_by_pk && data.jobs_by_pk.ro_number) ||
|
||||
t("general.labels.na"),
|
||||
});
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/jobs", label: t("titles.bc.jobs")},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number:
|
||||
(data && data.jobs_by_pk && data.jobs_by_pk.ro_number) ||
|
||||
t("general.labels.na"),
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
if (data && data.jobs_by_pk) {
|
||||
setJobReadOnly(IsJobReadOnly(data.jobs_by_pk));
|
||||
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
jobId,
|
||||
"job",
|
||||
|
||||
`${
|
||||
data.jobs_by_pk.ro_number || t("general.labels.na")
|
||||
} | ${OwnerNameDisplayFunction(data.jobs_by_pk)}`,
|
||||
`/manage/jobs/${jobId}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [
|
||||
loading,
|
||||
data,
|
||||
t,
|
||||
error,
|
||||
setBreadcrumbs,
|
||||
jobId,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
setJobReadOnly,
|
||||
]);
|
||||
|
||||
if (data && data.jobs_by_pk) {
|
||||
setJobReadOnly(IsJobReadOnly(data.jobs_by_pk));
|
||||
if (loading) return <SpinComponent/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
if (!!!data.jobs_by_pk) return <NotFound/>;
|
||||
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
jobId,
|
||||
"job",
|
||||
|
||||
`${
|
||||
data.jobs_by_pk.ro_number || t("general.labels.na")
|
||||
} | ${OwnerNameDisplayFunction(data.jobs_by_pk)}`,
|
||||
`/manage/jobs/${jobId}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [
|
||||
loading,
|
||||
data,
|
||||
t,
|
||||
error,
|
||||
setBreadcrumbs,
|
||||
jobId,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
setJobReadOnly,
|
||||
]);
|
||||
|
||||
if (loading) return <SpinComponent />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (!!!data.jobs_by_pk) return <NotFound />;
|
||||
|
||||
return data.jobs_by_pk ? (
|
||||
<RbacWrapper action="jobs:detail">
|
||||
<JobsDetailPage
|
||||
job={data.jobs_by_pk}
|
||||
mutationUpdateJob={mutationUpdateJob}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
) : (
|
||||
<AlertComponent message={t("jobs.errors.noaccess")} type="error" />
|
||||
);
|
||||
return data.jobs_by_pk ? (
|
||||
<RbacWrapper action="jobs:detail">
|
||||
<JobsDetailPage
|
||||
job={data.jobs_by_pk}
|
||||
mutationUpdateJob={mutationUpdateJob}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
) : (
|
||||
<AlertComponent message={t("jobs.errors.noaccess")} type="error"/>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsDetailPageContainer);
|
||||
|
||||
@@ -1,96 +1,93 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobChecklist from "../../components/job-checklist/job-checklist.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_INTAKE_CHECKLIST } from "../../graphql/bodyshop.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {QUERY_INTAKE_CHECKLIST} from "../../graphql/bodyshop.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { Result } from "antd";
|
||||
import {Result} from "antd";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function JobsIntakeContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { jobId } = useParams();
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const {jobId} = useParams();
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_INTAKE_CHECKLIST, {
|
||||
variables: { shopId: bodyshop.id, jobId: jobId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {loading, error, data} = useQuery(QUERY_INTAKE_CHECKLIST, {
|
||||
variables: {shopId: bodyshop.id, jobId: jobId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-intake");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/jobs", label: t("titles.bc.jobs") },
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number:
|
||||
data &&
|
||||
((data.jobs_by_pk && data.jobs_by_pk.ro_number) ||
|
||||
t("general.labels.na")),
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/intake`,
|
||||
label: t("titles.bc.jobs-intake"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, jobId, data, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-intake");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/jobs", label: t("titles.bc.jobs")},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}`,
|
||||
label: t("titles.bc.jobs-detail", {
|
||||
number:
|
||||
data &&
|
||||
((data.jobs_by_pk && data.jobs_by_pk.ro_number) ||
|
||||
t("general.labels.na")),
|
||||
}),
|
||||
},
|
||||
{
|
||||
link: `/manage/jobs/${jobId}/intake`,
|
||||
label: t("titles.bc.jobs-intake"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, jobId, data, setSelectedHeader]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
if (data && !!!data.bodyshops_by_pk.intakechecklist)
|
||||
return (
|
||||
<AlertComponent message={t("intake.errors.nochecklist")} type="error"/>
|
||||
);
|
||||
|
||||
if (data && !!!data.bodyshops_by_pk.intakechecklist)
|
||||
return (
|
||||
<AlertComponent message={t("intake.errors.nochecklist")} type="error" />
|
||||
<RbacWrapper action="jobs:intake">
|
||||
<div>
|
||||
{!!data.jobs_by_pk.intakechecklist ||
|
||||
!bodyshop.md_ro_statuses.pre_production_statuses.includes(
|
||||
data.jobs_by_pk.status
|
||||
) ? (
|
||||
<Result status="warning" title={t("jobs.errors.cannotintake")}/>
|
||||
) : (
|
||||
<JobChecklist
|
||||
type="intake"
|
||||
checklistConfig={
|
||||
(data && data.bodyshops_by_pk.intakechecklist) || {}
|
||||
}
|
||||
job={data && data.jobs_by_pk}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:intake">
|
||||
<div>
|
||||
{!!data.jobs_by_pk.intakechecklist ||
|
||||
!bodyshop.md_ro_statuses.pre_production_statuses.includes(
|
||||
data.jobs_by_pk.status
|
||||
) ? (
|
||||
<Result status="warning" title={t("jobs.errors.cannotintake")} />
|
||||
) : (
|
||||
<JobChecklist
|
||||
type="intake"
|
||||
checklistConfig={
|
||||
(data && data.bodyshops_by_pk.intakechecklist) || {}
|
||||
}
|
||||
job={data && data.jobs_by_pk}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsIntakeContainer);
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import JobDetailCards from "../../components/job-detail-cards/job-detail-cards.component";
|
||||
import JobsReadyList from "../../components/jobs-ready-list/jobs-ready-list.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function JobsReadyPage({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function JobsReadyPage({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.readyjobs");
|
||||
setSelectedHeader("readyjobs");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/jobs", label: t("titles.bc.jobs-ready") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.readyjobs");
|
||||
setSelectedHeader("readyjobs");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/jobs", label: t("titles.bc.jobs-ready")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:list-ready">
|
||||
<JobsReadyList />
|
||||
<JobDetailCards />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="jobs:list-ready">
|
||||
<JobsReadyList/>
|
||||
<JobDetailCards/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(JobsReadyPage);
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
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 {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function JobsPage({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function JobsPage({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/jobs", label: t("titles.bc.jobs-active") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs");
|
||||
setSelectedHeader("activejobs");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/jobs", label: t("titles.bc.jobs-active")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:list-active">
|
||||
<JobsList />
|
||||
<JobDetailCards />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="jobs:list-active">
|
||||
<JobsList/>
|
||||
<JobDetailCards/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(JobsPage);
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import React, {useEffect} from "react";
|
||||
import {connect} from "react-redux";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectCurrentUser} from "../../redux/user/user.selectors";
|
||||
import LandingPageStatic from "../../landing/index";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null)(LandingPage);
|
||||
|
||||
export function LandingPage({ currentUser }) {
|
||||
if (currentUser.authorized) return <Redirect to={"/manage"} />;
|
||||
export function LandingPage({currentUser}) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return <LandingPageStatic />;
|
||||
//return <Redirect to={"/signin"} />;
|
||||
useEffect(() => {
|
||||
if (currentUser.authorized) {
|
||||
navigate('/manage/jobs');
|
||||
}
|
||||
}, [currentUser, navigate]);
|
||||
|
||||
return <LandingPageStatic/>;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import React from "react";
|
||||
import React, {useEffect} from "react";
|
||||
//import DashboardGridComponent from "../../components/dashboard-grid/dashboard-grid.component";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
export default function ManageRootPageComponent() {
|
||||
//const client = useApolloClient();
|
||||
//const client = useApolloClient();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return <Redirect to={`/manage/jobs`} />;
|
||||
useEffect(() => {
|
||||
navigate('/manage/jobs');
|
||||
}, [navigate]);
|
||||
|
||||
return <div/>;
|
||||
// return (
|
||||
// <div>
|
||||
// <DashboardGridComponent />
|
||||
// </div>
|
||||
// );
|
||||
|
||||
// return (
|
||||
// <div>
|
||||
// <DashboardGridComponent />
|
||||
// </div>
|
||||
// );
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {setBreadcrumbs} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import ManageRootPageComponent from "./manage-root.page.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
});
|
||||
|
||||
export function ManageRootPageContainer({ setBreadcrumbs, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.manageroot");
|
||||
setBreadcrumbs([]);
|
||||
}, [t, setBreadcrumbs]);
|
||||
export function ManageRootPageContainer({setBreadcrumbs, bodyshop}) {
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.manageroot");
|
||||
setBreadcrumbs([]);
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return <ManageRootPageComponent />;
|
||||
return <ManageRootPageComponent/>;
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ManageRootPageContainer);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BackTop, Layout } from "antd";
|
||||
import {FloatButton, Layout} from "antd";
|
||||
import preval from "preval.macro";
|
||||
import React, { lazy, Suspense, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, Route, Switch } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {lazy, Suspense, useEffect, useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, Route, Routes} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
||||
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
||||
import ConflictComponent from "../../components/conflict/conflict.component";
|
||||
@@ -17,436 +17,400 @@ import PartnerPingComponent from "../../components/partner-ping/partner-ping.com
|
||||
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.page";
|
||||
import { requestForToken } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectInstanceConflict,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import {requestForToken} from "../../firebase/firebase.utils";
|
||||
import {selectBodyshop, selectInstanceConflict,} from "../../redux/user/user.selectors";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
||||
import "./manage.page.styles.scss";
|
||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||
|
||||
const ManageRootPage = lazy(() =>
|
||||
import("../manage-root/manage-root.page.container")
|
||||
);
|
||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||
|
||||
const CardPaymentModalContainer = lazy(() =>
|
||||
import("../../components/card-payment-modal/card-payment-modal.container.")
|
||||
import("../../components/card-payment-modal/card-payment-modal.container.")
|
||||
);
|
||||
|
||||
const JobsDetailPage = lazy(() =>
|
||||
import("../jobs-detail/jobs-detail.page.container")
|
||||
import("../jobs-detail/jobs-detail.page.container")
|
||||
);
|
||||
const InventoryListPage = lazy(() => import("../inventory/inventory.page"));
|
||||
const ProfilePage = lazy(() => import("../profile/profile.container.page"));
|
||||
const JobsAvailablePage = lazy(() =>
|
||||
import("../jobs-available/jobs-available.page.container")
|
||||
import("../jobs-available/jobs-available.page.container")
|
||||
);
|
||||
const ScheduleContainer = lazy(() =>
|
||||
import("../schedule/schedule.page.container")
|
||||
import("../schedule/schedule.page.container")
|
||||
);
|
||||
const VehiclesContainer = lazy(() =>
|
||||
import("../vehicles/vehicles.page.container")
|
||||
import("../vehicles/vehicles.page.container")
|
||||
);
|
||||
const VehiclesDetailContainer = lazy(() =>
|
||||
import("../vehicles-detail/vehicles-detail.page.container")
|
||||
import("../vehicles-detail/vehicles-detail.page.container")
|
||||
);
|
||||
const OwnersContainer = lazy(() => import("../owners/owners.page.container"));
|
||||
const OwnersDetailContainer = lazy(() =>
|
||||
import("../owners-detail/owners-detail.page.container")
|
||||
import("../owners-detail/owners-detail.page.container")
|
||||
);
|
||||
const ShopPage = lazy(() => import("../shop/shop.page.component"));
|
||||
const ShopVendorPageContainer = lazy(() =>
|
||||
import("../shop-vendor/shop-vendor.page.container")
|
||||
import("../shop-vendor/shop-vendor.page.container")
|
||||
);
|
||||
const EmailOverlayContainer = lazy(() =>
|
||||
import("../../components/email-overlay/email-overlay.container.jsx")
|
||||
import("../../components/email-overlay/email-overlay.container.jsx")
|
||||
);
|
||||
const JobsCreateContainerPage = lazy(() =>
|
||||
import("../jobs-create/jobs-create.container")
|
||||
import("../jobs-create/jobs-create.container")
|
||||
);
|
||||
const CourtesyCarCreateContainer = lazy(() =>
|
||||
import("../courtesy-car-create/courtesy-car-create.page.container")
|
||||
import("../courtesy-car-create/courtesy-car-create.page.container")
|
||||
);
|
||||
const CourtesyCarDetailContainer = lazy(() =>
|
||||
import("../courtesy-car-detail/courtesy-car-detail.page.container")
|
||||
import("../courtesy-car-detail/courtesy-car-detail.page.container")
|
||||
);
|
||||
const CourtesyCarsPage = lazy(() =>
|
||||
import("../courtesy-cars/courtesy-cars.page.container")
|
||||
import("../courtesy-cars/courtesy-cars.page.container")
|
||||
);
|
||||
const ContractCreatePage = lazy(() =>
|
||||
import("../contract-create/contract-create.page.container")
|
||||
import("../contract-create/contract-create.page.container")
|
||||
);
|
||||
const ContractDetailPage = lazy(() =>
|
||||
import("../contract-detail/contract-detail.page.container")
|
||||
import("../contract-detail/contract-detail.page.container")
|
||||
);
|
||||
const ContractsList = lazy(() =>
|
||||
import("../contracts/contracts.page.container")
|
||||
import("../contracts/contracts.page.container")
|
||||
);
|
||||
const BillsListPage = lazy(() => import("../bills/bills.page.container"));
|
||||
|
||||
const JobCostingModal = lazy(() =>
|
||||
import("../../components/job-costing-modal/job-costing-modal.container")
|
||||
import("../../components/job-costing-modal/job-costing-modal.container")
|
||||
);
|
||||
const ReportCenterModal = lazy(() =>
|
||||
import("../../components/report-center-modal/report-center-modal.container")
|
||||
import("../../components/report-center-modal/report-center-modal.container")
|
||||
);
|
||||
const BillEnterModalContainer = lazy(() =>
|
||||
import("../../components/bill-enter-modal/bill-enter-modal.container")
|
||||
import("../../components/bill-enter-modal/bill-enter-modal.container")
|
||||
);
|
||||
const TimeTicketModalContainer = lazy(() =>
|
||||
import("../../components/time-ticket-modal/time-ticket-modal.container")
|
||||
import("../../components/time-ticket-modal/time-ticket-modal.container")
|
||||
);
|
||||
const TimeTicketModalTask = lazy(() =>
|
||||
import(
|
||||
"../../components/time-ticket-task-modal/time-ticket-task-modal.container"
|
||||
)
|
||||
import(
|
||||
"../../components/time-ticket-task-modal/time-ticket-task-modal.container"
|
||||
)
|
||||
);
|
||||
const PaymentModalContainer = lazy(() =>
|
||||
import("../../components/payment-modal/payment-modal.container")
|
||||
import("../../components/payment-modal/payment-modal.container")
|
||||
);
|
||||
const ProductionListPage = lazy(() =>
|
||||
import("../production-list/production-list.container")
|
||||
import("../production-list/production-list.container")
|
||||
);
|
||||
const ProductionBoardPage = lazy(() =>
|
||||
import("../production-board/production-board.container")
|
||||
import("../production-board/production-board.container")
|
||||
);
|
||||
// const ShopTemplates = lazy(() =>
|
||||
// import("../shop-templates/shop-templates.container")
|
||||
// );
|
||||
const JobIntake = lazy(() =>
|
||||
import("../jobs-intake/jobs-intake.page.container")
|
||||
import("../jobs-intake/jobs-intake.page.container")
|
||||
);
|
||||
const JobChecklistView = lazy(() =>
|
||||
import("../jobs-checklist-view/jobs-checklist-view.page")
|
||||
import("../jobs-checklist-view/jobs-checklist-view.page")
|
||||
);
|
||||
const JobDeliver = lazy(() =>
|
||||
import("../jobs-deliver/jobs-delivery.page.container")
|
||||
import("../jobs-deliver/jobs-delivery.page.container")
|
||||
);
|
||||
const AccountingQboCallback = lazy(() =>
|
||||
import("../accounting-qbo/accounting-qbo.page")
|
||||
import("../accounting-qbo/accounting-qbo.page")
|
||||
);
|
||||
const AccountingReceivables = lazy(() =>
|
||||
import("../accounting-receivables/accounting-receivables.container")
|
||||
import("../accounting-receivables/accounting-receivables.container")
|
||||
);
|
||||
const AccountingPayables = lazy(() =>
|
||||
import("../accounting-payables/accounting-payables.container")
|
||||
import("../accounting-payables/accounting-payables.container")
|
||||
);
|
||||
const AccountingPayments = lazy(() =>
|
||||
import("../accounting-payments/accounting-payments.container")
|
||||
import("../accounting-payments/accounting-payments.container")
|
||||
);
|
||||
const AllJobs = lazy(() => import("../jobs-all/jobs-all.container"));
|
||||
const ReadyJobs = lazy(() => import("../jobs-ready/jobs-ready.page"));
|
||||
const JobsClose = lazy(() => import("../jobs-close/jobs-close.container"));
|
||||
const JobsAdmin = lazy(() => import("../jobs-admin/jobs-admin.page"));
|
||||
const TempDocs = lazy(() =>
|
||||
import("../temporary-docs/temporary-docs.container")
|
||||
import("../temporary-docs/temporary-docs.container")
|
||||
);
|
||||
|
||||
const ShopCsiPageContainer = lazy(() =>
|
||||
import("../shop-csi/shop-csi.container.page")
|
||||
import("../shop-csi/shop-csi.container.page")
|
||||
);
|
||||
const PaymentsAll = lazy(() =>
|
||||
import("../payments-all/payments-all.container.page")
|
||||
import("../payments-all/payments-all.container.page")
|
||||
);
|
||||
const ShiftClock = lazy(() => import("../shift-clock/shift-clock.page"));
|
||||
const Scoreboard = lazy(() =>
|
||||
import("../scoreboard/scoreboard.page.container")
|
||||
import("../scoreboard/scoreboard.page.container")
|
||||
);
|
||||
const TimeTicketsAll = lazy(() =>
|
||||
import("../time-tickets/time-tickets.container")
|
||||
import("../time-tickets/time-tickets.container")
|
||||
);
|
||||
const Help = lazy(() => import("../help/help.page"));
|
||||
const PartsQueue = lazy(() =>
|
||||
import("../parts-queue/parts-queue.page.container")
|
||||
import("../parts-queue/parts-queue.page.container")
|
||||
);
|
||||
const ExportLogs = lazy(() =>
|
||||
import("../export-logs/export-logs.page.container")
|
||||
import("../export-logs/export-logs.page.container")
|
||||
);
|
||||
const Phonebook = lazy(() => import("../phonebook/phonebook.page.container"));
|
||||
|
||||
const EmailTest = lazy(() =>
|
||||
import("../../components/email-test/email-test-component")
|
||||
import("../../components/email-test/email-test-component")
|
||||
);
|
||||
const Dashboard = lazy(() => import("../dashboard/dashboard.container"));
|
||||
const Dms = lazy(() => import("../dms/dms.container"));
|
||||
const DmsPayables = lazy(() =>
|
||||
import("../dms-payables/dms-payables.container")
|
||||
import("../dms-payables/dms-payables.container")
|
||||
);
|
||||
const ManageRootPage = lazy(() =>
|
||||
import("../manage-root/manage-root.page.container")
|
||||
);
|
||||
const TtApprovals = lazy(() =>
|
||||
import("../tt-approvals/tt-approvals.page.container")
|
||||
import("../tt-approvals/tt-approvals.page.container")
|
||||
);
|
||||
|
||||
const { Content, Footer } = Layout;
|
||||
const {Content, Footer} = Layout;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
conflict: selectInstanceConflict,
|
||||
bodyshop: selectBodyshop,
|
||||
conflict: selectInstanceConflict,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function Manage({ match, conflict, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
const widgetId = "mQdqARMzkZRUVugJ6TdS";
|
||||
window.noticeable.render("widget", widgetId);
|
||||
try {
|
||||
requestForToken();
|
||||
} catch (error) {
|
||||
console.log("Unable to request for token.", error);
|
||||
}
|
||||
}, []);
|
||||
export function Manage({conflict, bodyshop}) {
|
||||
const {t} = useTranslation();
|
||||
const [chatVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.app");
|
||||
}, [t]);
|
||||
|
||||
const AppRouteTable = (
|
||||
<Suspense
|
||||
fallback={<LoadingSpinner message={t("general.labels.loadingapp")} />}
|
||||
>
|
||||
<PaymentModalContainer />
|
||||
useEffect(() => {
|
||||
const widgetId = "mQdqARMzkZRUVugJ6TdS";
|
||||
window.noticeable.render("widget", widgetId);
|
||||
requestForToken().catch((error) => {
|
||||
console.error(`Unable to request for token.`, error)
|
||||
});
|
||||
}, []);
|
||||
|
||||
<CardPaymentModalContainer />
|
||||
useEffect(() => {
|
||||
document.title = t("titles.app");
|
||||
}, [t]);
|
||||
const AppRouteTable = (
|
||||
<Suspense
|
||||
fallback={<LoadingSpinner message={t("general.labels.loadingapp")}/>} This
|
||||
>
|
||||
<PaymentModalContainer/>
|
||||
|
||||
<BreadCrumbs />
|
||||
<BillEnterModalContainer />
|
||||
<JobCostingModal />
|
||||
<ReportCenterModal />
|
||||
<EmailOverlayContainer />
|
||||
<TimeTicketModalContainer />
|
||||
<TimeTicketModalTask />
|
||||
<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
|
||||
exact
|
||||
path={`${match.path}/jobs/:jobId/intake`}
|
||||
component={JobIntake}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/jobs/:jobId/deliver`}
|
||||
component={JobDeliver}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/jobs/:jobId/checklist`}
|
||||
component={JobChecklistView}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/jobs/:jobId/close`}
|
||||
component={JobsClose}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/jobs/:jobId/admin`}
|
||||
component={JobsAdmin}
|
||||
/>
|
||||
<Route exact path={`${match.path}/jobs/all`} component={AllJobs} />
|
||||
<Route exact path={`${match.path}/jobs/ready`} component={ReadyJobs} />
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/jobs/new`}
|
||||
component={JobsCreateContainerPage}
|
||||
/>
|
||||
<Route path={`${match.path}/jobs/:jobId`} component={JobsDetailPage} />
|
||||
</Switch>
|
||||
<Route exact path={`${match.path}/temporarydocs/`} component={TempDocs} />
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/inventory/`}
|
||||
component={InventoryListPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/courtesycars/`}
|
||||
component={CourtesyCarsPage}
|
||||
/>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/courtesycars/new`}
|
||||
component={CourtesyCarCreateContainer}
|
||||
/>
|
||||
<CardPaymentModalContainer/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/courtesycars/contracts`}
|
||||
component={ContractsList}
|
||||
/>
|
||||
<BreadCrumbs/>
|
||||
<BillEnterModalContainer/>
|
||||
<JobCostingModal/>
|
||||
<ReportCenterModal/>
|
||||
<EmailOverlayContainer/>
|
||||
<TimeTicketModalContainer/>
|
||||
<TimeTicketModalTask/>
|
||||
<PrintCenterModalContainer/>
|
||||
<Routes>
|
||||
<Route path='/_test' element={<TestComponent/>}/>
|
||||
<Route path='/' element={<ManageRootPage/>}/>
|
||||
<Route path='/jobs' element={<JobsPage/>}/>
|
||||
<Route
|
||||
path='/jobs/:jobId/intake'
|
||||
element={<JobIntake/>}
|
||||
/>
|
||||
<Route
|
||||
path='/jobs/:jobId/deliver'
|
||||
element={<JobDeliver/>}
|
||||
/>
|
||||
<Route
|
||||
path='/jobs/:jobId/checklist'
|
||||
element={<JobChecklistView/>}
|
||||
/>
|
||||
<Route
|
||||
path='/jobs/:jobId/close'
|
||||
element={<JobsClose/>}
|
||||
/>
|
||||
<Route
|
||||
path='/jobs/:jobId/admin'
|
||||
element={<JobsAdmin/>}
|
||||
/>
|
||||
<Route path='/jobs/all' element={<AllJobs/>}/>
|
||||
<Route path='/jobs/ready' element={<ReadyJobs/>}/>
|
||||
<Route
|
||||
path='/jobs/new'
|
||||
element={<JobsCreateContainerPage/>}
|
||||
/>
|
||||
<Route path='/jobs/:jobId' element={<JobsDetailPage/>}/>
|
||||
<Route path='/temporarydocs/' element={<TempDocs/>}/>
|
||||
<Route
|
||||
path='/inventory/'
|
||||
element={<InventoryListPage/>}
|
||||
/>
|
||||
<Route
|
||||
path='/courtesycars/'
|
||||
element={<CourtesyCarsPage/>}
|
||||
/>
|
||||
<Route
|
||||
path='/courtesycars/new'
|
||||
element={<CourtesyCarCreateContainer/>}
|
||||
/>
|
||||
<Route
|
||||
path='/courtesycars/contracts'
|
||||
element={<ContractsList/>}
|
||||
/>
|
||||
<Route
|
||||
path='/courtesycars/contracts/new'
|
||||
element={<ContractCreatePage/>}
|
||||
/>
|
||||
<Route
|
||||
path='/courtesycars/contracts/:contractId'
|
||||
element={<ContractDetailPage/>}
|
||||
/>
|
||||
<Route
|
||||
path='/courtesycars/:ccId'
|
||||
element={<CourtesyCarDetailContainer/>}
|
||||
/>
|
||||
<Route path='/profile' element={<ProfilePage/>}/>
|
||||
<Route
|
||||
path='/vehicles'
|
||||
element={<VehiclesContainer/>}
|
||||
/>
|
||||
<Route
|
||||
path='/production/list'
|
||||
element={<ProductionListPage/>}
|
||||
/>
|
||||
<Route
|
||||
path='/production/board'
|
||||
element={<ProductionBoardPage/>}
|
||||
/>
|
||||
<Route
|
||||
path='/vehicles/:vehId'
|
||||
element={<VehiclesDetailContainer/>}
|
||||
/>
|
||||
<Route path='/bills' element={<BillsListPage/>}/>
|
||||
<Route path='/owners' element={<OwnersContainer/>}/>
|
||||
<Route
|
||||
path='/owners/:ownerId'
|
||||
element={<OwnersDetailContainer/>}
|
||||
/>
|
||||
<Route
|
||||
path='/schedule'
|
||||
element={<ScheduleContainer/>}
|
||||
/>
|
||||
<Route
|
||||
path='/available'
|
||||
element={<JobsAvailablePage/>}
|
||||
/>
|
||||
<Route path='/shop' element={<ShopPage/>}/>
|
||||
{
|
||||
// <Route
|
||||
// path='/shop/templates'
|
||||
// element={<ShopTemplates />}
|
||||
// />
|
||||
}
|
||||
<Route
|
||||
path='/shop/vendors'
|
||||
element={<ShopVendorPageContainer/>}
|
||||
/>
|
||||
<Route
|
||||
path='/shop/csi'
|
||||
element={<ShopCsiPageContainer/>}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/courtesycars/contracts/new`}
|
||||
component={ContractCreatePage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/courtesycars/contracts/:contractId`}
|
||||
component={ContractDetailPage}
|
||||
/>
|
||||
<Route
|
||||
path='/accounting/qbo'
|
||||
element={<AccountingQboCallback/>}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/courtesycars/:ccId`}
|
||||
component={CourtesyCarDetailContainer}
|
||||
/>
|
||||
</Switch>
|
||||
<Route exact path={`${match.path}/profile`} component={ProfilePage} />
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/vehicles`}
|
||||
component={VehiclesContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/production/list`}
|
||||
component={ProductionListPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/production/board`}
|
||||
component={ProductionBoardPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/vehicles/:vehId`}
|
||||
component={VehiclesDetailContainer}
|
||||
/>
|
||||
<Route exact path={`${match.path}/bills`} component={BillsListPage} />
|
||||
<Route exact path={`${match.path}/owners`} component={OwnersContainer} />
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/owners/:ownerId`}
|
||||
component={OwnersDetailContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/schedule`}
|
||||
component={ScheduleContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/available`}
|
||||
component={JobsAvailablePage}
|
||||
/>
|
||||
<Route exact path={`${match.path}/shop/`} component={ShopPage} />
|
||||
{
|
||||
// <Route
|
||||
// exact
|
||||
// path={`${match.path}/shop/templates`}
|
||||
// component={ShopTemplates}
|
||||
// />
|
||||
}
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/shop/vendors`}
|
||||
component={ShopVendorPageContainer}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/shop/csi`}
|
||||
component={ShopCsiPageContainer}
|
||||
/>
|
||||
<Route
|
||||
path='/accounting/receivables'
|
||||
element={<AccountingReceivables/>}
|
||||
/>
|
||||
<Route
|
||||
path='/accounting/payables'
|
||||
element={<AccountingPayables/>}
|
||||
/>
|
||||
<Route
|
||||
path='/accounting/payments'
|
||||
element={<AccountingPayments/>}
|
||||
/>
|
||||
<Route
|
||||
path='/accounting/exportlogs'
|
||||
element={<ExportLogs/>}
|
||||
/>
|
||||
<Route path='/ttapprovals' element={<TtApprovals/>}/>
|
||||
<Route path='/partsqueue' element={<PartsQueue/>}/>
|
||||
<Route path='/phonebook' element={<Phonebook/>}/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/accounting/qbo`}
|
||||
component={AccountingQboCallback}
|
||||
/>
|
||||
<Route path='/payments' element={<PaymentsAll/>}/>
|
||||
<Route path='/shiftclock' element={<ShiftClock/>}/>
|
||||
<Route path='/scoreboard' element={<Scoreboard/>}/>
|
||||
<Route
|
||||
path='/timetickets'
|
||||
element={<TimeTicketsAll/>}
|
||||
/>
|
||||
<Route path='/help' element={<Help/>}/>
|
||||
<Route path='/emailtest' element={<EmailTest/>}/>
|
||||
<Route path='/dashboard' element={<Dashboard/>}/>
|
||||
<Route path='/dms' element={<Dms/>}/>
|
||||
<Route path='/dmsap' element={<DmsPayables/>}/>
|
||||
</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/accounting/receivables`}
|
||||
component={AccountingReceivables}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/accounting/payables`}
|
||||
component={AccountingPayables}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/accounting/payments`}
|
||||
component={AccountingPayments}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/accounting/exportlogs`}
|
||||
component={ExportLogs}
|
||||
/>
|
||||
<Route exact path={`${match.path}/ttapprovals`} component={TtApprovals} />
|
||||
<Route exact path={`${match.path}/partsqueue`} component={PartsQueue} />
|
||||
<Route exact path={`${match.path}/phonebook`} component={Phonebook} />
|
||||
let PageContent;
|
||||
|
||||
<Route exact path={`${match.path}/payments`} component={PaymentsAll} />
|
||||
<Route exact path={`${match.path}/shiftclock`} component={ShiftClock} />
|
||||
<Route exact path={`${match.path}/scoreboard`} component={Scoreboard} />
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/timetickets`}
|
||||
component={TimeTicketsAll}
|
||||
/>
|
||||
<Route exact path={`${match.path}/help`} component={Help} />
|
||||
<Route exact path={`${match.path}/emailtest`} component={EmailTest} />
|
||||
<Route exact path={`${match.path}/dashboard`} component={Dashboard} />
|
||||
<Route exact path={`${match.path}/dms`} component={Dms} />
|
||||
<Route exact path={`${match.path}/dmsap`} component={DmsPayables} />
|
||||
</Suspense>
|
||||
);
|
||||
if (conflict) PageContent = <ConflictComponent/>;
|
||||
else if (bodyshop && bodyshop.sub_status !== "active")
|
||||
PageContent = <ShopSubStatusComponent/>;
|
||||
else PageContent = AppRouteTable;
|
||||
|
||||
let PageContent;
|
||||
return (
|
||||
<>
|
||||
<ChatAffixContainer bodyshop={bodyshop} chatVisible={chatVisible}/>
|
||||
<Layout style={{minHeight: '100vh'}} className="layout-container">
|
||||
<UpdateAlert/>
|
||||
<HeaderContainer/>
|
||||
<Content className="content-container">
|
||||
<PartnerPingComponent/>
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundary/>} showDialog>
|
||||
{PageContent}
|
||||
</Sentry.ErrorBoundary>
|
||||
|
||||
if (conflict) PageContent = <ConflictComponent />;
|
||||
else if (bodyshop && bodyshop.sub_status !== "active")
|
||||
PageContent = <ShopSubStatusComponent />;
|
||||
else PageContent = AppRouteTable;
|
||||
<FloatButton.BackTop style={{right: 100, bottom: 25}}/>
|
||||
|
||||
return (
|
||||
<>
|
||||
<ChatAffixContainer />
|
||||
<Layout className="layout-container">
|
||||
<UpdateAlert />
|
||||
<HeaderContainer />
|
||||
|
||||
<Content className="content-container">
|
||||
<PartnerPingComponent />
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundary />} showDialog>
|
||||
{PageContent}
|
||||
</Sentry.ErrorBoundary>
|
||||
|
||||
<BackTop />
|
||||
<Footer>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
margin: "1rem 0rem",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div>
|
||||
{`${t("titles.app")} ${
|
||||
process.env.REACT_APP_GIT_SHA
|
||||
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`}
|
||||
</div>
|
||||
<div id="noticeable-widget" style={{ marginLeft: "1rem" }} />
|
||||
</div>
|
||||
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
|
||||
Disclaimer & Notices
|
||||
</Link>
|
||||
</div>
|
||||
</Footer>
|
||||
</Content>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
</Content>
|
||||
<Footer>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
margin: "1rem 0rem",
|
||||
}}
|
||||
>
|
||||
<div style={{display: "flex"}}>
|
||||
<div>
|
||||
{`${t("titles.app")} ${
|
||||
process.env.REACT_APP_GIT_SHA
|
||||
} - ${preval`module.exports = new Date().toLocaleString("en-US", {timeZone: "America/Los_Angeles"});`}`}
|
||||
</div>
|
||||
<div id="noticeable-widget" style={{marginLeft: "1rem"}}/>
|
||||
</div>
|
||||
<Link to="/disclaimer" target="_blank" style={{color: "#ccc"}}>
|
||||
Disclaimer & Notices
|
||||
</Link>
|
||||
</div>
|
||||
</Footer>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(Manage);
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
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 {QUERY_BODYSHOP} from "../../graphql/bodyshop.queries";
|
||||
import {setBodyshop} from "../../redux/user/user.actions";
|
||||
//import "../../utils/RegisterSw";
|
||||
import ManagePage from "./manage.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
|
||||
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
|
||||
});
|
||||
|
||||
function ManagePageContainer({ match, setBodyshop }) {
|
||||
const { loading, error, data } = useQuery(QUERY_BODYSHOP, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
function ManagePageContainer({setBodyshop}) {
|
||||
const {loading, error, data} = useQuery(QUERY_BODYSHOP, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setBodyshop(data.bodyshops[0] || { notfound: true });
|
||||
}
|
||||
}, [data, setBodyshop]);
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
setBodyshop(data.bodyshops[0] || {notfound: true});
|
||||
}
|
||||
}, [data, setBodyshop]);
|
||||
|
||||
if (loading)
|
||||
return <LoadingSpinner message={t("general.labels.loadingshop")} />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (loading)
|
||||
return <LoadingSpinner message={t("general.labels.loadingshop")}/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
return <ManagePage match={match} />;
|
||||
return <ManagePage/>;
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ManagePageContainer);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Col, Divider, Row } from "antd";
|
||||
import {Col, Divider, Row} from "antd";
|
||||
import React from "react";
|
||||
import OwnerDetailForm from "../../components/owner-detail-form/owner-detail-form.container";
|
||||
import OwnerDetailJobsComponent from "../../components/owner-detail-jobs/owner-detail-jobs.component";
|
||||
|
||||
export default function OwnersDetailComponent({ owner, refetch }) {
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<OwnerDetailForm owner={owner} refetch={refetch} />
|
||||
</Col>
|
||||
<Divider type="horizontal" />
|
||||
<Col span={24}>
|
||||
<OwnerDetailJobsComponent owner={owner} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
export default function OwnersDetailComponent({owner, refetch}) {
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<OwnerDetailForm owner={owner} refetch={refetch}/>
|
||||
</Col>
|
||||
<Divider type="horizontal"/>
|
||||
<Col span={24}>
|
||||
<OwnerDetailJobsComponent owner={owner}/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,76 +1,73 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useParams} from 'react-router-dom';
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_OWNER_BY_ID } from "../../graphql/owners.queries";
|
||||
import {
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import {QUERY_OWNER_BY_ID} from "../../graphql/owners.queries";
|
||||
import {addRecentItem, setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {CreateRecentItem} from "../../utils/create-recent-item";
|
||||
import OwnersDetailComponent from "./owners-detail.page.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
import {OwnerNameDisplayFunction} from "../../components/owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function OwnersDetailContainer({
|
||||
match,
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { ownerId } = match.params;
|
||||
const { t } = useTranslation();
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {ownerId} = useParams();
|
||||
const {t} = useTranslation();
|
||||
|
||||
const { loading, data, error, refetch } = useQuery(QUERY_OWNER_BY_ID, {
|
||||
variables: { id: ownerId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.owners-detail", {
|
||||
name: data ? OwnerNameDisplayFunction(data.owners_by_pk) : "",
|
||||
const {loading, data, error, refetch} = useQuery(QUERY_OWNER_BY_ID, {
|
||||
variables: {id: ownerId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
setSelectedHeader("owners");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/owners", label: t("titles.bc.owners") },
|
||||
{
|
||||
link: `/manage/owners/${ownerId}`,
|
||||
label: t("titles.bc.owner-detail", {
|
||||
name: data ? OwnerNameDisplayFunction(data.owners_by_pk) : "",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
if (data && data.owners_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
ownerId,
|
||||
"owner",
|
||||
OwnerNameDisplayFunction(data.owners_by_pk),
|
||||
`/manage/owners/${ownerId}`
|
||||
)
|
||||
);
|
||||
}, [setBreadcrumbs, t, data, ownerId, addRecentItem, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.owners-detail", {
|
||||
name: data ? OwnerNameDisplayFunction(data.owners_by_pk) : "",
|
||||
});
|
||||
setSelectedHeader("owners");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/owners", label: t("titles.bc.owners")},
|
||||
{
|
||||
link: `/manage/owners/${ownerId}`,
|
||||
label: t("titles.bc.owner-detail", {
|
||||
name: data ? OwnerNameDisplayFunction(data.owners_by_pk) : "",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (!!!data.owners_by_pk) return <NotFound />;
|
||||
if (data && data.owners_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
ownerId,
|
||||
"owner",
|
||||
OwnerNameDisplayFunction(data.owners_by_pk),
|
||||
`/manage/owners/${ownerId}`
|
||||
)
|
||||
);
|
||||
}, [setBreadcrumbs, t, data, ownerId, addRecentItem, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="owners:detail">
|
||||
<OwnersDetailComponent owner={data.owners_by_pk} refetch={refetch} />
|
||||
</RbacWrapper>
|
||||
);
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
if (!!!data.owners_by_pk) return <NotFound/>;
|
||||
|
||||
return (
|
||||
<RbacWrapper action="owners:detail">
|
||||
<OwnersDetailComponent owner={data.owners_by_pk} refetch={refetch}/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(OwnersDetailContainer);
|
||||
|
||||
@@ -2,5 +2,5 @@ import React from "react";
|
||||
import OwnersListContainer from "../../components/owners-list/owners-list.container";
|
||||
|
||||
export default function OwnersPageComponent() {
|
||||
return <OwnersListContainer />;
|
||||
return <OwnersListContainer/>;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, {useEffect} from "react";
|
||||
import OwnersPageComponent from "./owners.page.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { connect } from "react-redux";
|
||||
import {connect} from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function OwnersPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.owners");
|
||||
setSelectedHeader("owners");
|
||||
setBreadcrumbs([{ link: "/manage/owners", label: t("titles.bc.owners") }]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
export function OwnersPageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.owners");
|
||||
setSelectedHeader("owners");
|
||||
setBreadcrumbs([{link: "/manage/owners", label: t("titles.bc.owners")}]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="owners:list">
|
||||
<OwnersPageComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="owners:list">
|
||||
<OwnersPageComponent/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(OwnersPageContainer);
|
||||
|
||||
@@ -1,315 +1,317 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Card, Input, Space, Table } from "antd";
|
||||
import {SyncOutlined} from "@ant-design/icons";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Button, Card, Input, Space, Table} from "antd";
|
||||
import _ from "lodash";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
|
||||
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
|
||||
import JobRemoveFromPartsQueue
|
||||
from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
|
||||
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
|
||||
import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component";
|
||||
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import ProductionListColumnComment
|
||||
from "../../components/production-list-columns/production-list-columns.comment.component";
|
||||
import {QUERY_PARTS_QUEUE} from "../../graphql/jobs.queries";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {DateTimeFormatter, TimeAgoFormatter} from "../../utils/DateFormatter";
|
||||
import {alphaSort, dateSort} from "../../utils/sorters";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function PartsQueuePageComponent({ bodyshop }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {
|
||||
//page,
|
||||
sortcolumn,
|
||||
sortorder,
|
||||
statusFilters,
|
||||
} = searchParams;
|
||||
const history = useHistory();
|
||||
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
|
||||
export function PartsQueuePageComponent({bodyshop}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {
|
||||
//page,
|
||||
sortcolumn,
|
||||
sortorder,
|
||||
statusFilters,
|
||||
} = searchParams;
|
||||
const history = useNavigate();
|
||||
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_PARTS_QUEUE, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
// offset: page ? (page - 1) * 25 : 0,
|
||||
// limit: 25,
|
||||
statuses: (statusFilters && JSON.parse(statusFilters)) ||
|
||||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "ro_number"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
const {loading, error, data, refetch} = useQuery(QUERY_PARTS_QUEUE, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
// offset: page ? (page - 1) * 25 : 0,
|
||||
// limit: 25,
|
||||
statuses: (statusFilters && JSON.parse(statusFilters)) ||
|
||||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "ro_number"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const {t} = useTranslation();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
// searchParams.page = pagination.current;
|
||||
searchParams.sortcolumn = sorter.columnKey;
|
||||
searchParams.sortorder = sorter.order;
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
// searchParams.page = pagination.current;
|
||||
searchParams.sortcolumn = sorter.columnKey;
|
||||
searchParams.sortorder = sorter.order;
|
||||
|
||||
if (filters.status) {
|
||||
searchParams.statusFilters = JSON.stringify(
|
||||
_.flattenDeep(filters.status)
|
||||
);
|
||||
} else {
|
||||
delete searchParams.statusFilters;
|
||||
}
|
||||
setFilter(filters);
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
};
|
||||
if (filters.status) {
|
||||
searchParams.statusFilters = JSON.stringify(
|
||||
_.flattenDeep(filters.status)
|
||||
);
|
||||
} else {
|
||||
delete searchParams.statusFilters;
|
||||
}
|
||||
setFilter(filters);
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
||||
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/jobs/" + record.id}>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "ownr_ln",
|
||||
key: "ownr_ln",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link to={"/manage/owners/" + record.ownerid}>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/jobs/" + record.id}>
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "ownr_ln",
|
||||
key: "ownr_ln",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link to={"/manage/owners/" + record.ownerid}>
|
||||
<OwnerNameDisplay ownerObject={record}/>
|
||||
</Link>
|
||||
) : (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record}/>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sortOrder: sortcolumn === "status" && sortorder,
|
||||
filteredValue: statusFilters ? JSON.parse(statusFilters) : null,
|
||||
filters:
|
||||
bodyshop.md_ro_statuses.active_statuses.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
}) || [],
|
||||
render: (text, record) => {
|
||||
return record.status || t("general.labels.na");
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.scheduled_in"),
|
||||
dataIndex: "scheduled_in",
|
||||
key: "scheduled_in",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => dateSort(a.scheduled_in, b.scheduled_in),
|
||||
sortOrder: sortcolumn === "scheduled_in" && sortorder,
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.scheduled_in}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: t("vehicles.fields.plate_no"),
|
||||
// dataIndex: "plate_no",
|
||||
// key: "plate_no",
|
||||
// sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
// sortOrder: sortcolumn === "plate_no" && sortorder,
|
||||
// render: (text, record) => {
|
||||
// return record.plate_no ? record.plate_no : "";
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder: sortcolumn === "clm_no" && sortorder,
|
||||
},
|
||||
// {
|
||||
// title: t("jobs.fields.clm_total"),
|
||||
// dataIndex: "clm_total",
|
||||
// key: "clm_total",
|
||||
// sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
// sortOrder: sortcolumn === "clm_total" && sortorder,
|
||||
// render: (text, record) => {
|
||||
// return record.clm_total ? (
|
||||
// <CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
// ) : (
|
||||
// t("general.labels.unknown")
|
||||
// );
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: t("jobs.fields.updated_at"),
|
||||
dataIndex: "updated_at",
|
||||
key: "updated_at",
|
||||
sorter: (a, b) => dateSort(a.updated_at, b.updated_at),
|
||||
sortOrder: sortcolumn === "updated_at" && sortorder,
|
||||
render: (text, record) => (
|
||||
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.partsstatus"),
|
||||
dataIndex: "partsstatus",
|
||||
key: "partsstatus",
|
||||
render: (text, record) => (
|
||||
<JobPartsQueueCount
|
||||
style={{ minWidth: "10rem" }}
|
||||
parts={record.joblines_status}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
render: (text, record) => <ProductionListColumnComment record={record} />,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.queued_for_parts"),
|
||||
dataIndex: "queued_for_parts",
|
||||
key: "queued_for_parts",
|
||||
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
|
||||
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
|
||||
filteredValue: filter?.queued_for_parts || null,
|
||||
filters: [
|
||||
{
|
||||
text: "Queued",
|
||||
value: true,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Unqueued",
|
||||
value: false,
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sortOrder: sortcolumn === "status" && sortorder,
|
||||
filteredValue: statusFilters ? JSON.parse(statusFilters) : null,
|
||||
filters:
|
||||
bodyshop.md_ro_statuses.active_statuses.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
}) || [],
|
||||
render: (text, record) => {
|
||||
return record.status || t("general.labels.na");
|
||||
},
|
||||
},
|
||||
],
|
||||
onFilter: (value, record) => record.queued_for_parts === value,
|
||||
render: (text, record) => (
|
||||
<JobRemoveFromPartsQueue
|
||||
checked={record.queued_for_parts}
|
||||
jobId={record.id}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
<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
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: pageLimit,
|
||||
// current: parseInt(page || 1),
|
||||
// total: data && data.jobs_aggregate.aggregate.count,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
style={{ height: "100%" }}
|
||||
scroll={{ x: true }}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
{
|
||||
title: t("jobs.fields.scheduled_in"),
|
||||
dataIndex: "scheduled_in",
|
||||
key: "scheduled_in",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => dateSort(a.scheduled_in, b.scheduled_in),
|
||||
sortOrder: sortcolumn === "scheduled_in" && sortorder,
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.scheduled_in}</DateTimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => {
|
||||
return record.vehicleid ? (
|
||||
<Link to={"/manage/vehicles/" + record.vehicleid}>
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: t("vehicles.fields.plate_no"),
|
||||
// dataIndex: "plate_no",
|
||||
// key: "plate_no",
|
||||
// sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
|
||||
// sortOrder: sortcolumn === "plate_no" && sortorder,
|
||||
// render: (text, record) => {
|
||||
// return record.plate_no ? record.plate_no : "";
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder: sortcolumn === "clm_no" && sortorder,
|
||||
},
|
||||
// {
|
||||
// title: t("jobs.fields.clm_total"),
|
||||
// dataIndex: "clm_total",
|
||||
// key: "clm_total",
|
||||
// sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
// sortOrder: sortcolumn === "clm_total" && sortorder,
|
||||
// render: (text, record) => {
|
||||
// return record.clm_total ? (
|
||||
// <CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
// ) : (
|
||||
// t("general.labels.unknown")
|
||||
// );
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: t("jobs.fields.updated_at"),
|
||||
dataIndex: "updated_at",
|
||||
key: "updated_at",
|
||||
sorter: (a, b) => dateSort(a.updated_at, b.updated_at),
|
||||
sortOrder: sortcolumn === "updated_at" && sortorder,
|
||||
render: (text, record) => (
|
||||
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.partsstatus"),
|
||||
dataIndex: "partsstatus",
|
||||
key: "partsstatus",
|
||||
render: (text, record) => (
|
||||
<JobPartsQueueCount
|
||||
style={{minWidth: "10rem"}}
|
||||
parts={record.joblines_status}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
render: (text, record) => <ProductionListColumnComment record={record}/>,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.queued_for_parts"),
|
||||
dataIndex: "queued_for_parts",
|
||||
key: "queued_for_parts",
|
||||
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
|
||||
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
|
||||
filteredValue: filter?.queued_for_parts || null,
|
||||
filters: [
|
||||
{
|
||||
text: "Queued",
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
text: "Unqueued",
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
onFilter: (value, record) => record.queued_for_parts === value,
|
||||
render: (text, record) => (
|
||||
<JobRemoveFromPartsQueue
|
||||
checked={record.queued_for_parts}
|
||||
jobId={record.id}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
<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
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: pageLimit,
|
||||
// current: parseInt(page || 1),
|
||||
// total: data && data.jobs_aggregate.aggregate.count,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={jobs}
|
||||
style={{height: "100%"}}
|
||||
scroll={{x: true}}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(PartsQueuePageComponent);
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import PartsQueuePage from "./parts-queue.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function PartsQueuePageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function PartsQueuePageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.parts-queue");
|
||||
setSelectedHeader("parts-queue");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/partsqueue", label: t("titles.bc.parts-queue") },
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.parts-queue");
|
||||
setSelectedHeader("parts-queue");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/partsqueue", label: t("titles.bc.parts-queue")},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="jobs:partsqueue">
|
||||
<PartsQueuePage />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="jobs:partsqueue">
|
||||
<PartsQueuePage/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(PartsQueuePageContainer);
|
||||
|
||||
@@ -1,78 +1,75 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import PaymentsListPaginated from "../../components/payments-list-paginated/payment-list-paginated.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_ALL_PAYMENTS_PAGINATED } from "../../graphql/payments.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {QUERY_ALL_PAYMENTS_PAGINATED} from "../../graphql/payments.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function AllJobs({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder, searchObj } = searchParams;
|
||||
export function AllJobs({bodyshop, setBreadcrumbs, setSelectedHeader}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder, searchObj} = searchParams;
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_ALL_PAYMENTS_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
searchObj
|
||||
? JSON.parse(searchObj)
|
||||
: {
|
||||
[sortcolumn || "date"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_ALL_PAYMENTS_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
searchObj
|
||||
? JSON.parse(searchObj)
|
||||
: {
|
||||
[sortcolumn || "date"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.payments-all");
|
||||
setSelectedHeader("allpayments");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/payments", label: t("titles.bc.payments-all") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.payments-all");
|
||||
setSelectedHeader("allpayments");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/payments", label: t("titles.bc.payments-all")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<RbacWrapper action="payments:list">
|
||||
<PaymentsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.payments_aggregate.aggregate.count : 0}
|
||||
payments={data ? data.payments : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
return (
|
||||
<RbacWrapper action="payments:list">
|
||||
<PaymentsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.payments_aggregate.aggregate.count : 0}
|
||||
payments={data ? data.payments : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AllJobs);
|
||||
|
||||
@@ -1,219 +1,216 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Card, Input, Space, Table, Typography } from "antd";
|
||||
import {SyncOutlined} from "@ant-design/icons";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Button, Card, Input, Space, Table, Typography} from "antd";
|
||||
import _ from "lodash";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_PHONEBOOK_PAGINATED } from "../../graphql/phonebook.queries";
|
||||
import {
|
||||
selectAuthLevel,
|
||||
selectBodyshop,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import {QUERY_PHONEBOOK_PAGINATED} from "../../graphql/phonebook.queries";
|
||||
import {selectAuthLevel, selectBodyshop,} from "../../redux/user/user.selectors";
|
||||
import ChatOpenButton from "../../components/chat-open-button/chat-open-button.component";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import { HasRbacAccess } from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {alphaSort} from "../../utils/sorters";
|
||||
import {HasRbacAccess} from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
authLevel: selectAuthLevel,
|
||||
bodyshop: selectBodyshop,
|
||||
authLevel: selectAuthLevel,
|
||||
});
|
||||
|
||||
export function PhonebookPageComponent({ bodyshop, authLevel }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder, search, phonebookentry } = searchParams;
|
||||
const history = useHistory();
|
||||
export function PhonebookPageComponent({bodyshop, authLevel}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder, search, phonebookentry} = searchParams;
|
||||
const history = useNavigate();
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_PHONEBOOK_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
search: search || "",
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "lastname"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "asc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
searchParams.page = pagination.current;
|
||||
searchParams.sortcolumn = sorter.columnKey;
|
||||
searchParams.sortorder = sorter.order;
|
||||
if (filters.status) {
|
||||
searchParams.statusFilters = JSON.stringify(
|
||||
_.flattenDeep(filters.status)
|
||||
);
|
||||
} else {
|
||||
delete searchParams.statusFilters;
|
||||
}
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("phonebook.fields.firstname"),
|
||||
dataIndex: "firstname",
|
||||
key: "firstname",
|
||||
sorter: (a, b) => alphaSort(a.firstname, b.firstname),
|
||||
sortOrder: sortcolumn === "firstname" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.lastname"),
|
||||
dataIndex: "lastname",
|
||||
key: "lastname",
|
||||
sorter: (a, b) => alphaSort(a.lastname, b.lastname),
|
||||
sortOrder: sortcolumn === "lastname" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.company"),
|
||||
dataIndex: "company",
|
||||
key: "company",
|
||||
sorter: (a, b) => alphaSort(a.company, b.company),
|
||||
sortOrder: sortcolumn === "company" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.category"),
|
||||
dataIndex: "category",
|
||||
key: "category",
|
||||
sorter: (a, b) => alphaSort(a.category, b.category),
|
||||
sortOrder: sortcolumn === "category" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.email"),
|
||||
dataIndex: "email",
|
||||
key: "email",
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.phone1"),
|
||||
dataIndex: "phone1",
|
||||
key: "phone1",
|
||||
render: (text, record) => <ChatOpenButton phone={text} />,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.phone2"),
|
||||
dataIndex: "phone2",
|
||||
key: "phone2",
|
||||
render: (text, record) => <ChatOpenButton phone={text} />,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.address1"),
|
||||
dataIndex: "address1",
|
||||
key: "address1",
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.city"),
|
||||
dataIndex: "city",
|
||||
key: "city",
|
||||
},
|
||||
];
|
||||
|
||||
const handleNewPhonebook = () => {
|
||||
searchParams.phonebookentry = "new";
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
};
|
||||
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
searchParams.phonebookentry = record.id;
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
} else {
|
||||
delete searchParams.phonebookentry;
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
}
|
||||
};
|
||||
const hasNoAccess = !HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel,
|
||||
action: "phonebook:edit",
|
||||
});
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
{searchParams.search && (
|
||||
<>
|
||||
<Typography.Title level={4}>
|
||||
{t("general.labels.searchresults", {
|
||||
search: searchParams.search,
|
||||
})}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
onClick={() => {
|
||||
delete searchParams.search;
|
||||
searchParams.page = 1;
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
}}
|
||||
>
|
||||
{t("general.actions.clear")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button disabled={hasNoAccess} onClick={handleNewPhonebook}>
|
||||
{t("phonebook.actions.new")}
|
||||
</Button>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={searchParams.search || t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
searchParams.search = value;
|
||||
searchParams.page = 1;
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: data && data.search_phonebook_aggregate.aggregate.count,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={data && data.search_phonebook}
|
||||
//scroll={{ x: true }}
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelect: handleOnRowClick,
|
||||
type: "radio",
|
||||
selectedRowKeys: [phonebookentry],
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_PHONEBOOK_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
search: search || "",
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "lastname"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
: "asc"
|
||||
: "asc",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const {t} = useTranslation();
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
searchParams.page = pagination.current;
|
||||
searchParams.sortcolumn = sorter.columnKey;
|
||||
searchParams.sortorder = sorter.order;
|
||||
if (filters.status) {
|
||||
searchParams.statusFilters = JSON.stringify(
|
||||
_.flattenDeep(filters.status)
|
||||
);
|
||||
} else {
|
||||
delete searchParams.statusFilters;
|
||||
}
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("phonebook.fields.firstname"),
|
||||
dataIndex: "firstname",
|
||||
key: "firstname",
|
||||
sorter: (a, b) => alphaSort(a.firstname, b.firstname),
|
||||
sortOrder: sortcolumn === "firstname" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.lastname"),
|
||||
dataIndex: "lastname",
|
||||
key: "lastname",
|
||||
sorter: (a, b) => alphaSort(a.lastname, b.lastname),
|
||||
sortOrder: sortcolumn === "lastname" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.company"),
|
||||
dataIndex: "company",
|
||||
key: "company",
|
||||
sorter: (a, b) => alphaSort(a.company, b.company),
|
||||
sortOrder: sortcolumn === "company" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.category"),
|
||||
dataIndex: "category",
|
||||
key: "category",
|
||||
sorter: (a, b) => alphaSort(a.category, b.category),
|
||||
sortOrder: sortcolumn === "category" && sortorder,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.email"),
|
||||
dataIndex: "email",
|
||||
key: "email",
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.phone1"),
|
||||
dataIndex: "phone1",
|
||||
key: "phone1",
|
||||
render: (text, record) => <ChatOpenButton phone={text}/>,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.phone2"),
|
||||
dataIndex: "phone2",
|
||||
key: "phone2",
|
||||
render: (text, record) => <ChatOpenButton phone={text}/>,
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.address1"),
|
||||
dataIndex: "address1",
|
||||
key: "address1",
|
||||
},
|
||||
{
|
||||
title: t("phonebook.fields.city"),
|
||||
dataIndex: "city",
|
||||
key: "city",
|
||||
},
|
||||
];
|
||||
|
||||
const handleNewPhonebook = () => {
|
||||
searchParams.phonebookentry = "new";
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
};
|
||||
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
searchParams.phonebookentry = record.id;
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
} else {
|
||||
delete searchParams.phonebookentry;
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
}
|
||||
};
|
||||
const hasNoAccess = !HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel,
|
||||
action: "phonebook:edit",
|
||||
});
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
{searchParams.search && (
|
||||
<>
|
||||
<Typography.Title level={4}>
|
||||
{t("general.labels.searchresults", {
|
||||
search: searchParams.search,
|
||||
})}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
onClick={() => {
|
||||
delete searchParams.search;
|
||||
searchParams.page = 1;
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
}}
|
||||
>
|
||||
{t("general.actions.clear")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button disabled={hasNoAccess} onClick={handleNewPhonebook}>
|
||||
{t("phonebook.actions.new")}
|
||||
</Button>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder={searchParams.search || t("general.labels.search")}
|
||||
onSearch={(value) => {
|
||||
searchParams.search = value;
|
||||
searchParams.page = 1;
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
position: "top",
|
||||
pageSize: pageLimit,
|
||||
current: parseInt(page || 1),
|
||||
total: data && data.search_phonebook_aggregate.aggregate.count,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={data && data.search_phonebook}
|
||||
//scroll={{ x: true }}
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelect: handleOnRowClick,
|
||||
type: "radio",
|
||||
selectedRowKeys: [phonebookentry],
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: (event) => {
|
||||
handleOnRowClick(record);
|
||||
},
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(PhonebookPageComponent);
|
||||
|
||||
@@ -1,68 +1,69 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import PhonebookPage from "./phonebook.page.component";
|
||||
import { Drawer, Grid } from "antd";
|
||||
import queryString from "query-string";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import {Drawer, Grid} from "antd";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import PhonebookFormContainer from "../../components/phonebook-form/phonebook-form.container";
|
||||
import queryString from "query-string";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function PhonebookContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function PhonebookContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.phonebook");
|
||||
setSelectedHeader("phonebook");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/phonebook",
|
||||
label: t("titles.bc.phonebook"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const { phonebookentry } = search;
|
||||
const history = useHistory();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.phonebook");
|
||||
setSelectedHeader("phonebook");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/phonebook",
|
||||
label: t("titles.bc.phonebook"),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, setSelectedHeader]);
|
||||
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const {phonebookentry} = search;
|
||||
|
||||
const bpoints = {
|
||||
xs: "100%",
|
||||
sm: "100%",
|
||||
md: "100%",
|
||||
lg: "50%",
|
||||
xl: "50%",
|
||||
xxl: "45%",
|
||||
};
|
||||
const drawerPercentage = selectedBreakpoint
|
||||
? bpoints[selectedBreakpoint[0]]
|
||||
: "100%";
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<RbacWrapper action="phonebook:view">
|
||||
<PhonebookPage />
|
||||
<Drawer
|
||||
width={drawerPercentage}
|
||||
onClose={() => {
|
||||
delete search.phonebookentry;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}}
|
||||
visible={phonebookentry}
|
||||
>
|
||||
<PhonebookFormContainer />
|
||||
</Drawer>
|
||||
</RbacWrapper>
|
||||
);
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
|
||||
const bpoints = {
|
||||
xs: "100%",
|
||||
sm: "100%",
|
||||
md: "100%",
|
||||
lg: "50%",
|
||||
xl: "50%",
|
||||
xxl: "45%",
|
||||
};
|
||||
const drawerPercentage = selectedBreakpoint
|
||||
? bpoints[selectedBreakpoint[0]]
|
||||
: "100%";
|
||||
|
||||
return (
|
||||
(<RbacWrapper action="phonebook:view">
|
||||
<PhonebookPage/>
|
||||
<Drawer
|
||||
width={drawerPercentage}
|
||||
onClose={() => {
|
||||
delete search.phonebookentry;
|
||||
navigate({search: queryString.stringify(search)});
|
||||
}}
|
||||
open={phonebookentry}
|
||||
>
|
||||
<PhonebookFormContainer/>
|
||||
</Drawer>
|
||||
</RbacWrapper>)
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(PhonebookContainer);
|
||||
|
||||
export default connect(null, mapDispatchToProps)(PhonebookContainer);
|
||||
@@ -2,5 +2,5 @@ import React from "react";
|
||||
import ProductionBoardKanbanContainer from "../../components/production-board-kanban/production-board-kanban.container";
|
||||
|
||||
export default function ProductionBoardComponent() {
|
||||
return <ProductionBoardKanbanContainer />;
|
||||
return <ProductionBoardKanbanContainer/>;
|
||||
}
|
||||
|
||||
@@ -1,52 +1,50 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import ProductionBoardComponent from "./production-board.component";
|
||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ProductionBoardContainer({
|
||||
setBreadcrumbs,
|
||||
bodyshop,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
setBreadcrumbs,
|
||||
bodyshop,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.productionboard");
|
||||
setSelectedHeader("productionboard");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/production/board",
|
||||
label: t("titles.bc.productionboard"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.productionboard");
|
||||
setSelectedHeader("productionboard");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/production/board",
|
||||
label: t("titles.bc.productionboard"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<FeatureWrapper featureName="production-board">
|
||||
<RbacWrapper action="production:board">
|
||||
<ProductionBoardComponent />
|
||||
</RbacWrapper>
|
||||
</FeatureWrapper>
|
||||
);
|
||||
return (
|
||||
<FeatureWrapper featureName="production-board">
|
||||
<RbacWrapper action="production:board">
|
||||
<ProductionBoardComponent/>
|
||||
</RbacWrapper>
|
||||
</FeatureWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ProductionBoardContainer);
|
||||
|
||||
@@ -2,5 +2,5 @@ import React from "react";
|
||||
import ProductionListTable from "../../components/production-list-table/production-list-table.container";
|
||||
|
||||
export default function ProductionListComponent() {
|
||||
return <ProductionListTable />;
|
||||
return <ProductionListTable/>;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import ProductionListComponent from "./production-list.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ProductionListContainer({
|
||||
setBreadcrumbs,
|
||||
setBreadcrumbs,
|
||||
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.productionlist");
|
||||
setSelectedHeader("productionlist");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/production/list", label: t("titles.bc.productionlist") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.productionlist");
|
||||
setSelectedHeader("productionlist");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/production/list", label: t("titles.bc.productionlist")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="production:list">
|
||||
<ProductionListComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="production:list">
|
||||
<ProductionListComponent/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ProductionListContainer);
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import ProfilePage from "./profile.page";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ProfileContainerPage({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
setSelectedHeader("profile");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/profile", label: t("titles.bc.profile") },
|
||||
]);
|
||||
document.title = t("titles.profile");
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
export function ProfileContainerPage({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
setSelectedHeader("profile");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/profile", label: t("titles.bc.profile")},
|
||||
]);
|
||||
document.title = t("titles.profile");
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return <ProfilePage />;
|
||||
return <ProfilePage/>;
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ProfileContainerPage);
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Row } from "antd";
|
||||
import {Row} from "antd";
|
||||
import React from "react";
|
||||
import ProfileMyComponent from "../../components/profile-my/profile-my.component";
|
||||
import ProfileShopsContainer from "../../components/profile-shops/profile-shops.container";
|
||||
|
||||
export default function ProfilePage() {
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<ProfileMyComponent />
|
||||
<ProfileShopsContainer />
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<ProfileMyComponent/>
|
||||
<ProfileShopsContainer/>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import UserRequestResetPw from "../../components/user-request-pw-reset/user-request-reset-pw.component";
|
||||
import UserValidatePwReset from "../../components/user-validate-pw-reset/user-validate-pw-reset.component";
|
||||
import queryString from "query-string";
|
||||
|
||||
export default function ResetPassword() {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { mode, oobCode } = searchParams;
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {mode, oobCode} = searchParams;
|
||||
|
||||
if (mode === "resetPassword")
|
||||
return <UserValidatePwReset oobCode={oobCode} />;
|
||||
return <UserRequestResetPw />;
|
||||
}
|
||||
if (mode === "resetPassword")
|
||||
return <UserValidatePwReset oobCode={oobCode}/>;
|
||||
return <UserRequestResetPw/>;
|
||||
}
|
||||
@@ -2,5 +2,5 @@ import React from "react";
|
||||
import ScheduleCalendarContainer from "../../components/schedule-calendar/schedule-calendar.container";
|
||||
|
||||
export default function SchedulePageComponent() {
|
||||
return <ScheduleCalendarContainer />;
|
||||
return <ScheduleCalendarContainer/>;
|
||||
}
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import SchedulePageComponent from "./schedule.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function SchedulePageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function SchedulePageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.schedule");
|
||||
setSelectedHeader("schedule");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/schedule", label: t("titles.bc.schedule") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.schedule");
|
||||
setSelectedHeader("schedule");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/schedule", label: t("titles.bc.schedule")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="schedule:view">
|
||||
<SchedulePageComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="schedule:view">
|
||||
<SchedulePageComponent/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(SchedulePageContainer);
|
||||
|
||||
@@ -1,103 +1,109 @@
|
||||
import Icon, { FieldTimeOutlined } from "@ant-design/icons";
|
||||
import { Tabs } from "antd";
|
||||
import Icon, {FieldTimeOutlined} from "@ant-design/icons";
|
||||
import {Tabs} from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaShieldAlt } from "react-icons/fa";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {FaShieldAlt} from "react-icons/fa";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
|
||||
import ScoreboardTimeTicketsStats from "../../components/scoreboard-timetickets-stats/scoreboard-timetickets.component";
|
||||
import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
/**
|
||||
* Mapping state to props
|
||||
*/
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
/**
|
||||
* Mapping dispatch to props
|
||||
*/
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { tab } = searchParams;
|
||||
const history = useHistory();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.scoreboard");
|
||||
setSelectedHeader("scoreboard");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/scoreboard",
|
||||
label: t("titles.bc.scoreboard"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
/**
|
||||
* ScoreboardContainer component
|
||||
* @param {Object} props - The props that were defined by the caller of this component.
|
||||
* @param {Function} props.setBreadcrumbs - Function to set breadcrumbs.
|
||||
* @param {Function} props.setSelectedHeader - Function to set selected header.
|
||||
*/
|
||||
export function ScoreboardContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {tab} = searchParams;
|
||||
const history = useNavigate();
|
||||
|
||||
return (
|
||||
<FeatureWrapper featureName="scoreboard">
|
||||
<RbacWrapper action="scoreboard:view">
|
||||
<Tabs
|
||||
activeKey={tab || "sb"}
|
||||
destroyInactiveTabPane
|
||||
onChange={(key) => {
|
||||
searchParams.tab = key;
|
||||
history.push({
|
||||
search: queryString.stringify(searchParams),
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<Icon component={FaShieldAlt} />
|
||||
{t("scoreboard.labels.jobs")}
|
||||
</span>
|
||||
}
|
||||
destroyInactiveTabPane
|
||||
key="sb"
|
||||
>
|
||||
<ScoreboardDisplay />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<FieldTimeOutlined />
|
||||
{t("scoreboard.labels.timeticketsemployee")}
|
||||
</span>
|
||||
}
|
||||
destroyInactiveTabPane
|
||||
key="tickets"
|
||||
>
|
||||
<ScoreboardTimeTickets />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<FieldTimeOutlined />
|
||||
{t("scoreboard.labels.allemployeetimetickets")}
|
||||
</span>
|
||||
}
|
||||
destroyInactiveTabPane
|
||||
key="ticketsstats"
|
||||
>
|
||||
<ScoreboardTimeTicketsStats />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</RbacWrapper>
|
||||
</FeatureWrapper>
|
||||
);
|
||||
/**
|
||||
* useEffect hook to set document title, selected header and breadcrumbs
|
||||
*/
|
||||
useEffect(() => {
|
||||
document.title = t("titles.scoreboard");
|
||||
setSelectedHeader("scoreboard");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/scoreboard",
|
||||
label: t("titles.bc.scoreboard"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
/**
|
||||
* Render the component
|
||||
*/
|
||||
return (
|
||||
<FeatureWrapper featureName="scoreboard">
|
||||
<RbacWrapper action="scoreboard:view">
|
||||
<Tabs
|
||||
activeKey={tab || "sb"}
|
||||
destroyInactiveTabPane
|
||||
onChange={(key) => {
|
||||
searchParams.tab = key;
|
||||
history({
|
||||
search: queryString.stringify(searchParams),
|
||||
});
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
key: "sb",
|
||||
icon: <Icon component={FaShieldAlt}/>,
|
||||
label: t("scoreboard.labels.jobs"),
|
||||
forceRender: true,
|
||||
children: <ScoreboardDisplay/>,
|
||||
},
|
||||
{
|
||||
key: "tickets",
|
||||
icon: <FieldTimeOutlined/>,
|
||||
label: t("scoreboard.labels.timeticketsemployee"),
|
||||
forceRender: true,
|
||||
children: <ScoreboardTimeTickets/>,
|
||||
},
|
||||
{
|
||||
key: "ticketsstats",
|
||||
icon: <FieldTimeOutlined/>,
|
||||
label: t("scoreboard.labels.allemployeetimetickets"),
|
||||
forceRender: true,
|
||||
children: <ScoreboardTimeTicketsStats/>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</FeatureWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connecting the component to Redux store
|
||||
*/
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ScoreboardContainer);
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ScoreboardContainer);
|
||||
@@ -3,9 +3,9 @@ import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
|
||||
|
||||
export default function ShiftClock() {
|
||||
return (
|
||||
<RbacWrapper action="shiftclock:view">
|
||||
<TimeTicketShift />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="shiftclock:view">
|
||||
<TimeTicketShift/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,98 +1,87 @@
|
||||
import { Row, Col } from "antd";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {Col, Row} from "antd";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
|
||||
import CsiResponseListPaginated from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
|
||||
import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CsiResponseListPaginated
|
||||
from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
|
||||
import {QUERY_CSI_RESPONSE_PAGINATED} from "../../graphql/csi.queries";
|
||||
import {setBreadcrumbs, setSelectedHeader} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {pageLimit} from "../../utils/config";
|
||||
import queryString from "query-string";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ShopCsiContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
export function ShopCsiContainer({bodyshop, setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page, sortcolumn, sortorder} = searchParams;
|
||||
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder } = searchParams;
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_CSI_RESPONSE_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "completedon"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc_nulls_last"
|
||||
: "asc"
|
||||
: "desc_nulls_last",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_CSI_RESPONSE_PAGINATED,
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
//search: search || "",
|
||||
offset: page ? (page - 1) * pageLimit : 0,
|
||||
limit: pageLimit,
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "completedon"]: sortorder
|
||||
? sortorder === "descend"
|
||||
? "desc_nulls_last"
|
||||
: "asc"
|
||||
: "desc_nulls_last",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop-csi");
|
||||
setSelectedHeader("shop-csi");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/shop",
|
||||
label: t("titles.bc.shop", {shopname: bodyshop.shopname}),
|
||||
},
|
||||
{link: "/manage/shop/csi", label: t("titles.bc.shop-csi")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, bodyshop.shopname, setSelectedHeader]);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop-csi");
|
||||
setSelectedHeader("shop-csi");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/shop",
|
||||
label: t("titles.bc.shop", { shopname: bodyshop.shopname }),
|
||||
},
|
||||
{ link: "/manage/shop/csi", label: t("titles.bc.shop-csi") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, bodyshop.shopname, setSelectedHeader]);
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<RbacWrapper
|
||||
action="csi:page"
|
||||
// noauth={
|
||||
// <AlertComponent message="You don't have acess to see this screen." />
|
||||
// }
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={10}>
|
||||
<CsiResponseListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
responses={data ? data.csi : []}
|
||||
total={data ? data.csi_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={14}>
|
||||
<CsiResponseFormContainer />
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="csi:page">
|
||||
<Row gutter={16}>
|
||||
<Col span={10}>
|
||||
<CsiResponseListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
responses={data ? data.csi : []}
|
||||
total={data ? data.csi_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={14}>
|
||||
<CsiResponseFormContainer/>
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopCsiContainer);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopCsiContainer);
|
||||
@@ -42,7 +42,7 @@
|
||||
// return (
|
||||
// <RbacWrapper action="shop:templates">
|
||||
// <div>
|
||||
// <ShopTemplatesListContainer visibleState={drawerVisibility} />
|
||||
// <ShopTemplatesListContainer openState={drawerVisibility} />
|
||||
// <Button onClick={() => drawerVisibility[1](true)}>Show List</Button>
|
||||
// <ShopTemplateEditor />
|
||||
// </div>
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
import { Drawer, Grid } from "antd";
|
||||
import queryString from "query-string";
|
||||
import {Drawer, Grid} from "antd";
|
||||
import React from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import {useNavigate, useSearchParams} from "react-router-dom";
|
||||
import VendorsFormContainer from "../../components/vendors-form/vendors-form.container";
|
||||
import VendorsListContainer from "../../components/vendors-list/vendors-list.container";
|
||||
|
||||
export default function ShopVendorPageComponent() {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const { selectedvendor } = search;
|
||||
const history = useHistory();
|
||||
const [searchParams] = useSearchParams();
|
||||
const {selectedvendor} = Object.fromEntries(searchParams);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
|
||||
const bpoints = {
|
||||
xs: "100%",
|
||||
sm: "100%",
|
||||
md: "100%",
|
||||
lg: "50%",
|
||||
xl: "50%",
|
||||
xxl: "45%",
|
||||
};
|
||||
const drawerPercentage = selectedBreakpoint
|
||||
? bpoints[selectedBreakpoint[0]]
|
||||
: "100%";
|
||||
const bpoints = {
|
||||
xs: "100%",
|
||||
sm: "100%",
|
||||
md: "100%",
|
||||
lg: "50%",
|
||||
xl: "50%",
|
||||
xxl: "45%",
|
||||
};
|
||||
const drawerPercentage = selectedBreakpoint
|
||||
? bpoints[selectedBreakpoint[0]]
|
||||
: "100%";
|
||||
|
||||
return (
|
||||
<div>
|
||||
<VendorsListContainer />
|
||||
|
||||
<Drawer
|
||||
width={drawerPercentage}
|
||||
onClose={() => {
|
||||
delete search.selectedvendor;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
}}
|
||||
visible={selectedvendor}
|
||||
>
|
||||
<VendorsFormContainer />
|
||||
</Drawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
(<div>
|
||||
<VendorsListContainer/>
|
||||
<Drawer
|
||||
width={drawerPercentage}
|
||||
onClose={() => {
|
||||
searchParams.delete("selectedvendor");
|
||||
navigate({search: searchParams.toString()});
|
||||
}}
|
||||
open={selectedvendor}
|
||||
>
|
||||
<VendorsFormContainer/>
|
||||
</Drawer>
|
||||
</div>)
|
||||
);
|
||||
}
|
||||
@@ -1,48 +1,47 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import ShopVendorPageComponent from "./shop-vendor.page.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function ShopVendorPageContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop_vendors");
|
||||
setSelectedHeader("shop-vendors");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/shop",
|
||||
label: t("titles.bc.shop", { shopname: bodyshop.shopname }),
|
||||
},
|
||||
{ link: "/manage/shop/vendors", label: t("titles.bc.shop-vendors") },
|
||||
]);
|
||||
}, [t, bodyshop.shopname, setBreadcrumbs, setSelectedHeader]);
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop_vendors");
|
||||
setSelectedHeader("shop-vendors");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/shop",
|
||||
label: t("titles.bc.shop", {shopname: bodyshop.shopname}),
|
||||
},
|
||||
{link: "/manage/shop/vendors", label: t("titles.bc.shop-vendors")},
|
||||
]);
|
||||
}, [t, bodyshop.shopname, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="shop:vendors">
|
||||
<ShopVendorPageComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="shop:vendors">
|
||||
<ShopVendorPageComponent/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ShopVendorPageContainer);
|
||||
|
||||
@@ -1,77 +1,89 @@
|
||||
import { Tabs } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import {Tabs} from "antd";
|
||||
import React, {useEffect} from "react";
|
||||
import {useLocation, useNavigate} from "react-router-dom";
|
||||
import queryString from "query-string";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
|
||||
import ShopInfoContainer from "../../components/shop-info/shop-info.container";
|
||||
import ShopCsiConfig from "../../components/shop-csi-config/shop-csi-config.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
setSelectedHeader,
|
||||
setBreadcrumbs,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import ShopInfoUsersComponent from "../../components/shop-users/shop-users.component";
|
||||
|
||||
import ShopTeamsContainer from "../../components/shop-teams/shop-teams.container";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
});
|
||||
|
||||
export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
export function ShopPage({bodyshop, setSelectedHeader, setBreadcrumbs}) {
|
||||
const {t} = useTranslation();
|
||||
const history = useNavigate();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop");
|
||||
setSelectedHeader("shop");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/shop",
|
||||
label: t("titles.bc.shop", { shopname: bodyshop.shopname }),
|
||||
},
|
||||
]);
|
||||
}, [t, setSelectedHeader, setBreadcrumbs, bodyshop.shopname]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop");
|
||||
setSelectedHeader("shop");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/shop",
|
||||
label: t("titles.bc.shop", {shopname: bodyshop.shopname}),
|
||||
},
|
||||
]);
|
||||
}, [t, setSelectedHeader, setBreadcrumbs, bodyshop.shopname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!search.tab) history.push({ search: "?tab=info" });
|
||||
}, [history, search]);
|
||||
useEffect(() => {
|
||||
if (!search.tab) history({search: "?tab=info"});
|
||||
}, [history, search]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="shop:config">
|
||||
<Tabs
|
||||
defaultActiveKey={search.tab}
|
||||
onChange={(key) => history.push({ search: `?tab=${key}` })}
|
||||
>
|
||||
<Tabs.TabPane tab={t("bodyshop.labels.shopinfo")} key="info">
|
||||
<ShopInfoContainer />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab={t("bodyshop.labels.employees")} key="employees">
|
||||
<ShopEmployeesContainer />
|
||||
</Tabs.TabPane>
|
||||
const items = [
|
||||
{
|
||||
bodyshop.md_tasks_presets.enable_tasks &&
|
||||
<Tabs.TabPane tab={t("bodyshop.labels.employee_teams")} key="teams">
|
||||
<ShopTeamsContainer />
|
||||
</Tabs.TabPane>
|
||||
}
|
||||
<Tabs.TabPane tab={t("bodyshop.labels.licensing")} key="licensing">
|
||||
<ShopInfoUsersComponent />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab={t("bodyshop.labels.csiq")} key="csiq">
|
||||
<ShopCsiConfig />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</RbacWrapper>
|
||||
);
|
||||
key: "info",
|
||||
label: t("bodyshop.labels.shopinfo"),
|
||||
children: <ShopInfoContainer/>,
|
||||
},
|
||||
{
|
||||
key: "employees",
|
||||
label: t("bodyshop.labels.employees"),
|
||||
children: <ShopEmployeesContainer/>,
|
||||
}];
|
||||
|
||||
if (bodyshop.md_tasks_presets.enable_tasks) {
|
||||
items.push({
|
||||
key: "teams",
|
||||
label: t("bodyshop.labels.employee_teams"),
|
||||
children: <ShopTeamsContainer/>
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
key: "licensing",
|
||||
label: t("bodyshop.labels.licensing"),
|
||||
children: <ShopInfoUsersComponent/>,
|
||||
},
|
||||
{
|
||||
key: "csiq",
|
||||
label: t("bodyshop.labels.csiq"),
|
||||
children: <ShopCsiConfig/>,
|
||||
});
|
||||
|
||||
return (
|
||||
<RbacWrapper action="shop:config">
|
||||
<Tabs
|
||||
activeKey={search.tab}
|
||||
onChange={(key) => history({search: `?tab=${key}`})}
|
||||
items={items}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopPage);
|
||||
|
||||
@@ -2,9 +2,9 @@ import React from "react";
|
||||
import SignIn from "../../components/sign-in-form/sign-in-form.component";
|
||||
|
||||
export default function SignInPage() {
|
||||
return (
|
||||
<div>
|
||||
<SignIn />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<SignIn/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,261 +1,261 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useState } from "react";
|
||||
import { Table } from "antd";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM } from "../../graphql/jobs.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Input, Space } from "antd";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useState} from "react";
|
||||
import {Button, Card, Input, Space, Table} from "antd";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM} from "../../graphql/jobs.queries";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {alphaSort} from "../../utils/sorters";
|
||||
import {SyncOutlined} from "@ant-design/icons";
|
||||
import queryString from "query-string";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useNavigate, useLocation} from "react-router-dom";
|
||||
import {onlyUnique} from "../../utils/arrayHelper";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import {setModalContext} from "../../redux/modals/modals.actions";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
//currentUser: selectCurrentUser
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setTimeTicketTaskContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
||||
setTimeTicketTaskContext: (context) =>
|
||||
dispatch(setModalContext({context: context, modal: "timeTicketTask"})),
|
||||
});
|
||||
|
||||
export function TechAssignedProdJobs({
|
||||
setTimeTicketTaskContext,
|
||||
technician,
|
||||
bodyshop,
|
||||
}) {
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM,
|
||||
{
|
||||
variables: {
|
||||
teamIds: bodyshop.employee_teams
|
||||
.filter((et) =>
|
||||
et.employee_team_members.find(
|
||||
(etm) => etm.employeeid === technician.id
|
||||
setTimeTicketTaskContext,
|
||||
technician,
|
||||
bodyshop,
|
||||
}) {
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM,
|
||||
{
|
||||
variables: {
|
||||
teamIds: bodyshop.employee_teams
|
||||
.filter((et) =>
|
||||
et.employee_team_members.find(
|
||||
(etm) => etm.employeeid === technician.id
|
||||
)
|
||||
)
|
||||
.map((et) => et.id),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {selected} = searchParams;
|
||||
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: {text: ""},
|
||||
});
|
||||
const {t} = useTranslation();
|
||||
const history = useNavigate();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase())
|
||||
)
|
||||
)
|
||||
.map((et) => et.id),
|
||||
},
|
||||
}
|
||||
);
|
||||
: [];
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { selected } = searchParams;
|
||||
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [searchText, setSearchText] = useState("");
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const jobs = data
|
||||
? searchText === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(j.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
|
||||
render: (text, record) => record.ro_number || t("general.labels.na"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
render: (text, record) => record.ro_number || t("general.labels.na"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record}/>
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.status)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
render: (text, record) => {
|
||||
return record.status || t("general.labels.na");
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.status)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
render: (text, record) => {
|
||||
return record.status || t("general.labels.na");
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
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,
|
||||
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("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTimeTicketTaskContext({
|
||||
actions: { refetch: refetch },
|
||||
context: { jobid: record.id },
|
||||
});
|
||||
}}
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("vehicles.fields.plate_no"),
|
||||
dataIndex: "plate_no",
|
||||
key: "plate_no",
|
||||
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,
|
||||
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("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTimeTicketTaskContext({
|
||||
actions: {refetch: refetch},
|
||||
context: {jobid: record.id},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("timetickets.actions.claimtasks")}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({...state, filteredInfo: filters, sortedInfo: sorter});
|
||||
};
|
||||
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
history({
|
||||
search: queryString.stringify({
|
||||
...searchParams,
|
||||
selected: record.id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Card
|
||||
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>
|
||||
}
|
||||
>
|
||||
{t("timetickets.actions.claimtasks")}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
const handleOnRowClick = (record) => {
|
||||
if (record) {
|
||||
if (record.id) {
|
||||
history.push({
|
||||
search: queryString.stringify({
|
||||
...searchParams,
|
||||
selected: record.id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Card
|
||||
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}
|
||||
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>
|
||||
);
|
||||
<Table
|
||||
loading={loading}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TechAssignedProdJobs);
|
||||
|
||||
@@ -1,139 +1,137 @@
|
||||
import {
|
||||
MinusCircleTwoTone,
|
||||
PlusCircleTwoTone,
|
||||
SyncOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Card, Space, Table } from "antd";
|
||||
import {MinusCircleTwoTone, PlusCircleTwoTone, SyncOutlined,} from "@ant-design/icons";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Button, Card, Space, Table} from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useNavigate, useLocation} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import PartsDispatchExpander from "../../components/parts-dispatch-expander/parts-dispatch-expander.component";
|
||||
import { GET_UNACCEPTED_PARTS_DISPATCH } from "../../graphql/parts-dispatch.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import {GET_UNACCEPTED_PARTS_DISPATCH} from "../../graphql/parts-dispatch.queries";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {alphaSort} from "../../utils/sorters";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
//currentUser: selectCurrentUser
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export function TechDispatchedParts({ technician, bodyshop }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page } = searchParams;
|
||||
export function TechDispatchedParts({technician, bodyshop}) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const {page} = searchParams;
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
GET_UNACCEPTED_PARTS_DISPATCH,
|
||||
{
|
||||
variables: {
|
||||
techId: technician.id,
|
||||
offset: page ? (page - 1) * 25 : 0,
|
||||
limit: 25,
|
||||
},
|
||||
}
|
||||
);
|
||||
const {loading, error, data, refetch} = useQuery(
|
||||
GET_UNACCEPTED_PARTS_DISPATCH,
|
||||
{
|
||||
variables: {
|
||||
techId: technician.id,
|
||||
offset: page ? (page - 1) * 25 : 0,
|
||||
limit: 25,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const {t} = useTranslation();
|
||||
const history = useNavigate();
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
const parts_dispatch = data?.parts_dispatch;
|
||||
const parts_dispatch = data?.parts_dispatch;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "job.ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "job.ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
|
||||
render: (text, record) => record.job.ro_number || t("general.labels.na"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
render: (text, record) => {
|
||||
return record.job.status || t("general.labels.na");
|
||||
},
|
||||
},
|
||||
render: (text, record) => record.job.ro_number || t("general.labels.na"),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
render: (text, record) => {
|
||||
return record.job.status || t("general.labels.na");
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.job.v_model_yr || ""} ${
|
||||
record.job.v_make_desc || ""
|
||||
} ${record.job.v_model_desc || ""}`}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Button onClick={() => {}}>
|
||||
{t("timetickets.actions.claimtasks")}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
searchParams.page = pagination.current;
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
pageSize: 25,
|
||||
current: parseInt(page || 1),
|
||||
total: data ? data.parts_dispatch_aggregate.aggregate.count : 0,
|
||||
showSizeChanger: false,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={parts_dispatch}
|
||||
scroll={{ x: true }}
|
||||
onChange={handleTableChange}
|
||||
expandable={{
|
||||
expandedRowRender: (record) => (
|
||||
<PartsDispatchExpander dispatch={record} />
|
||||
),
|
||||
rowExpandable: (record) => true,
|
||||
//expandRowByClick: true,
|
||||
expandIcon: ({ expanded, onExpand, record }) =>
|
||||
expanded ? (
|
||||
<MinusCircleTwoTone onClick={(e) => onExpand(record, e)} />
|
||||
) : (
|
||||
<PlusCircleTwoTone onClick={(e) => onExpand(record, e)} />
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.job.v_model_yr || ""} ${
|
||||
record.job.v_make_desc || ""
|
||||
} ${record.job.v_model_desc || ""}`}</span>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Button onClick={() => {
|
||||
}}>
|
||||
{t("timetickets.actions.claimtasks")}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
searchParams.page = pagination.current;
|
||||
history({search: queryString.stringify(searchParams)});
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
<Button onClick={() => refetch()}>
|
||||
<SyncOutlined/>
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
loading={loading}
|
||||
pagination={{
|
||||
pageSize: 25,
|
||||
current: parseInt(page || 1),
|
||||
total: data ? data.parts_dispatch_aggregate.aggregate.count : 0,
|
||||
showSizeChanger: false,
|
||||
}}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
dataSource={parts_dispatch}
|
||||
scroll={{x: true}}
|
||||
onChange={handleTableChange}
|
||||
expandable={{
|
||||
expandedRowRender: (record) => (
|
||||
<PartsDispatchExpander dispatch={record}/>
|
||||
),
|
||||
rowExpandable: (record) => true,
|
||||
//expandRowByClick: true,
|
||||
expandIcon: ({expanded, onExpand, record}) =>
|
||||
expanded ? (
|
||||
<MinusCircleTwoTone onClick={(e) => onExpand(record, e)}/>
|
||||
) : (
|
||||
<PlusCircleTwoTone onClick={(e) => onExpand(record, e)}/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TechDispatchedParts);
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import { Divider } from "antd";
|
||||
import React from "react";
|
||||
import {Divider} from "antd";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
||||
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
||||
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
|
||||
|
||||
export default function TechClockComponent() {
|
||||
return (
|
||||
<div>
|
||||
<TechJobStatistics />
|
||||
<TechClockInFormContainer />
|
||||
<Divider />
|
||||
<TechClockedInList />
|
||||
</div>
|
||||
);
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.techjobclock");
|
||||
}, [t]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TechJobStatistics/>
|
||||
<TechClockInFormContainer/>
|
||||
<Divider/>
|
||||
<TechClockedInList/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import React from "react";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
||||
|
||||
export default function TechLookupContainer() {
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapperComponent action="jobs:list-active">
|
||||
<TechLookupJobsList />
|
||||
</RbacWrapperComponent>
|
||||
</div>
|
||||
);
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.techjoblookup");
|
||||
}, [t]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapperComponent action="jobs:list-active">
|
||||
<TechLookupJobsList/>
|
||||
</RbacWrapperComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import React from "react";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
|
||||
|
||||
export default function TechShiftClock() {
|
||||
return (
|
||||
<div>
|
||||
<TimeTicketShift isTechConsole />
|
||||
</div>
|
||||
);
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.techshiftclock");
|
||||
}, [t]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TimeTicketShift isTechConsole/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BackTop, Layout } from "antd";
|
||||
import React, { Suspense, lazy, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Route, Switch } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {FloatButton, Layout} from "antd";
|
||||
import React, {lazy, Suspense, useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {Route, Routes, useNavigate} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||
|
||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
@@ -12,132 +12,107 @@ import TechHeader from "../../components/tech-header/tech-header.component";
|
||||
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
||||
import TechSider from "../../components/tech-sider/tech-sider.component";
|
||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import "./tech.page.styles.scss";
|
||||
import {selectTechnician} from "../../redux/tech/tech.selectors";
|
||||
import "./tech.page.styles.scss";
|
||||
|
||||
const TimeTicketModalContainer = lazy(() =>
|
||||
import("../../components/time-ticket-modal/time-ticket-modal.container")
|
||||
import("../../components/time-ticket-modal/time-ticket-modal.container")
|
||||
);
|
||||
const EmailOverlayContainer = lazy(() =>
|
||||
import("../../components/email-overlay/email-overlay.container.jsx")
|
||||
import("../../components/email-overlay/email-overlay.container.jsx")
|
||||
);
|
||||
const PrintCenterModalContainer = lazy(() =>
|
||||
import("../../components/print-center-modal/print-center-modal.container")
|
||||
import("../../components/print-center-modal/print-center-modal.container")
|
||||
);
|
||||
const TechLogin = lazy(() =>
|
||||
import("../../components/tech-login/tech-login.component")
|
||||
import("../../components/tech-login/tech-login.component")
|
||||
);
|
||||
const TechLookup = lazy(() => import("../tech-lookup/tech-lookup.container"));
|
||||
const ProductionListPage = lazy(() =>
|
||||
import("../production-list/production-list.container")
|
||||
import("../production-list/production-list.container")
|
||||
);
|
||||
const ProductionBoardPage = lazy(() =>
|
||||
import("../production-board/production-board.container")
|
||||
import("../production-board/production-board.container")
|
||||
);
|
||||
const TechJobClock = lazy(() =>
|
||||
import("../tech-job-clock/tech-job-clock.component")
|
||||
import("../tech-job-clock/tech-job-clock.component")
|
||||
);
|
||||
const TechShiftClock = lazy(() =>
|
||||
import("../tech-shift-clock/tech-shift-clock.component")
|
||||
import("../tech-shift-clock/tech-shift-clock.component")
|
||||
);
|
||||
const TimeTicketModalTask = lazy(() =>
|
||||
import(
|
||||
"../../components/time-ticket-task-modal/time-ticket-task-modal.container"
|
||||
)
|
||||
import(
|
||||
"../../components/time-ticket-task-modal/time-ticket-task-modal.container"
|
||||
)
|
||||
);
|
||||
const TechAssignedProdJobs = lazy(() =>
|
||||
import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component")
|
||||
import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component")
|
||||
);
|
||||
const TechDispatchedParts = lazy(() =>
|
||||
import("../tech-dispatched-parts/tech-dispatched-parts.page")
|
||||
import("../tech-dispatched-parts/tech-dispatched-parts.page")
|
||||
);
|
||||
|
||||
const { Content } = Layout;
|
||||
const {Content} = Layout;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
technician: selectTechnician,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function TechPage({ technician, match }) {
|
||||
const { t } = useTranslation();
|
||||
export function TechPage({technician}) {
|
||||
const {t} = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.app");
|
||||
}, [t]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.app");
|
||||
}, [t]);
|
||||
|
||||
return (
|
||||
<Layout className="tech-layout-container">
|
||||
<TechSider />
|
||||
<Layout>
|
||||
{technician ? null : <Redirect to={`${match.path}/login`} />}
|
||||
<UpdateAlert />
|
||||
<TechHeader />
|
||||
<TechLookupJobsDrawer />
|
||||
<Content className="tech-content-container">
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
fallback={
|
||||
<LoadingSpinner message={t("general.labels.loadingapp")} />
|
||||
}
|
||||
>
|
||||
<FeatureWrapper featureName="tech-console">
|
||||
<TimeTicketModalContainer />
|
||||
<EmailOverlayContainer />
|
||||
<PrintCenterModalContainer />
|
||||
<TimeTicketModalTask />
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/login`}
|
||||
component={TechLogin}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/joblookup`}
|
||||
component={TechLookup}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/list`}
|
||||
component={ProductionListPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/jobclock`}
|
||||
component={TechJobClock}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/shiftclock`}
|
||||
component={TechShiftClock}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/board`}
|
||||
component={ProductionBoardPage}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/assigned`}
|
||||
component={TechAssignedProdJobs}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/dispatchedparts`}
|
||||
component={TechDispatchedParts}
|
||||
/>
|
||||
</Switch>
|
||||
</FeatureWrapper>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
useEffect(() => {
|
||||
if (!technician) {
|
||||
navigate(`/tech/login`);
|
||||
}
|
||||
}, [technician, navigate]);
|
||||
|
||||
<BackTop />
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
return (
|
||||
<Layout className="tech-layout-container">
|
||||
<TechSider/>
|
||||
<Layout>
|
||||
<UpdateAlert/>
|
||||
<TechHeader/>
|
||||
<TechLookupJobsDrawer/>
|
||||
<Content className="tech-content-container">
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
fallback={
|
||||
<LoadingSpinner message={t("general.labels.loadingapp")}/>
|
||||
}
|
||||
>
|
||||
<FeatureWrapper featureName="tech-console">
|
||||
<TimeTicketModalContainer/>
|
||||
<EmailOverlayContainer/>
|
||||
<PrintCenterModalContainer/>
|
||||
<TimeTicketModalTask/>
|
||||
<Routes>
|
||||
<Route path='/login' element={<TechLogin/>}/>
|
||||
<Route path='/joblookup' element={<TechLookup/>}/>
|
||||
<Route path='/list' element={<ProductionListPage/>}/>
|
||||
<Route path='/jobclock' element={<TechJobClock/>}/>
|
||||
<Route path='/shiftclock' element={<TechShiftClock/>}/>
|
||||
<Route path='/board' element={<ProductionBoardPage/>}/>
|
||||
<Route path='/assigned' element={<TechAssignedProdJobs/>}/>
|
||||
<Route path='/dispatchedparts' element={<TechDispatchedParts/>}/>
|
||||
|
||||
</Routes>
|
||||
</FeatureWrapper>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
<FloatButton.BackTop/>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TechPage);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TechPage);
|
||||
@@ -1,38 +1,40 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import React, { useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React, {useEffect} from "react";
|
||||
import {connect} from "react-redux";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
|
||||
import { setBodyshop } from "../../redux/user/user.actions";
|
||||
import {QUERY_BODYSHOP} from "../../graphql/bodyshop.queries";
|
||||
import {setBodyshop} from "../../redux/user/user.actions";
|
||||
import TechPage from "./tech.page.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
//import "../../utils/RegisterSw";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
|
||||
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
|
||||
});
|
||||
|
||||
export function TechPageContainer({ bodyshop, setBodyshop, match }) {
|
||||
const { loading, error, data } = useQuery(QUERY_BODYSHOP, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
if (data) setBodyshop(data.bodyshops[0]);
|
||||
}, [data, setBodyshop]);
|
||||
export function TechPageContainer({bodyshop, setBodyshop}) {
|
||||
const {loading, error, data} = useQuery(QUERY_BODYSHOP, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const {t} = useTranslation();
|
||||
|
||||
if (loading || !bodyshop)
|
||||
return <LoadingSpinner message={t("general.labels.loadingshop")} />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return <TechPage match={match} />;
|
||||
useEffect(() => {
|
||||
if (data) setBodyshop(data.bodyshops[0]);
|
||||
}, [data, setBodyshop]);
|
||||
|
||||
|
||||
if (loading || !bodyshop)
|
||||
return <LoadingSpinner message={t("general.labels.loadingshop")}/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
return <TechPage/>;
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TechPageContainer);
|
||||
|
||||
@@ -1,45 +1,47 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import React from "react";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobsDocumentsComponent from "../../components/jobs-documents-gallery/jobs-documents-gallery.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries";
|
||||
import {QUERY_TEMPORARY_DOCS} from "../../graphql/documents.queries";
|
||||
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import JobsDocumentsLocalGallery
|
||||
from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TemporaryDocsComponent);
|
||||
|
||||
export function TemporaryDocsComponent({ bodyshop }) {
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
skip: bodyshop.uselocalmediaserver,
|
||||
});
|
||||
export function TemporaryDocsComponent({bodyshop}) {
|
||||
const {loading, error, data, refetch} = useQuery(QUERY_TEMPORARY_DOCS, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
skip: bodyshop.uselocalmediaserver,
|
||||
});
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
if (bodyshop.uselocalmediaserver) {
|
||||
return <JobsDocumentsLocalGallery job={{ id: "temporary" }} />;
|
||||
}
|
||||
return (
|
||||
<JobsDocumentsComponent
|
||||
data={data ? data.documents : []}
|
||||
jobId={null}
|
||||
billId={null}
|
||||
refetch={refetch}
|
||||
ignoreSizeLimit
|
||||
/>
|
||||
);
|
||||
if (bodyshop.uselocalmediaserver) {
|
||||
return <JobsDocumentsLocalGallery job={{id: "temporary"}}/>;
|
||||
}
|
||||
return (
|
||||
<JobsDocumentsComponent
|
||||
data={data ? data.documents : []}
|
||||
jobId={null}
|
||||
billId={null}
|
||||
refetch={refetch}
|
||||
ignoreSizeLimit
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
import TemporaryDocsComponent from "./temporary-docs.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function TempDocumentsContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function TempDocumentsContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.temporarydocs");
|
||||
setSelectedHeader("temporarydocs");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/temporarydocs",
|
||||
label: t("titles.bc.temporarydocs"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.temporarydocs");
|
||||
setSelectedHeader("temporarydocs");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/temporarydocs",
|
||||
label: t("titles.bc.temporarydocs"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="temporarydocs:view">
|
||||
<TemporaryDocsComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="temporarydocs:view">
|
||||
<TemporaryDocsComponent/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TempDocumentsContainer);
|
||||
|
||||
@@ -1,100 +1,99 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Col, Row, Space } from "antd";
|
||||
import moment from "moment";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {Col, Row, Space} from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {useSearchParams} from "react-router-dom";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import TimeTicketsDatesSelector from "../../components/ticket-tickets-dates-selector/time-tickets-dates-selector.component";
|
||||
import TimeTicketsDatesSelector
|
||||
from "../../components/ticket-tickets-dates-selector/time-tickets-dates-selector.component";
|
||||
import TimeTicketList from "../../components/time-ticket-list/time-ticket-list.component";
|
||||
import TimeTicketsPayrollTable from "../../components/time-tickets-payroll-table/time-tickets-payroll-table.component";
|
||||
import TimeTicketsSummaryEmployees from "../../components/time-tickets-summary-employees/time-tickets-summary-employees.component";
|
||||
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
|
||||
import TimeTicketsAttendanceTable from "../../components/time-tickets-attendance-table/time-tickets-attendance-table.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import TimeTicketsSummaryEmployees
|
||||
from "../../components/time-tickets-summary-employees/time-tickets-summary-employees.component";
|
||||
import {QUERY_TIME_TICKETS_IN_RANGE} from "../../graphql/timetickets.queries";
|
||||
import TimeTicketsAttendanceTable
|
||||
from "../../components/time-tickets-attendance-table/time-tickets-attendance-table.component";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import TimeTicketsCommit from "../../components/time-tickets-commit/time-tickets-commit.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function TimeTicketsContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.timetickets");
|
||||
setSelectedHeader("timetickets");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/timetickets",
|
||||
label: t("titles.bc.timetickets"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const [searchParams] = useSearchParams();
|
||||
const {start, end} = Object.fromEntries(searchParams);
|
||||
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { start, end } = searchParams;
|
||||
const startDate = start
|
||||
? dayjs(start)
|
||||
: dayjs().startOf("week").subtract(7, "day");
|
||||
const endDate = end ? dayjs(end) : dayjs().endOf("week");
|
||||
|
||||
const startDate = start
|
||||
? moment(start)
|
||||
: moment().startOf("week").subtract(7, "days");
|
||||
const endDate = end ? moment(end) : moment().endOf("week");
|
||||
const {loading, error, data} = useQuery(QUERY_TIME_TICKETS_IN_RANGE, {
|
||||
variables: {
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_TIME_TICKETS_IN_RANGE, {
|
||||
variables: {
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
useEffect(() => {
|
||||
document.title = t("titles.timetickets");
|
||||
setSelectedHeader("timetickets");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/timetickets",
|
||||
label: t("titles.bc.timetickets"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
|
||||
return (
|
||||
<RbacWrapper action="timetickets:list">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<TimeTicketList
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<TimeTicketsAttendanceTable />
|
||||
<TimeTicketsPayrollTable />
|
||||
<TimeTicketsCommit timetickets={data ? data.timetickets : []} />
|
||||
<TimeTicketsDatesSelector />
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<TimeTicketsSummaryEmployees
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="timetickets:list">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<TimeTicketList
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<TimeTicketsAttendanceTable/>
|
||||
<TimeTicketsPayrollTable/>
|
||||
<TimeTicketsCommit timetickets={data ? data.timetickets : []}/>
|
||||
<TimeTicketsDatesSelector/>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<TimeTicketsSummaryEmployees
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TimeTicketsContainer);
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(TimeTicketsContainer);
|
||||
@@ -1,42 +1,40 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import React, {useEffect} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import TtApprovalsList from "../../components/tt-approvals-list/tt-approvals-list.container";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function TtApprovalsPage({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function TtApprovalsPage({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.ttapprovals");
|
||||
setSelectedHeader("ttapprovals");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/ttapprovals",
|
||||
label: t("titles.bc.ttapprovals"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.ttapprovals");
|
||||
setSelectedHeader("ttapprovals");
|
||||
setBreadcrumbs([
|
||||
{
|
||||
link: "/manage/ttapprovals",
|
||||
label: t("titles.bc.ttapprovals"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return (
|
||||
<RbacWrapper action="ttapprovals:view">
|
||||
<TtApprovalsList />
|
||||
</RbacWrapper>
|
||||
);
|
||||
return (
|
||||
<RbacWrapper action="ttapprovals:view">
|
||||
<TtApprovalsList/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TtApprovalsPage);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Col, Divider, Row } from "antd";
|
||||
import {Col, Divider, Row} from "antd";
|
||||
import React from "react";
|
||||
import VehicleDetailFormContainer from "../../components/vehicle-detail-form/vehicle-detail-form.container";
|
||||
import VehicleDetailJobsComponent from "../../components/vehicle-detail-jobs/vehicle-detail-jobs.component";
|
||||
|
||||
export default function VehicleDetailComponent({ vehicle, refetch }) {
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<VehicleDetailFormContainer vehicle={vehicle} refetch={refetch} />
|
||||
</Col>
|
||||
<Divider type="horizontal" />
|
||||
<Col span={24}>
|
||||
<VehicleDetailJobsComponent vehicle={vehicle} />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
export default function VehicleDetailComponent({vehicle, refetch}) {
|
||||
return (
|
||||
<div>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<VehicleDetailFormContainer vehicle={vehicle} refetch={refetch}/>
|
||||
</Col>
|
||||
<Divider type="horizontal"/>
|
||||
<Col span={24}>
|
||||
<VehicleDetailJobsComponent vehicle={vehicle}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,94 +1,90 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, {useEffect} from "react";
|
||||
import VehicleDetailComponent from "./vehicles-detail.page.component";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { QUERY_VEHICLE_BY_ID } from "../../graphql/vehicles.queries";
|
||||
import {useQuery} from "@apollo/client";
|
||||
import {useParams} from 'react-router-dom';
|
||||
import {QUERY_VEHICLE_BY_ID} from "../../graphql/vehicles.queries";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {addRecentItem, setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
import {connect} from "react-redux";
|
||||
import {CreateRecentItem} from "../../utils/create-recent-item";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function VehicleDetailContainer({
|
||||
match,
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const { vehId } = match.params;
|
||||
const { t } = useTranslation();
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
const {vehId} = useParams();
|
||||
const {t} = useTranslation();
|
||||
|
||||
const { loading, data, error, refetch } = useQuery(QUERY_VEHICLE_BY_ID, {
|
||||
variables: { id: vehId },
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.vehicledetail", {
|
||||
vehicle:
|
||||
data && data.vehicles_by_pk
|
||||
? `${(data.vehicles_by_pk && data.vehicles_by_pk.v_model_yr) || ""} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_make_desc) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_desc) || ""
|
||||
}`
|
||||
: "",
|
||||
const {loading, data, error, refetch} = useQuery(QUERY_VEHICLE_BY_ID, {
|
||||
variables: {id: vehId},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
setSelectedHeader("vehicles");
|
||||
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/vehicles", label: t("titles.bc.vehicles") },
|
||||
{
|
||||
link: `/manage/vehicles/${vehId}`,
|
||||
label: t("titles.bc.vehicle-details", {
|
||||
vehicle:
|
||||
data && data.vehicles_by_pk
|
||||
? `${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_yr) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_make_desc) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_desc) ||
|
||||
""
|
||||
}`
|
||||
: "",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.vehicledetail", {
|
||||
vehicle:
|
||||
data && data.vehicles_by_pk
|
||||
? `${(data.vehicles_by_pk && data.vehicles_by_pk.v_model_yr) || ""} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_make_desc) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_desc) || ""
|
||||
}`
|
||||
: "",
|
||||
});
|
||||
setSelectedHeader("vehicles");
|
||||
|
||||
if (data && data.vehicles_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
vehId,
|
||||
"vehicle",
|
||||
`${data.vehicles_by_pk.v_vin || "N/A"} | ${
|
||||
data.vehicles_by_pk.v_model_yr || ""
|
||||
} ${data.vehicles_by_pk.v_make_desc || ""} ${
|
||||
data.vehicles_by_pk.v_model_desc || ""
|
||||
}`.trim(),
|
||||
`/manage/vehicles/${vehId}`
|
||||
)
|
||||
);
|
||||
}, [t, data, setBreadcrumbs, vehId, addRecentItem, setSelectedHeader]);
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/vehicles", label: t("titles.bc.vehicles")},
|
||||
{
|
||||
link: `/manage/vehicles/${vehId}`,
|
||||
label: t("titles.bc.vehicle-details", {
|
||||
vehicle:
|
||||
data && data.vehicles_by_pk
|
||||
? `${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_yr) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_make_desc) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_desc) ||
|
||||
""
|
||||
}`
|
||||
: "",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (data && data.vehicles_by_pk)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
vehId,
|
||||
"vehicle",
|
||||
`${data.vehicles_by_pk.v_vin || "N/A"} | ${
|
||||
data.vehicles_by_pk.v_model_yr || ""
|
||||
} ${data.vehicles_by_pk.v_make_desc || ""} ${
|
||||
data.vehicles_by_pk.v_model_desc || ""
|
||||
}`.trim(),
|
||||
`/manage/vehicles/${vehId}`
|
||||
)
|
||||
);
|
||||
}, [t, data, setBreadcrumbs, vehId, addRecentItem, setSelectedHeader]);
|
||||
|
||||
if (!!!data.vehicles_by_pk) return <NotFound />;
|
||||
return (
|
||||
<VehicleDetailComponent vehicle={data.vehicles_by_pk} refetch={refetch} />
|
||||
);
|
||||
if (loading) return <LoadingSpinner/>;
|
||||
if (error) return <AlertComponent message={error.message} type="error"/>;
|
||||
if (!data.vehicles_by_pk) return <NotFound/>;
|
||||
return (
|
||||
<VehicleDetailComponent vehicle={data.vehicles_by_pk} refetch={refetch}/>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(VehicleDetailContainer);
|
||||
|
||||
@@ -2,5 +2,5 @@ import React from "react";
|
||||
import VehiclesListContainer from "../../components/vehicles-list/vehicles-list.container";
|
||||
|
||||
export default function VehiclesPageComponent() {
|
||||
return <VehiclesListContainer />;
|
||||
return <VehiclesListContainer/>;
|
||||
}
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import React, { useEffect } from "react";
|
||||
import React, {useEffect} from "react";
|
||||
import VehiclesPageComponent from "./vehicles.page.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
|
||||
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
|
||||
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||
});
|
||||
|
||||
export function VehiclesPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||
const { t } = useTranslation();
|
||||
export function VehiclesPageContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
const {t} = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.vehicles");
|
||||
setSelectedHeader("vehicles");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/vehicles", label: t("titles.bc.vehicles") },
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
useEffect(() => {
|
||||
document.title = t("titles.vehicles");
|
||||
setSelectedHeader("vehicles");
|
||||
setBreadcrumbs([
|
||||
{link: "/manage/vehicles", label: t("titles.bc.vehicles")},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
|
||||
return <VehiclesPageComponent />;
|
||||
return <VehiclesPageComponent/>;
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(VehiclesPageContainer);
|
||||
|
||||
Reference in New Issue
Block a user