From af7ff2d8b5f78aa85da3762cac8fe8d889e7054d Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Fri, 16 Feb 2024 14:01:39 -0700 Subject: [PATCH] FIrst round of changes for Instance Manger & Pro Manager --- .editorconfig | 9 +- _reference/BodyshopFeatures.json | 12 + client/.env.development.imex | 2 +- client/.env.development.promanager | 2 +- client/.env.development.rome | 2 +- client/.env.production.imex | 2 +- client/.env.production.promanager | 2 +- client/.env.production.rome | 2 +- client/.env.test.imex | 2 +- client/.env.test.promanager | 2 +- client/.env.test.rome | 2 +- .../feature-wrapper.component.jsx | 52 +- .../components/header/header.component.jsx | 1078 +++++++++-------- .../job-detail-lines/job-lines.component.jsx | 5 +- .../jobs-detail-pli.component.jsx | 5 + .../parts-order-list-table.component.jsx | 5 + .../schedule-calendar-header.component.js | 29 +- .../scheduler-calendar-wrapper.component.jsx | 290 ++--- .../schedule-job-modal.component.jsx | 268 ++-- client/src/index.js | 6 + .../accounting-payables.container.jsx | 3 + .../accounting-payments.container.jsx | 3 + .../accounting-receivables.container.jsx | 3 + .../src/pages/bills/bills.page.container.jsx | 3 + .../contract-detail.page.container.jsx | 3 + .../contracts/contracts.page.container.jsx | 15 +- .../courtesy-car-create.page.container.jsx | 18 +- .../courtesy-cars.page.container.jsx | 17 +- .../export-logs.page.container.jsx | 3 + .../jobs-detail.page.component.jsx | 12 +- .../payments-all.container.page.jsx | 3 + .../pages/shift-clock/shift-clock.page.jsx | 19 +- .../temporary-docs.container.jsx | 9 +- .../time-tickets/time-tickets.container.jsx | 5 +- .../tt-approvals.page.container.jsx | 3 + client/src/utils/instanceRenderMgr.js | 57 +- 36 files changed, 1068 insertions(+), 885 deletions(-) create mode 100644 _reference/BodyshopFeatures.json diff --git a/.editorconfig b/.editorconfig index 79368e997..259533a5b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,10 +3,13 @@ root = true [*] charset = utf-8 indent_style = space -indent_size = 4 +indent_size = 2 end_of_line = lf insert_final_newline = true -trim_trailing_whitespace = true +trim_trailing_whitespace = false +quote_type = single +max_line_length = 100 +bracketSpacing = false [*.md] trim_trailing_whitespace = false @@ -15,4 +18,4 @@ trim_trailing_whitespace = false indent_size = 2 [*.json] -indent_size = 2 \ No newline at end of file +indent_size = 2 diff --git a/_reference/BodyshopFeatures.json b/_reference/BodyshopFeatures.json new file mode 100644 index 000000000..a179d729b --- /dev/null +++ b/_reference/BodyshopFeatures.json @@ -0,0 +1,12 @@ +{ + "mobile": "date", + "allAccess": "bool", + "timetickets": "date", + "payments": "date", + "partsorders": "date", + "bills": "Date", + "export": "date", + "csi": "Date", + "courtesycars": "date", + "media": "date" +} diff --git a/client/.env.development.imex b/client/.env.development.imex index 05cd1cbc7..01e665c45 100644 --- a/client/.env.development.imex +++ b/client/.env.development.imex @@ -11,4 +11,4 @@ REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxw REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000 REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc -INSTANCE=IMEX +REACT_APP_INSTANCE=IMEX diff --git a/client/.env.development.promanager b/client/.env.development.promanager index a4d89915d..03ce9a5fc 100644 --- a/client/.env.development.promanager +++ b/client/.env.development.promanager @@ -11,4 +11,4 @@ REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxw REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000 REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc -INSTANCE=PROMANAGER +REACT_APP_INSTANCE=PROMANAGER diff --git a/client/.env.development.rome b/client/.env.development.rome index e5591e583..81072fc37 100644 --- a/client/.env.development.rome +++ b/client/.env.development.rome @@ -12,4 +12,4 @@ REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000 REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc REACT_APP_COUNTRY=USA -INSTANCE=ROME +REACT_APP_INSTANCE=ROME diff --git a/client/.env.production.imex b/client/.env.production.imex index 5ca553cdb..eb9feac1b 100644 --- a/client/.env.production.imex +++ b/client/.env.production.imex @@ -12,4 +12,4 @@ REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxw REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/ REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk -INSTANCE=IMEX +REACT_APP_INSTANCE=IMEX diff --git a/client/.env.production.promanager b/client/.env.production.promanager index 112ef4751..e7a75d808 100644 --- a/client/.env.production.promanager +++ b/client/.env.production.promanager @@ -12,4 +12,4 @@ REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxw REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/ REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk -INSTANCE=PROMANAGER +REACT_APP_INSTANCE=PROMANAGER diff --git a/client/.env.production.rome b/client/.env.production.rome index f8d45517d..1b01cdb12 100644 --- a/client/.env.production.rome +++ b/client/.env.production.rome @@ -12,4 +12,4 @@ REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxw REACT_APP_AXIOS_BASE_API_URL=https://api.romeonline.io/ REACT_APP_REPORTS_SERVER_URL=https://reports.romeonline.io REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk -INSTANCE=ROME +REACT_APP_INSTANCE=ROME diff --git a/client/.env.test.imex b/client/.env.test.imex index ea15d7ad6..af8d337fc 100644 --- a/client/.env.test.imex +++ b/client/.env.test.imex @@ -12,4 +12,4 @@ REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/ REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online REACT_APP_IS_TEST=true REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc -INSTANCE=IMEX +REACT_APP_INSTANCE=IMEX diff --git a/client/.env.test.promanager b/client/.env.test.promanager index 0b5b0da03..2591e575a 100644 --- a/client/.env.test.promanager +++ b/client/.env.test.promanager @@ -12,4 +12,4 @@ REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/ REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online REACT_APP_IS_TEST=true REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc -INSTANCE=PROMANAGER +REACT_APP_INSTANCE=PROMANAGER diff --git a/client/.env.test.rome b/client/.env.test.rome index 2a5182437..a4dcb1889 100644 --- a/client/.env.test.rome +++ b/client/.env.test.rome @@ -12,4 +12,4 @@ REACT_APP_AXIOS_BASE_API_URL=https://api.test.romeonline.io/ REACT_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io REACT_APP_IS_TEST=true REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc -INSTANCE=ROME +REACT_APP_INSTANCE=ROME diff --git a/client/src/components/feature-wrapper/feature-wrapper.component.jsx b/client/src/components/feature-wrapper/feature-wrapper.component.jsx index c456bcf25..62c9ed5ab 100644 --- a/client/src/components/feature-wrapper/feature-wrapper.component.jsx +++ b/client/src/components/feature-wrapper/feature-wrapper.component.jsx @@ -1,41 +1,41 @@ import dayjs from "../../utils/day"; import React from "react"; -import {useTranslation} from "react-i18next"; -import {connect} from "react-redux"; -import {createStructuredSelector} from "reselect"; -import {selectBodyshop} from "../../redux/user/user.selectors"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import AlertComponent from "../alert/alert.component"; const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, + bodyshop: selectBodyshop, }); function FeatureWrapper({ - bodyshop, - featureName, - noauth, - children, - ...restProps - }) { - const {t} = useTranslation(); + bodyshop, + featureName, + noauth, + children, + ...restProps +}) { + const { t } = useTranslation(); - if (HasFeatureAccess({featureName, bodyshop})) return children; + if (HasFeatureAccess({ featureName, bodyshop })) return children; - return ( - noauth || ( - - ) - ); + return ( + noauth || ( + + ) + ); } -export function HasFeatureAccess({featureName, bodyshop}) { - return ( - bodyshop.features.allAccess || - dayjs(bodyshop.features[featureName]).isAfter(dayjs()) - ); +export function HasFeatureAccess({ featureName, bodyshop }) { + return ( + bodyshop?.features.allAccess || + dayjs(bodyshop?.features[featureName]).isAfter(dayjs()) + ); } export default connect(mapStateToProps, null)(FeatureWrapper); diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index d58f544f8..d23343a89 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -1,531 +1,615 @@ import Icon, { - BankFilled, - BarChartOutlined, - CarFilled, - CheckCircleOutlined, - ClockCircleFilled, - DashboardFilled, - DollarCircleFilled, - ExportOutlined, - FieldTimeOutlined, - FileAddFilled, - FileAddOutlined, - FileFilled, - HomeFilled, - ImportOutlined, - InfoCircleOutlined, - LineChartOutlined, - PaperClipOutlined, - PhoneOutlined, - QuestionCircleFilled, - ScheduleOutlined, - SettingOutlined, - TeamOutlined, - ToolFilled, - UnorderedListOutlined, - UserOutlined, -} from "@ant-design/icons"; -import {useSplitTreatments} from "@splitsoftware/splitio-react"; -import {Layout, Menu, Switch, Tooltip} from "antd"; -import React, {useEffect, useState} from "react"; -import {useTranslation} from "react-i18next"; -import {BsKanban} from "react-icons/bs"; -import {FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar,} from "react-icons/fa"; -import {GiPayMoney, GiPlayerTime, GiSettingsKnobs} from "react-icons/gi"; -import {IoBusinessOutline} from "react-icons/io5"; -import {RiSurveyLine} from "react-icons/ri"; -import {connect} from "react-redux"; -import {Link} from "react-router-dom"; -import {createStructuredSelector} from "reselect"; -import {selectRecentItems, selectSelectedHeader,} from "../../redux/application/application.selectors"; -import {setModalContext} from "../../redux/modals/modals.actions"; -import {signOutStart} from "../../redux/user/user.actions"; -import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors"; -import {FiLogOut} from "react-icons/fi"; -import {checkBeta, handleBeta, setBeta} from "../../utils/betaHandler"; -import InstanceRenderManager from "../../utils/instanceRenderMgr"; + BankFilled, + BarChartOutlined, + CarFilled, + CheckCircleOutlined, + ClockCircleFilled, + DashboardFilled, + DollarCircleFilled, + ExportOutlined, + FieldTimeOutlined, + FileAddFilled, + FileAddOutlined, + FileFilled, + HomeFilled, + ImportOutlined, + InfoCircleOutlined, + LineChartOutlined, + PaperClipOutlined, + PhoneOutlined, + QuestionCircleFilled, + ScheduleOutlined, + SettingOutlined, + TeamOutlined, + ToolFilled, + UnorderedListOutlined, + UserOutlined, +} from '@ant-design/icons'; +import { useSplitTreatments } from '@splitsoftware/splitio-react'; +import { Layout, Menu, Switch, Tooltip } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { BsKanban } from 'react-icons/bs'; +import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar } from 'react-icons/fa'; +import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from 'react-icons/gi'; +import { IoBusinessOutline } from 'react-icons/io5'; +import { RiSurveyLine } from 'react-icons/ri'; +import { connect } from 'react-redux'; +import { Link } from 'react-router-dom'; +import { createStructuredSelector } from 'reselect'; +import { + selectRecentItems, + selectSelectedHeader, +} from '../../redux/application/application.selectors'; +import { setModalContext } from '../../redux/modals/modals.actions'; +import { signOutStart } from '../../redux/user/user.actions'; +import { selectBodyshop, selectCurrentUser } from '../../redux/user/user.selectors'; +import { FiLogOut } from 'react-icons/fi'; +import { checkBeta, handleBeta, setBeta } from '../../utils/betaHandler'; +import InstanceRenderManager from '../../utils/instanceRenderMgr'; +import { HasFeatureAccess } from '../feature-wrapper/feature-wrapper.component'; const mapStateToProps = createStructuredSelector({ - currentUser: selectCurrentUser, - recentItems: selectRecentItems, - selectedHeader: selectSelectedHeader, - bodyshop: selectBodyshop, + currentUser: selectCurrentUser, + recentItems: selectRecentItems, + selectedHeader: selectSelectedHeader, + bodyshop: selectBodyshop, }); const mapDispatchToProps = (dispatch) => ({ - setBillEnterContext: (context) => - dispatch(setModalContext({context: context, modal: "billEnter"})), - setTimeTicketContext: (context) => - dispatch(setModalContext({context: context, modal: "timeTicket"})), - setPaymentContext: (context) => - dispatch(setModalContext({context: context, modal: "payment"})), - setReportCenterContext: (context) => - dispatch(setModalContext({context: context, modal: "reportCenter"})), - signOutStart: () => dispatch(signOutStart()), - setCardPaymentContext: (context) => - dispatch(setModalContext({context: context, modal: "cardPayment"})), + setBillEnterContext: (context) => + dispatch(setModalContext({ context: context, modal: 'billEnter' })), + setTimeTicketContext: (context) => + dispatch(setModalContext({ context: context, modal: 'timeTicket' })), + setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: 'payment' })), + setReportCenterContext: (context) => + dispatch(setModalContext({ context: context, modal: 'reportCenter' })), + signOutStart: () => dispatch(signOutStart()), + setCardPaymentContext: (context) => + dispatch(setModalContext({ context: context, modal: 'cardPayment' })), }); function Header({ - handleMenuClick, - currentUser, - bodyshop, - selectedHeader, - signOutStart, - setBillEnterContext, - setTimeTicketContext, - setPaymentContext, - setReportCenterContext, - recentItems, - setCardPaymentContext - }) { + handleMenuClick, + currentUser, + bodyshop, + selectedHeader, + signOutStart, + setBillEnterContext, + setTimeTicketContext, + setPaymentContext, + setReportCenterContext, + recentItems, + setCardPaymentContext, +}) { + const { + treatments: { ImEXPay, DmsAp, Simple_Inventory }, + } = useSplitTreatments({ + attributes: {}, + names: ['ImEXPay', 'DmsAp', 'Simple_Inventory'], + splitKey: bodyshop && bodyshop.imexshopid, + }); + const [betaSwitch, setBetaSwitch] = useState(false); + const { t } = useTranslation(); - const {treatments: {ImEXPay, DmsAp, Simple_Inventory}} = useSplitTreatments({ - attributes: {}, - names: ["ImEXPay", "DmsAp", "Simple_Inventory"], - splitKey: bodyshop && bodyshop.imexshopid, - }); - const [betaSwitch, setBetaSwitch] = useState(false); - const {t} = useTranslation(); + useEffect(() => { + const isBeta = checkBeta(); + setBetaSwitch(isBeta); + }, []); - useEffect(() => { - const isBeta = checkBeta(); - setBetaSwitch(isBeta); - }, []); + const betaSwitchChange = (checked) => { + setBeta(checked); + setBetaSwitch(checked); + handleBeta(); + }; + const accountingChildren = []; - const betaSwitchChange = (checked) => { - setBeta(checked); - setBetaSwitch(checked); - handleBeta(); - } - - const accountingChildren = [ - { - key: 'bills', - icon: , - label: ({t("menus.header.bills")}) + if ( + InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'bills', bodyshop }), + }) + ) { + accountingChildren.push([ + { + key: 'bills', + icon: , + label: {t('menus.header.bills')}, + }, + { + key: 'enterbills', + icon: , + label: t('menus.header.enterbills'), + onClick: () => { + setBillEnterContext({ + actions: {}, + context: {}, + }); }, - { - key: 'enterbills', - icon: , - label: t("menus.header.enterbills"), - onClick: () => { - setBillEnterContext({ - actions: {}, - context: {}, - }); - } - }, - ]; + }, + ]); + } - if (Simple_Inventory.treatment === "on") { - accountingChildren.push( - { - type: 'divider', - }, - { - key: 'inventory', - icon: , - label: ({t("menus.header.inventory")}) - } - ); - } + if (Simple_Inventory.treatment === 'on') { accountingChildren.push( - { - type: 'divider' - }, - { - key: 'allpayments', - icon: , - label: ({t("menus.header.allpayments")}) - }, - { - key: 'enterpayments', - icon: , - label: t("menus.header.enterpayment"), - onClick: () => { - setPaymentContext({ - actions: {}, - context: null, - }); - }, - } + { + type: 'divider', + }, + { + key: 'inventory', + icon: , + label: {t('menus.header.inventory')}, + } ); - - if (ImEXPay.treatment === "on") { - accountingChildren.push({ - key: 'entercardpayments', - icon: , - label: t("menus.header.entercardpayment"), - onClick: () => { - setCardPaymentContext({ - actions: {}, - context: {}, - }); - }, - }); - } - + } + if ( + InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'payments', bodyshop }), + }) + ) { accountingChildren.push( - { - type: 'divider', + { + type: 'divider', + }, + { + key: 'allpayments', + icon: , + label: {t('menus.header.allpayments')}, + }, + { + key: 'enterpayments', + icon: , + label: t('menus.header.enterpayment'), + onClick: () => { + setPaymentContext({ + actions: {}, + context: null, + }); }, - { - key: 'timetickets', - icon: , - label: ({t("menus.header.timetickets")}) + } + ); + } + + if (ImEXPay.treatment === 'on') { + accountingChildren.push({ + key: 'entercardpayments', + icon: , + label: t('menus.header.entercardpayment'), + onClick: () => { + setCardPaymentContext({ + actions: {}, + context: {}, }); + }, + }); + } + + if ( + InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'timetickets', bodyshop }), + }) + ) { + accountingChildren.push( + { + type: 'divider', + }, + { + key: 'timetickets', + icon: , + label: {t('menus.header.timetickets')}, + } + ); if (bodyshop?.md_tasks_presets?.use_approvals) { - accountingChildren.push({ - key: 'ttapprovals', - icon: , - label: - - {t("menus.header.ttapprovals")} - - }); + accountingChildren.push({ + key: 'ttapprovals', + icon: , + label: {t('menus.header.ttapprovals')}, + }); } - - accountingChildren.push({ - key: 'entertimetickets', - icon: , - label: t("menus.header.entertimeticket"), - onClick: () => { - setTimeTicketContext({ - actions: {}, - context: { - created_by: currentUser.displayName - ? currentUser.email.concat(" | ", currentUser.displayName) - : currentUser.email, - }, - }); - }, - }, - { - type: 'divider', - } - ); - - const accountingExportChildren = [ - { - key: 'receivables', - label: ({t("menus.header.accounting-receivables")}) - }, - ] - - if (!((bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.pbs_serialnumber)) || DmsAp.treatment === "on") { - accountingExportChildren.push({ - key: 'payables', - label: ({t("menus.header.accounting-payables")}) - }); - } - - if (!( - (bodyshop && bodyshop.cdk_dealerid) || - (bodyshop && bodyshop.pbs_serialnumber) - )) { - accountingExportChildren.push({ - key: 'payments', - label: ({t("menus.header.accounting-payments")}) - }); - } - - accountingExportChildren.push({ - type: 'divider', - }, { - key: 'exportlogs', - label: ({t("menus.header.export-logs")}) - }); - accountingChildren.push( - { - key: 'accountingexport', - icon: , - label: t("menus.header.export"), - children: accountingExportChildren + { + key: 'entertimetickets', + icon: , + label: t('menus.header.entertimeticket'), + onClick: () => { + setTimeTicketContext({ + actions: {}, + context: { + created_by: currentUser.displayName + ? currentUser.email.concat(' | ', currentUser.displayName) + : currentUser.email, + }, + }); }, + }, + { + type: 'divider', + } ); + } - const menuItems = [ - { - key: 'home', - icon: , - label: ({t("menus.header.home")}) - }, - { - key: 'schedule', - icon: , - label: ({t("menus.header.schedule")}) - }, - { - key: 'jobssubmenu', - icon: , - label: t("menus.header.jobs"), - children: [ - { - key: 'activejobs', - icon: , - label: ({t("menus.header.activejobs")}) - }, - { - key: 'readyjobs', - icon: , - label: ({t("menus.header.readyjobs")}) - }, - { - key: 'parts-queue', - icon: , - label: ({t("menus.header.parts-queue")}) - }, - { - key: 'availablejobs', - icon: , - label: ({t("menus.header.availablejobs")}) - }, - { - key: 'newjob', - icon: , - label: ({t("menus.header.newjob")}) - }, - { - type: 'divider', - }, - { - key: 'alljobs', - icon: , - label: ({t("menus.header.alljobs")}) - }, - { - type: 'divider', - }, - { - key: 'productionlist', - icon: , - label: ({t("menus.header.productionlist")}) - }, - { - key: 'productionboard', - icon: , - label: ({t("menus.header.productionboard")}) - }, - { - type: 'divider', - }, - { - key: 'scoreboard', - icon: , - label: ({t("menus.header.scoreboard")}) - }, - ] - }, - { - key: 'customers', - icon: , - label: t("menus.header.customers"), - children: [ - { - key: 'owners', - icon: , - label: ({t("menus.header.owners")}) - }, - { - key: 'vehicles', - icon: , - label: ({t("menus.header.vehicles")}) - }, - ] - }, - { - key: 'ccs', - icon: , - label: t("menus.header.courtesycars"), - children: [ - { - key: 'courtesycarsall', - icon: , - label: ({t("menus.header.courtesycars-all")}) - }, - { - key: 'contracts', - icon: , - label: ({t("menus.header.courtesycars-contracts")}) - }, - { - key: 'newcontract', - icon: , - label: ({t("menus.header.courtesycars-newcontract")}) - }, - ] - }, - { - key: 'accounting', - icon: , - label: t("menus.header.accounting"), - children: accountingChildren, - }, - { - key: 'phonebook', - icon: , - label: ({t("menus.header.phonebook")}) - }, - { - key: 'temporarydocs', - icon: , - label: ({t("menus.header.temporarydocs")}) - }, - { - key: 'shopsubmenu', - icon: , - label: t("menus.header.shop"), - children: [ - { - key: 'shop', - icon: , - label: ({t("menus.header.shop_config")}) - }, - { - key: 'dashboard', - icon: , - label: ({t("menus.header.dashboard")}) - }, - { - key: 'reportcenter', - icon: , - label: t("menus.header.reportcenter"), - onClick: () => { - setReportCenterContext({ - actions: {}, - context: {}, - }); - } - }, - { - key: 'shop-vendors', - icon: , - label: ({t("menus.header.shop_vendors")}) - }, - { - key: 'shop-csi', - icon: , - label: ({t("menus.header.shop_csi")}) - }, - ] - }, - { - key: 'user', - label: ( - currentUser.displayName || - currentUser.email || - t("general.labels.unknown") - ), - children: [ - { - key: 'signout', - icon: , - danger: true, - label: t("user.actions.signout"), - onClick: () => signOutStart() - }, - { - key: 'help', - icon: , - label: t("menus.header.help"), - onClick: () => { - window.open(InstanceRenderManager({imex:"https://help.imex.online/",rome: "https://rometech.com//", promanager:"https://web-est.com" }) + const accountingExportChildren = [ + { + key: 'receivables', + label: ( + {t('menus.header.accounting-receivables')} + ), + }, + ]; - , "_blank"); - } - }, - // { - // key: 'rescue', - // icon: , - // label: t("menus.header.rescueme"), - // onClick: () => { - // window.open("https://imexrescue.com/", "_blank"); - // } - // }, - { - key: 'shiftclock', - icon: , - label: ({t("menus.header.shiftclock")}) - }, - { - key: 'profile', - icon: , - label: ({t("menus.currentuser.profile")}) - }, - // { - // key: 'langselecter', - // label: t("menus.currentuser.languageselector"), - // children: [ - // { - // key: 'en-US', - // label: t("general.languages.english"), - // onClick: () => { - // window.location.href = "/?lang=en-US"; - // } - // }, - // { - // key: 'fr-CA', - // label: t("general.languages.french"), - // onClick: () => { - // window.location.href = "/?lang=fr-CA"; - // } - // }, - // { - // key: 'es-MX', - // label: t("general.languages.spanish"), - // onClick: () => { - // window.location.href = "/?lang=es-MX"; - // } - // }, - // ] - // }, - ] - }, - { - key: 'recent', - icon: , - children: recentItems.map((i, idx) => ({ - key: idx, - label: ({i.label}) - - })), - } - ]; - - - menuItems.push({ - key: 'beta-switch', - style: {marginLeft: 'auto'}, - label: ( - - - Try the new app - - - ) + if ( + !((bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.pbs_serialnumber)) || + DmsAp.treatment === 'on' + ) { + accountingExportChildren.push({ + key: 'payables', + label: {t('menus.header.accounting-payables')}, }); + } - return ( - - + if (!((bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.pbs_serialnumber))) { + accountingExportChildren.push({ + key: 'payments', + label: {t('menus.header.accounting-payments')}, + }); + } - - ); + accountingExportChildren.push( + { + type: 'divider', + }, + { + key: 'exportlogs', + label: {t('menus.header.export-logs')}, + } + ); + + if ( + InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'export', bodyshop }), + }) + ) { + accountingChildren.push({ + key: 'accountingexport', + icon: , + label: t('menus.header.export'), + children: accountingExportChildren, + }); + } + + const menuItems = [ + { + key: 'home', + icon: , + label: {t('menus.header.home')}, + }, + { + key: 'schedule', + icon: , + label: {t('menus.header.schedule')}, + }, + { + key: 'jobssubmenu', + icon: , + label: t('menus.header.jobs'), + children: [ + { + key: 'activejobs', + icon: , + label: {t('menus.header.activejobs')}, + }, + { + key: 'readyjobs', + icon: , + label: {t('menus.header.readyjobs')}, + }, + { + key: 'parts-queue', + icon: , + label: {t('menus.header.parts-queue')}, + }, + { + key: 'availablejobs', + icon: , + label: {t('menus.header.availablejobs')}, + }, + { + key: 'newjob', + icon: , + label: {t('menus.header.newjob')}, + }, + { + type: 'divider', + }, + { + key: 'alljobs', + icon: , + label: {t('menus.header.alljobs')}, + }, + { + type: 'divider', + }, + { + key: 'productionlist', + icon: , + label: {t('menus.header.productionlist')}, + }, + { + key: 'productionboard', + icon: , + label: {t('menus.header.productionboard')}, + }, + { + type: 'divider', + }, + { + key: 'scoreboard', + icon: , + label: {t('menus.header.scoreboard')}, + }, + ], + }, + { + key: 'customers', + icon: , + label: t('menus.header.customers'), + children: [ + { + key: 'owners', + icon: , + label: {t('menus.header.owners')}, + }, + { + key: 'vehicles', + icon: , + label: {t('menus.header.vehicles')}, + }, + ], + }, + ...(InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'courtesycards', bodyshop }), + }) + ? [ + { + key: 'ccs', + icon: , + label: t('menus.header.courtesycars'), + children: [ + { + key: 'courtesycarsall', + icon: , + label: {t('menus.header.courtesycars-all')}, + }, + { + key: 'contracts', + icon: , + label: ( + + {t('menus.header.courtesycars-contracts')} + + ), + }, + { + key: 'newcontract', + icon: , + label: ( + + {t('menus.header.courtesycars-newcontract')} + + ), + }, + ], + }, + ] + : []), + + ...(accountingChildren.length > 0 + ? [ + { + key: 'accounting', + icon: , + label: t('menus.header.accounting'), + children: accountingChildren, + }, + ] + : []), + { + key: 'phonebook', + icon: , + label: {t('menus.header.phonebook')}, + }, + ...(InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'media', bodyshop }), + }) + ? [ + { + key: 'temporarydocs', + icon: , + label: {t('menus.header.temporarydocs')}, + }, + ] + : []), + { + key: 'shopsubmenu', + icon: , + label: t('menus.header.shop'), + children: [ + { + key: 'shop', + icon: , + label: {t('menus.header.shop_config')}, + }, + { + key: 'dashboard', + icon: , + label: {t('menus.header.dashboard')}, + }, + { + key: 'reportcenter', + icon: , + label: t('menus.header.reportcenter'), + onClick: () => { + setReportCenterContext({ + actions: {}, + context: {}, + }); + }, + }, + { + key: 'shop-vendors', + icon: , + label: {t('menus.header.shop_vendors')}, + }, + ...(InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'csi', bodyshop }), + }) + ? [ + { + key: 'shop-csi', + icon: , + label: {t('menus.header.shop_csi')}, + }, + ] + : []), + ], + }, + { + key: 'user', + label: currentUser.displayName || currentUser.email || t('general.labels.unknown'), + children: [ + { + key: 'signout', + icon: , + danger: true, + label: t('user.actions.signout'), + onClick: () => signOutStart(), + }, + { + key: 'help', + icon: , + label: t('menus.header.help'), + onClick: () => { + window.open( + InstanceRenderManager({ + imex: 'https://help.imex.online/', + rome: 'https://rometech.com//', + promanager: 'https://web-est.com', + }), + + '_blank' + ); + }, + }, + // { + // key: 'rescue', + // icon: , + // label: t("menus.header.rescueme"), + // onClick: () => { + // window.open("https://imexrescue.com/", "_blank"); + // } + // }, + + ...(InstanceRenderManager({ + imex: true, + rome: true, + promanager: HasFeatureAccess({ featureName: 'timetickets', bodyshop }), + }) + ? [ + { + key: 'shiftclock', + icon: , + label: {t('menus.header.shiftclock')}, + }, + ] + : []), + { + key: 'profile', + icon: , + label: {t('menus.currentuser.profile')}, + }, + // { + // key: 'langselecter', + // label: t("menus.currentuser.languageselector"), + // children: [ + // { + // key: 'en-US', + // label: t("general.languages.english"), + // onClick: () => { + // window.location.href = "/?lang=en-US"; + // } + // }, + // { + // key: 'fr-CA', + // label: t("general.languages.french"), + // onClick: () => { + // window.location.href = "/?lang=fr-CA"; + // } + // }, + // { + // key: 'es-MX', + // label: t("general.languages.spanish"), + // onClick: () => { + // window.location.href = "/?lang=es-MX"; + // } + // }, + // ] + // }, + ], + }, + { + key: 'recent', + icon: , + children: recentItems.map((i, idx) => ({ + key: idx, + label: {i.label}, + })), + }, + ]; + + menuItems.push({ + key: 'beta-switch', + style: { marginLeft: 'auto' }, + label: ( + + + Try the new app + + + ), + }); + + return ( + + + + ); } export default connect(mapStateToProps, mapDispatchToProps)(Header); diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx index edc9ca63a..a64771a86 100644 --- a/client/src/components/job-detail-lines/job-lines.component.jsx +++ b/client/src/components/job-detail-lines/job-lines.component.jsx @@ -41,6 +41,7 @@ import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-ass import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component"; import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component"; import {useSplitTreatments} from "@splitsoftware/splitio-react"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -318,13 +319,13 @@ export function JobLinesComponent({ ), }, - { + ...HasFeatureAccess({featureName: "bills"}) ? [ { title: t("joblines.labels.billref"), dataIndex: "billref", key: "billref", render: (text, record) => , responsive: ["md"], - }, + },] : [], { title: t("joblines.fields.status"), dataIndex: "status", diff --git a/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx b/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx index 3ac2c0bcb..6e3e414af 100644 --- a/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx +++ b/client/src/components/jobs-detail-pli/jobs-detail-pli.component.jsx @@ -7,6 +7,7 @@ import JobBillsTotal from "../job-bills-total/job-bills-total.component"; import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component"; import PartsOrderModal from "../parts-order-modal/parts-order-modal.container"; import PartsDispatchTable from "../parts-dispatch-table/parts-dispatch-table.component"; +import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component"; export default function JobsDetailPliComponent({ job, @@ -23,6 +24,7 @@ export default function JobsDetailPliComponent({ ) : null} + null}> + + null}> + )} + + + null}> + - 0} - date={date} - refetch={refetch} - > -
- {label} - {calculating ? : LoadComponent} -
-
- +
+ 0} + date={date} + refetch={refetch} + > +
+ {label} + {InstanceRenderMgr({ + imex: calculating ? : LoadComponent, + rome: "USE_IMEX", + promanager: <>, + })} +
+
+
); } diff --git a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx index 90cd33bc4..4acff1345 100644 --- a/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx +++ b/client/src/components/schedule-calendar-wrapper/scheduler-calendar-wrapper.component.jsx @@ -1,161 +1,171 @@ import dayjs from "../../utils/day"; import queryString from "query-string"; import React from "react"; -import {Calendar, dayjsLocalizer} from "react-big-calendar"; -import {connect} from "react-redux"; -import {Link, useLocation, useNavigate} from "react-router-dom"; -import {createStructuredSelector} from "reselect"; -import {selectBodyshop} from "../../redux/user/user.selectors"; +import { Calendar, dayjsLocalizer } from "react-big-calendar"; +import { connect } from "react-redux"; +import { Link, useLocation, useNavigate } from "react-router-dom"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import Event from "../job-at-change/schedule-event.container"; import HeaderComponent from "./schedule-calendar-header.component"; import "./schedule-calendar.styles.scss"; import JobDetailCards from "../job-detail-cards/job-detail-cards.component"; -import {selectProblemJobs} from "../../redux/application/application.selectors"; -import {Alert, Collapse, Space} from "antd"; -import {Trans, useTranslation} from "react-i18next"; +import { selectProblemJobs } from "../../redux/application/application.selectors"; +import { Alert, Collapse, Space } from "antd"; +import { Trans, useTranslation } from "react-i18next"; +import InstanceRenderManager from "../../utils/instanceRenderMgr"; const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, - problemJobs: selectProblemJobs, + bodyshop: selectBodyshop, + problemJobs: selectProblemJobs, }); const localizer = dayjsLocalizer(dayjs); export function ScheduleCalendarWrapperComponent({ - bodyshop, - problemJobs, - data, - refetch, - defaultView, - setDateRangeCallback, - date, - ...otherProps - }) { - const search = queryString.parse(useLocation().search); - const history = useNavigate(); - const {t} = useTranslation(); - const handleEventPropStyles = (event, start, end, isSelected) => { - return { - ...(event.color && !((search.view || defaultView) === "agenda") - ? { - style: { - backgroundColor: - event.color && event.color.hex ? event.color.hex : event.color, - }, - } - : {}), - className: `${event.arrived ? "imex-event-arrived" : ""} ${ - event.block ? "imex-event-block" : "" - }`, - }; + bodyshop, + problemJobs, + data, + refetch, + defaultView, + setDateRangeCallback, + date, + ...otherProps +}) { + const search = queryString.parse(useLocation().search); + const history = useNavigate(); + const { t } = useTranslation(); + const handleEventPropStyles = (event, start, end, isSelected) => { + return { + ...(event.color && !((search.view || defaultView) === "agenda") + ? { + style: { + backgroundColor: + event.color && event.color.hex ? event.color.hex : event.color, + }, + } + : {}), + className: `${event.arrived ? "imex-event-arrived" : ""} ${ + event.block ? "imex-event-block" : "" + }`, }; + }; - const selectedDate = new Date(date || dayjs(search.date) || Date.now()); + const selectedDate = new Date(date || dayjs(search.date) || Date.now()); - return ( - <> - - {problemJobs && problemJobs.length > 2 ? ( - - - {t("appointments.labels.severalerrorsfound")} - - } - > - - {problemJobs.map((problem) => ( - , - ]} - values={{ - ro_number: problem.ro_number, - code: problem.code, - }} - /> - } - /> - ))} - - - - - ) : ( - - { - problemJobs.map((problem) => ( - , - ]} - values={{ - ro_number: problem.ro_number, - code: problem.code, - }} - /> - } - /> - )) - } + return ( + <> + + {InstanceRenderManager({ + imex: + problemJobs && problemJobs.length > 2 ? ( + + + {t("appointments.labels.severalerrorsfound")} + + } + > + + {problemJobs.map((problem) => ( + , + ]} + values={{ + ro_number: problem.ro_number, + code: problem.code, + }} + /> + } + /> + ))} - )} + + + ) : ( + + {problemJobs.map((problem) => ( + , + ]} + values={{ + ro_number: problem.ro_number, + code: problem.code, + }} + /> + } + /> + ))} + + ), - { - search.date = date.toISOString().substr(0, 10); - history({search: queryString.stringify(search)}); - }} - onRangeChange={(start, end) => { - if (setDateRangeCallback) setDateRangeCallback({start, end}); - }} - onView={(view) => { - search.view = view; - history({search: queryString.stringify(search)}); - }} - step={15} - // timeslots={1} - showMultiDayTimes - localizer={localizer} - min={ - bodyshop.schedule_start_time - ? new Date(bodyshop.schedule_start_time) - : new Date("2020-01-01T06:00:00") - } - max={ - bodyshop.schedule_end_time - ? new Date(bodyshop.schedule_end_time) - : new Date("2020-01-01T20:00:00") - } - eventPropGetter={handleEventPropStyles} - components={{ - event: (e) => - Event({bodyshop: bodyshop, event: e.event, refetch: refetch}), - header: (p) => ( - - ), - }} - {...otherProps} - /> - - ); + rome: "USE_IMEX", + promanager: , + })} + + { + search.date = date.toISOString().substr(0, 10); + history({ search: queryString.stringify(search) }); + }} + onRangeChange={(start, end) => { + if (setDateRangeCallback) setDateRangeCallback({ start, end }); + }} + onView={(view) => { + search.view = view; + history({ search: queryString.stringify(search) }); + }} + step={15} + // timeslots={1} + showMultiDayTimes + localizer={localizer} + min={ + bodyshop.schedule_start_time + ? new Date(bodyshop.schedule_start_time) + : new Date("2020-01-01T06:00:00") + } + max={ + bodyshop.schedule_end_time + ? new Date(bodyshop.schedule_end_time) + : new Date("2020-01-01T20:00:00") + } + eventPropGetter={handleEventPropStyles} + components={{ + event: (e) => + Event({ bodyshop: bodyshop, event: e.event, refetch: refetch }), + header: (p) => ( + + ), + }} + {...otherProps} + /> + + ); } export default connect(mapStateToProps, null)(ScheduleCalendarWrapperComponent); diff --git a/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx b/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx index 3289b2015..721c2d870 100644 --- a/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx +++ b/client/src/components/schedule-job-modal/schedule-job-modal.component.jsx @@ -15,6 +15,7 @@ import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.con import ScheduleExistingAppointmentsList from "../schedule-existing-appointments-list/schedule-existing-appointments-list.component"; import "./schedule-job-modal.scss"; +import InstanceRenderManager from "../../utils/instanceRenderMgr"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -69,141 +70,144 @@ export function ScheduleJobModalComponent({ }; return ( - - - - - {lbrHrsData?.jobs_by_pk?.ro_number} - - {`B/R Hrs:${ - lbrHrsData?.jobs_by_pk.labhrs?.aggregate?.sum?.mod_lb_hrs || 0 - }/${ - lbrHrsData?.jobs_by_pk.larhrs?.aggregate?.sum?.mod_lb_hrs || 0 - }`} - - - - - - - - - - - - {t("appointments.labels.smartscheduling")} - - + + + + + {lbrHrsData?.jobs_by_pk?.ro_number} + + {`B/R Hrs:${ + lbrHrsData?.jobs_by_pk.labhrs?.aggregate?.sum?.mod_lb_hrs || 0 + }/${ + lbrHrsData?.jobs_by_pk.larhrs?.aggregate?.sum?.mod_lb_hrs || 0 + }`} + + + 0 && ( - //
{t("appointments.labels.suggesteddates")}
- // ) - } + required: true, + //message: t("general.validation.required"), + }, + ]} + > + +
+ + + +
+ {InstanceRenderManager({ + imex: ( + <> + + {t("appointments.labels.smartscheduling")} + - - {smartOptions.map((d, idx) => ( - - ))} - - - - - - - - - - - - - - - - - - - - - - - {t("appointments.labels.history")} - - - - prev.start !== cur.start}> - {() => { - const values = form.getFieldsValue(); - if (values.start) { - calculateScheduleLoad(dayjs(values.start).add(3, "day")); + + {smartOptions.map((d, idx) => ( + + ))} +
+ + ), + rome: "USE_IMEX", + promanager: <>, + })} + + + + + + + + + + + + + + + + + + + + + + {t("appointments.labels.history")} + + + + prev.start !== cur.start}> + {() => { + const values = form.getFieldsValue(); + if (values.start) { + calculateScheduleLoad(dayjs(values.start).add(3, "day")); + } + return ( +
+ +
+ ); + }} +
+ +
); } diff --git a/client/src/index.js b/client/src/index.js index e5a472fcc..979587226 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -66,6 +66,12 @@ const router = createBrowserRouter( createRoutesFromElements(} />) ); + +if (process.env.NODE_ENV === "development") { + let styles = 'font-weight: bold; font-size: 50px;color: red; 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) ' + console.log('%c %s', styles, `VER: ${process.env.REACT_APP_INSTANCE}`) +} + function App() { return ( + {noPath && ( + ); } diff --git a/client/src/pages/accounting-payments/accounting-payments.container.jsx b/client/src/pages/accounting-payments/accounting-payments.container.jsx index aaa23540a..73b7b561a 100644 --- a/client/src/pages/accounting-payments/accounting-payments.container.jsx +++ b/client/src/pages/accounting-payments/accounting-payments.container.jsx @@ -11,6 +11,7 @@ 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 FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -61,6 +62,7 @@ export function AccountingPaymentsContainer({ ); return (
+ {noPath && ( +
); } diff --git a/client/src/pages/accounting-receivables/accounting-receivables.container.jsx b/client/src/pages/accounting-receivables/accounting-receivables.container.jsx index 90bedb512..a88195324 100644 --- a/client/src/pages/accounting-receivables/accounting-receivables.container.jsx +++ b/client/src/pages/accounting-receivables/accounting-receivables.container.jsx @@ -12,6 +12,7 @@ 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 FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -64,6 +65,7 @@ export function AccountingReceivablesContainer({ return (
+ {noPath && ( +
); } diff --git a/client/src/pages/bills/bills.page.container.jsx b/client/src/pages/bills/bills.page.container.jsx index 61f206595..315fad4b3 100644 --- a/client/src/pages/bills/bills.page.container.jsx +++ b/client/src/pages/bills/bills.page.container.jsx @@ -11,6 +11,7 @@ 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"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapDispatchToProps = (dispatch) => ({ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), @@ -55,6 +56,7 @@ export function BillsPageContainer({setBreadcrumbs, setSelectedHeader}) { if (error) return ; return ( +
+
); } diff --git a/client/src/pages/contract-detail/contract-detail.page.container.jsx b/client/src/pages/contract-detail/contract-detail.page.container.jsx index 56bbf82ab..8afb9b565 100644 --- a/client/src/pages/contract-detail/contract-detail.page.container.jsx +++ b/client/src/pages/contract-detail/contract-detail.page.container.jsx @@ -15,6 +15,7 @@ import {addRecentItem, setBreadcrumbs, setSelectedHeader,} from "../../redux/app import {CreateRecentItem} from "../../utils/create-recent-item"; import ContractDetailPageComponent from "./contract-detail.page.component"; import NotFound from "../../components/not-found/not-found.component"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapDispatchToProps = (dispatch) => ({ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), @@ -122,6 +123,7 @@ export function ContractDetailPageContainer({ if (!!!data.cccontracts_by_pk) return ; return ( +
@@ -160,6 +162,7 @@ export function ContractDetailPageContainer({
+
); } diff --git a/client/src/pages/contracts/contracts.page.container.jsx b/client/src/pages/contracts/contracts.page.container.jsx index fe64a86d1..c8f15b921 100644 --- a/client/src/pages/contracts/contracts.page.container.jsx +++ b/client/src/pages/contracts/contracts.page.container.jsx @@ -10,6 +10,7 @@ import {QUERY_ACTIVE_CONTRACTS_PAGINATED} from "../../graphql/cccontracts.querie import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions"; import ContractsPageComponent from "./contracts.page.component"; import {pageLimit} from "../../utils/config"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapDispatchToProps = (dispatch) => ({ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), @@ -56,14 +57,16 @@ export function ContractsPageContainer({setBreadcrumbs, setSelectedHeader}) { if (error) return ; return ( + - + + ); } diff --git a/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx b/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx index 896393742..3a6f55867 100644 --- a/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx +++ b/client/src/pages/courtesy-car-create/courtesy-car-create.page.container.jsx @@ -10,6 +10,7 @@ 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 FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -67,16 +68,13 @@ export function CourtesyCarCreateContainer({ }, [t, setBreadcrumbs, setSelectedHeader]); return ( - -
- - -
+ + +
+ + +
+
); } diff --git a/client/src/pages/courtesy-cars/courtesy-cars.page.container.jsx b/client/src/pages/courtesy-cars/courtesy-cars.page.container.jsx index 982d6db16..f1ffe2827 100644 --- a/client/src/pages/courtesy-cars/courtesy-cars.page.container.jsx +++ b/client/src/pages/courtesy-cars/courtesy-cars.page.container.jsx @@ -7,6 +7,7 @@ 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 CourtesyCarsPageComponent from "./courtesy-cars.page.component"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapDispatchToProps = (dispatch) => ({ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), @@ -32,13 +33,15 @@ export function CourtesyCarsPageContainer({ if (error) return ; return ( - - - + + + + + ); } diff --git a/client/src/pages/export-logs/export-logs.page.container.jsx b/client/src/pages/export-logs/export-logs.page.container.jsx index 279363bae..ae6273b75 100644 --- a/client/src/pages/export-logs/export-logs.page.container.jsx +++ b/client/src/pages/export-logs/export-logs.page.container.jsx @@ -4,6 +4,7 @@ import {connect} from "react-redux"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions"; import ExportLogsPage from "./export-logs.page.component"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapDispatchToProps = (dispatch) => ({ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), @@ -25,9 +26,11 @@ export function ExportsLogPageContainer({setBreadcrumbs, setSelectedHeader}) { }, [setBreadcrumbs, t, setSelectedHeader]); return ( + + ); } diff --git a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx index 92f257d37..788274a1e 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -52,6 +52,8 @@ import _ from "lodash"; import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component"; import {DateTimeFormat} from "../../utils/DateFormatter"; import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component"; +import InstanceRenderManager from "../../utils/instanceRenderMgr"; +import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -333,12 +335,12 @@ export function JobsDetailPage({ label: t("menus.jobsdetail.partssublet"), children: , }, - { + ...InstanceRenderManager({ imex: true, rome: true, promanager: HasFeatureAccess({ featureName: 'timetickets', bodyshop }) }) ? [ { key: "labor", icon: , label: t("menus.jobsdetail.labor"), children: , - }, + },]: [], { key: 'lifecycle', icon: , @@ -352,7 +354,7 @@ export function JobsDetailPage({ forceRender: true, children: , }, - { + ...InstanceRenderManager({ imex: true, rome: true, promanager: HasFeatureAccess({ featureName: 'media', bodyshop }) }) ? [ { key: "documents", icon: , label: t("jobs.labels.documents"), @@ -361,7 +363,7 @@ export function JobsDetailPage({ ) : ( ), - }, + },]:[], { key: "notes", icon: , @@ -402,4 +404,4 @@ const transformJobToForm = (job) => { transformedJob.date_estimated = transformedJob.date_estimated ? dayjs(transformedJob.date_estimated) : null; return transformedJob; -}; \ No newline at end of file +}; diff --git a/client/src/pages/payments-all/payments-all.container.page.jsx b/client/src/pages/payments-all/payments-all.container.page.jsx index d276b6ad1..2cfc205cb 100644 --- a/client/src/pages/payments-all/payments-all.container.page.jsx +++ b/client/src/pages/payments-all/payments-all.container.page.jsx @@ -12,6 +12,7 @@ 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"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -60,6 +61,7 @@ export function AllJobs({bodyshop, setBreadcrumbs, setSelectedHeader}) { if (error) return ; return ( + + ); } diff --git a/client/src/pages/shift-clock/shift-clock.page.jsx b/client/src/pages/shift-clock/shift-clock.page.jsx index 582f5491a..5641ec0b5 100644 --- a/client/src/pages/shift-clock/shift-clock.page.jsx +++ b/client/src/pages/shift-clock/shift-clock.page.jsx @@ -1,11 +1,14 @@ -import React from "react"; -import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; -import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container"; +import React from 'react'; +import RbacWrapper from '../../components/rbac-wrapper/rbac-wrapper.component'; +import TimeTicketShift from '../../components/time-ticket-shift/time-ticket-shift.container'; +import FeatureWrapperComponent from '../../components/feature-wrapper/feature-wrapper.component'; export default function ShiftClock() { - return ( - - - - ); + return ( + + + + + + ); } diff --git a/client/src/pages/temporary-docs/temporary-docs.container.jsx b/client/src/pages/temporary-docs/temporary-docs.container.jsx index d4c50d31f..6230fece1 100644 --- a/client/src/pages/temporary-docs/temporary-docs.container.jsx +++ b/client/src/pages/temporary-docs/temporary-docs.container.jsx @@ -6,6 +6,7 @@ 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 TemporaryDocsComponent from "./temporary-docs.component"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -31,9 +32,11 @@ export function TempDocumentsContainer({setBreadcrumbs, setSelectedHeader}) { }, [t, setBreadcrumbs, setSelectedHeader]); return ( - - - + + + + + ); } diff --git a/client/src/pages/time-tickets/time-tickets.container.jsx b/client/src/pages/time-tickets/time-tickets.container.jsx index 1d6dba1f4..0c426c622 100644 --- a/client/src/pages/time-tickets/time-tickets.container.jsx +++ b/client/src/pages/time-tickets/time-tickets.container.jsx @@ -19,6 +19,7 @@ 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"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({}); @@ -64,6 +65,7 @@ export function TimeTicketsContainer({ if (error) return ; return ( + @@ -90,10 +92,11 @@ export function TimeTicketsContainer({ + ); } export default connect( mapStateToProps, mapDispatchToProps -)(TimeTicketsContainer); \ No newline at end of file +)(TimeTicketsContainer); diff --git a/client/src/pages/tt-approvals/tt-approvals.page.container.jsx b/client/src/pages/tt-approvals/tt-approvals.page.container.jsx index dcd34ff01..92b36003d 100644 --- a/client/src/pages/tt-approvals/tt-approvals.page.container.jsx +++ b/client/src/pages/tt-approvals/tt-approvals.page.container.jsx @@ -6,6 +6,7 @@ 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 FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -31,9 +32,11 @@ export function TtApprovalsPage({setBreadcrumbs, setSelectedHeader}) { }, [t, setBreadcrumbs, setSelectedHeader]); return ( + + ); } diff --git a/client/src/utils/instanceRenderMgr.js b/client/src/utils/instanceRenderMgr.js index d6f4b82e2..8903e06f9 100644 --- a/client/src/utils/instanceRenderMgr.js +++ b/client/src/utils/instanceRenderMgr.js @@ -9,30 +9,39 @@ * @property { string | object | function } imex Return this prop if Rome. */ -export default function InstanceRenderManager({ - executeFunction, - rome, - promanager, - imex, -}) { - let propToReturn = null; +export default function InstanceRenderManager({ executeFunction, rome, promanager, imex, debug }) { + let propToReturn = null; - switch (process.env.INSTANCE) { - case "IMEX": - propToReturn = imex; - break; - case "ROME": - propToReturn = rome; - break; - case "PROMANAGER": - propToReturn = - promanager === "USE_ROME" ? rome : promanager || rome; //default promanager back to rome. - break; - default: - propToReturn = imex; - break; - } + switch (process.env.REACT_APP_INSTANCE) { + case 'IMEX': + propToReturn = imex; + break; + case 'ROME': + propToReturn = rome; + break; + case 'PROMANAGER': + //Return the rome prop if USE_ROME. + //If not USE_ROME, we want to default back to the rome prop if it's undefined. + //If null, we might want to show nothing, so make sure we return null. + propToReturn = + promanager === 'USE_ROME' ? rome : promanager !== undefined ? promanager : rome; + break; + default: + propToReturn = imex; + break; + } - if (executeFunction && typeof propToReturn === "function") propToReturn(); - return propToReturn === undefined ? null : propToReturn; + if (executeFunction && typeof propToReturn === 'function') propToReturn(); + + //Checking to see if we need to default to another one. + if (propToReturn === 'imex') { + propToReturn = imex; + } + if (debug) { + console.log('InstanceRenderManager Debugger'); + console.log('========================='); + console.log({ executeFunction, rome, promanager, imex, debug, propToReturn }); + console.log('========================='); + } + return propToReturn === undefined ? null : propToReturn; }