diff --git a/client/src/components/bills-list-table/bills-list-table.component.jsx b/client/src/components/bills-list-table/bills-list-table.component.jsx index 503751268..89813d74f 100644 --- a/client/src/components/bills-list-table/bills-list-table.component.jsx +++ b/client/src/components/bills-list-table/bills-list-table.component.jsx @@ -1,6 +1,6 @@ import { EditFilled, SyncOutlined } from "@ant-design/icons"; import { Button, Card, Checkbox, Input, Space, Table } from "antd"; -import React, { useRef, useState } from "react"; +import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { FaTasks } from "react-icons/fa"; import { connect } from "react-redux"; @@ -17,8 +17,7 @@ import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit- import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import LockerWrapperComponent from "../lock-wrapper/lock-wrapper.component"; import PrintWrapperComponent from "../print-wrapper/print-wrapper.component"; -import UpsellComponent from "../upsell/upsell.component"; -import { upsellEnum } from "../upsell/upsell.component"; +import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; const mapStateToProps = createStructuredSelector({ jobRO: selectJobReadOnly, @@ -61,7 +60,6 @@ export function BillsListTableComponent({ // const search = queryString.parse(useLocation().search); // const selectedBill = search.billid; const [searchText, setSearchText] = useState(""); - const containerRef = useRef(null); const Templates = TemplateList("bill"); const bills = billsQuery.data ? billsQuery.data.bills : []; diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 5ac82ddc0..8acabfe40 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -148,7 +148,7 @@ function Header({ label: ( - {t(t("menus.header.enterbills"))} + {t("menus.header.enterbills")} ), diff --git a/client/src/components/job-detail-cards/job-detail-cards.component.jsx b/client/src/components/job-detail-cards/job-detail-cards.component.jsx index 204c956db..33315f959 100644 --- a/client/src/components/job-detail-cards/job-detail-cards.component.jsx +++ b/client/src/components/job-detail-cards/job-detail-cards.component.jsx @@ -122,7 +122,7 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) { {!bodyshop.uselocalmediaserver && ( - + )} diff --git a/client/src/components/job-detail-cards/job-detail-cards.documents.component.jsx b/client/src/components/job-detail-cards/job-detail-cards.documents.component.jsx index 3129eaab7..573662722 100644 --- a/client/src/components/job-detail-cards/job-detail-cards.documents.component.jsx +++ b/client/src/components/job-detail-cards/job-detail-cards.documents.component.jsx @@ -1,12 +1,14 @@ import { Carousel } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import { GenerateThumbUrl } from "../jobs-documents-gallery/job-documents.utility"; -import CardTemplate from "./job-detail-cards.template.component"; import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; +import CardTemplate from "./job-detail-cards.template.component"; -export default function JobDetailCardsDocumentsComponent({ loading, data }) { +export default function JobDetailCardsDocumentsComponent({ loading, data, bodyshop }) { const { t } = useTranslation(); + const hasMediaAccess = HasFeatureAccess({ bodyshop, featureName: "media" }); if (!data) return ( @@ -21,17 +23,19 @@ export default function JobDetailCardsDocumentsComponent({ loading, data }) { title={t("jobs.labels.cards.documents")} extraLink={`/manage/jobs/${data.id}?tab=documents`} > - - {data.documents.length > 0 ? ( - - {data.documents.map((item) => ( - {item.name} - ))} - - ) : ( -
{t("documents.errors.nodocuments")}
- )} -
+ {!hasMediaAccess && ( + + {data.documents.length > 0 ? ( + + {data.documents.map((item) => ( + {item.name} + ))} + + ) : ( +
{t("documents.errors.nodocuments")}
+ )} +
+ )} ); } diff --git a/client/src/components/job-lifecycle/job-lifecycle.component.jsx b/client/src/components/job-lifecycle/job-lifecycle.component.jsx index 7f99d08b5..df71c88c3 100644 --- a/client/src/components/job-lifecycle/job-lifecycle.component.jsx +++ b/client/src/components/job-lifecycle/job-lifecycle.component.jsx @@ -9,14 +9,25 @@ import { useTranslation } from "react-i18next"; import "./job-lifecycle.styles.scss"; import BlurWrapperComponent from "../feature-wrapper/blur-wrapper.component"; + import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); // show text on bar if text can fit -export function JobLifecycleComponent({ job, statuses, ...rest }) { +export function JobLifecycleComponent({ bodyshop, job, statuses, ...rest }) { const [loading, setLoading] = useState(true); const [lifecycleData, setLifecycleData] = useState(null); const { t } = useTranslation(); // Used for tracking external state changes. - + const hasLifeCycleAccess = HasFeatureAccess({ bodyshop, featureName: "lifecycle" }); const { data } = useQuery( gql` query get_job_test($id: uuid!) { @@ -143,9 +154,11 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) { } style={{ width: "100%" }} > - - - + {!hasLifeCycleAccess && ( + + + + )}
); } - -export default JobLifecycleComponent; +export default connect(mapStateToProps, mapDispatchToProps)(JobLifecycleComponent); diff --git a/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx b/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx index 2bf99c1a9..b00ff0c36 100644 --- a/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx +++ b/client/src/components/schedule-calendar-wrapper/schedule-calendar-header.component.jsx @@ -13,12 +13,12 @@ import { selectScheduleLoad, selectScheduleLoadCalculating } from "../../redux/a import { selectBodyshop } from "../../redux/user/user.selectors"; import { DateTimeFormatter } from "../../utils/DateFormatter"; import { default as BlurWrapper, default as BlurWrapperComponent } from "../feature-wrapper/blur-wrapper.component"; +import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import ScheduleBlockDay from "../schedule-block-day/schedule-block-day.component"; +import UpsellComponent, { upsellEnum } from "../upsell/upsell.component"; import ScheduleCalendarHeaderGraph from "./schedule-calendar-header-graph.component"; -import UpsellComponent, { upsellEnum, UpsellMaskWrapper } from "../upsell/upsell.component"; -import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, diff --git a/client/src/components/upsell/upsell.component.jsx b/client/src/components/upsell/upsell.component.jsx index 8857a278d..652cbeab9 100644 --- a/client/src/components/upsell/upsell.component.jsx +++ b/client/src/components/upsell/upsell.component.jsx @@ -13,6 +13,9 @@ import { Button, Card, Result } from "antd"; import i18n from "i18next"; import React, { useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; +import { store } from "../../redux/store.js"; +import InstanceRenderManager from "../../utils/instanceRenderMgr.js"; import "./upsell.styles.scss"; export default function UpsellComponent({ featureName, subFeatureName, upsell, disableMask }) { @@ -64,174 +67,275 @@ export function UpsellMaskWrapper({ children, upsell, featureName, subFeatureNam
); } + //This is kept in this function as pulling it out into it's own util/enum prevents passing JSX as an `extra` prop -export const upsellEnum = () => ({ - bills: { - autoreconcile: { - //icon: null, - title: i18n.t("upsell.messages.bills.autoreconcile.title"), - subTitle: i18n.t("upsell.messages.bills.autoreconcile.subtitle"), - extra: - //status: null +export const upsellEnum = () => { + const { currentUser, bodyshop } = store.getState().user; + + const [first_name, ...last_name] = currentUser?.displayName ? currentUser.displayName.split(" ") : []; + const LearnMoreLink = encodeURI( + InstanceRenderManager({ + imex: `https://imexsystems.ca/schedule-a-demo/`, + rome: `https://forms.zohopublic.com/rometech/form/ROLearnMore/formperma/0G29z8LgLlvKK8nno-b7s-GHgNXwIFlrMeE0mC394L4?first_name=${first_name || ""}&last_name=${last_name.join(" ") || ""}&shop_name=${bodyshop?.shopname || ""}&email=${currentUser?.email || ""}&shop_phone=${bodyshop?.phone || ""}` + }) + ); + + return { + bills: { + autoreconcile: { + //icon: null, + title: i18n.t("upsell.messages.bills.autoreconcile.title"), + subTitle: i18n.t("upsell.messages.bills.autoreconcile.subtitle"), + extra: ( + + + + ) + //status: null + }, + general: { + //icon: null, + title: i18n.t("upsell.messages.bills.general.title"), + subTitle: i18n.t("upsell.messages.bills.general.subtitle"), + extra: ( + + + + ) + //status: null + } }, - general: { - //icon: null, - title: i18n.t("upsell.messages.bills.general.title"), - subTitle: i18n.t("upsell.messages.bills.general.subtitle"), - extra: - //status: null - } - }, - checklist: { - general: { - //icon: null, - title: i18n.t("upsell.messages.checklist.general.title"), - subTitle: i18n.t("upsell.messages.checklist.general.subtitle"), - extra: - //status: null - } - }, - payments: { - general: { - //icon: null, - title: i18n.t("upsell.messages.payments.general.title"), - subTitle: i18n.t("upsell.messages.payments.general.subtitle"), - extra: - //status: null - } - }, - audit: { - general: { - //icon: null, - title: i18n.t("upsell.messages.audit.general.title"), - subTitle: i18n.t("upsell.messages.audit.general.subtitle"), - extra: - //status: null - } - }, - lifecycle: { - general: { - //icon: null, - title: i18n.t("upsell.messages.lifecycle.general.title"), - subTitle: i18n.t("upsell.messages.lifecycle.general.subtitle"), - extra: - //status: null } - } - }, - media: { - general: { - //icon: null, - title: i18n.t("upsell.messages.media.general.title"), - subTitle: i18n.t("upsell.messages.media.general.subtitle"), - extra: - //status: null } - }, - mobile: { - icon: , - title: i18n.t("upsell.messages.media.mobile.title"), - subTitle: i18n.t("upsell.messages.media.mobile.subtitle"), - extra: - //status: null } - } - }, - timetickets: { - allocations: { - title: i18n.t("upsell.messages.timetickets.allocations.title"), - subTitle: i18n.t("upsell.messages.timetickets.allocations.subtitle"), - extra: - }, - general: { - title: i18n.t("upsell.messages.timetickets.general.title"), - subTitle: i18n.t("upsell.messages.timetickets.general.subtitle"), - extra: - } - }, - smartscheduling: { - general: { - icon: , - title: i18n.t("upsell.messages.smartscheduling.general.title"), - subTitle: i18n.t("upsell.messages.smartscheduling.general.subtitle"), - extra: - }, - hrsdelta: { - icon: , - title: i18n.t("upsell.messages.smartscheduling.hrsdelta.title"), - subTitle: i18n.t("upsell.messages.smartscheduling.hrsdelta.subtitle"), - extra: - }, - datepicker: { - icon: , - title: i18n.t("upsell.messages.smartscheduling.datepicker.title"), - subTitle: i18n.t("upsell.messages.smartscheduling.datepicker.subtitle"), - extra: - } - }, - accounting: { - payables: { - icon: , - title: i18n.t("upsell.messages.accounting.payables.title"), - subTitle: i18n.t("upsell.messages.accounting.payables.subtitle"), - extra: - }, - receivables: { - icon: , - title: i18n.t("upsell.messages.accounting.receivables.title"), - subTitle: i18n.t("upsell.messages.accounting.receivables.subtitle"), - extra: + checklist: { + general: { + //icon: null, + title: i18n.t("upsell.messages.checklist.general.title"), + subTitle: i18n.t("upsell.messages.checklist.general.subtitle"), + extra: ( + + + + ) + //status: null + } }, payments: { - icon: , - title: i18n.t("upsell.messages.accounting.payments.title"), - subTitle: i18n.t("upsell.messages.accounting.payments.subtitle"), - extra: + general: { + //icon: null, + title: i18n.t("upsell.messages.payments.general.title"), + subTitle: i18n.t("upsell.messages.payments.general.subtitle"), + extra: ( + + + + ) + //status: null + } + }, + audit: { + general: { + //icon: null, + title: i18n.t("upsell.messages.audit.general.title"), + subTitle: i18n.t("upsell.messages.audit.general.subtitle"), + extra: ( + + + + ) + //status: null + } + }, + lifecycle: { + general: { + //icon: null, + title: i18n.t("upsell.messages.lifecycle.general.title"), + subTitle: i18n.t("upsell.messages.lifecycle.general.subtitle"), + extra: ( + + + + ) + //status: null } + } + }, + media: { + general: { + //icon: null, + title: i18n.t("upsell.messages.media.general.title"), + subTitle: i18n.t("upsell.messages.media.general.subtitle"), + extra: ( + + + + ) + //status: null } + }, + mobile: { + icon: , + title: i18n.t("upsell.messages.media.mobile.title"), + subTitle: i18n.t("upsell.messages.media.mobile.subtitle"), + extra: ( + + + + ) + //status: null } + } + }, + timetickets: { + allocations: { + title: i18n.t("upsell.messages.timetickets.allocations.title"), + subTitle: i18n.t("upsell.messages.timetickets.allocations.subtitle"), + extra: ( + + + + ) + }, + general: { + title: i18n.t("upsell.messages.timetickets.general.title"), + subTitle: i18n.t("upsell.messages.timetickets.general.subtitle"), + extra: ( + + + + ) + } + }, + smartscheduling: { + general: { + icon: , + title: i18n.t("upsell.messages.smartscheduling.general.title"), + subTitle: i18n.t("upsell.messages.smartscheduling.general.subtitle"), + extra: ( + + + + ) + }, + hrsdelta: { + icon: , + title: i18n.t("upsell.messages.smartscheduling.hrsdelta.title"), + subTitle: i18n.t("upsell.messages.smartscheduling.hrsdelta.subtitle"), + extra: ( + + + + ) + }, + datepicker: { + icon: , + title: i18n.t("upsell.messages.smartscheduling.datepicker.title"), + subTitle: i18n.t("upsell.messages.smartscheduling.datepicker.subtitle"), + extra: ( + + + + ) + } + }, + accounting: { + payables: { + icon: , + title: i18n.t("upsell.messages.accounting.payables.title"), + subTitle: i18n.t("upsell.messages.accounting.payables.subtitle"), + extra: ( + + + + ) + }, + receivables: { + icon: , + title: i18n.t("upsell.messages.accounting.receivables.title"), + subTitle: i18n.t("upsell.messages.accounting.receivables.subtitle"), + extra: ( + + + + ) + }, + payments: { + icon: , + title: i18n.t("upsell.messages.accounting.payments.title"), + subTitle: i18n.t("upsell.messages.accounting.payments.subtitle"), + extra: ( + + + + ) + } + }, + courtesycars: { + general: { + icon: , + title: i18n.t("upsell.messages.courtesycars.general.title"), + subTitle: i18n.t("upsell.messages.courtesycars.general.subtitle"), + extra: ( + + + + ) + } + }, + dashboard: { + general: { + icon: , + title: i18n.t("upsell.messages.dashboard.general.title"), + subTitle: i18n.t("upsell.messages.dashboard.general.subtitle"), + extra: ( + + + + ) + } + }, + visualboard: { + general: { + icon: , + title: i18n.t("upsell.messages.visualboard.general.title"), + subTitle: i18n.t("upsell.messages.visualboard.general.subtitle"), + extra: ( + + + + ) + } + }, + scoreboard: { + general: { + icon: , + title: i18n.t("upsell.messages.scoreboard.general.title"), + subTitle: i18n.t("upsell.messages.scoreboard.general.subtitle"), + extra: ( + + + + ) + } + }, + techconsole: { + general: { + icon: , + title: i18n.t("upsell.messages.techconsole.general.title"), + subTitle: i18n.t("upsell.messages.techconsole.general.subtitle"), + extra: ( + + + + ) + } + }, + csi: { + general: { + icon: , + title: i18n.t("upsell.messages.csi.general.title"), + subTitle: i18n.t("upsell.messages.csi.general.subtitle"), + extra: ( + + + + ) + } } - }, - courtesycars: { - general: { - icon: , - title: i18n.t("upsell.messages.courtesycars.general.title"), - subTitle: i18n.t("upsell.messages.courtesycars.general.subtitle"), - extra: - } - }, - dashboard: { - general: { - icon: , - title: i18n.t("upsell.messages.dashboard.general.title"), - subTitle: i18n.t("upsell.messages.dashboard.general.subtitle"), - extra: - } - }, - visualboard: { - general: { - icon: , - title: i18n.t("upsell.messages.visualboard.general.title"), - subTitle: i18n.t("upsell.messages.visualboard.general.subtitle"), - extra: - } - }, - scoreboard: { - general: { - icon: , - title: i18n.t("upsell.messages.scoreboard.general.title"), - subTitle: i18n.t("upsell.messages.scoreboard.general.subtitle"), - extra: - } - }, - techconsole: { - general: { - icon: , - title: i18n.t("upsell.messages.techconsole.general.title"), - subTitle: i18n.t("upsell.messages.techconsole.general.subtitle"), - extra: - } - }, - csi: { - general: { - icon: , - title: i18n.t("upsell.messages.csi.general.title"), - subTitle: i18n.t("upsell.messages.csi.general.subtitle"), - extra: - } - } -}); + }; +}; 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 320c2903c..eded63067 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -20,7 +20,6 @@ import { FaHardHat, FaRegStickyNote, FaShieldAlt, FaTasks } from "react-icons/fa import { connect } from "react-redux"; import { useLocation, useNavigate } from "react-router-dom"; import { createStructuredSelector } from "reselect"; -import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component"; 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"; @@ -42,6 +41,7 @@ import JobsDetailTotals from "../../components/jobs-detail-totals/jobs-detail-to 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 JobNotesContainer from "../../components/jobs-notes/jobs-notes.container"; +import LockWrapperComponent from "../../components/lock-wrapper/lock-wrapper.component.jsx"; import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container"; import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container"; import TaskListContainer from "../../components/task-list/task-list.container.jsx"; @@ -54,9 +54,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import { DateTimeFormat } from "../../utils/DateFormatter"; import dayjs from "../../utils/day"; -import InstanceRenderManager from "../../utils/instanceRenderMgr"; import UndefinedToNull from "../../utils/undefinedtonull"; -import LockWrapperComponent from "../../components/lock-wrapper/lock-wrapper.component.jsx"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -403,7 +401,9 @@ export function JobsDetailPage({ icon: , id: "job-details-lifecycle", label: ( - {t("menus.jobsdetail.lifecycle")} + + {t("menus.jobsdetail.lifecycle")} + ), children: }, @@ -438,7 +438,11 @@ export function JobsDetailPage({ key: "audit", icon: , id: "job-details-audit", - label: {t("jobs.labels.audit")}, + label: ( + + {t("jobs.labels.audit")} + + ), children: }, {