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) => (
-
- ))}
-
- ) : (
- {t("documents.errors.nodocuments")}
- )}
-
+ {!hasMediaAccess && (
+
+ {data.documents.length > 0 ? (
+
+ {data.documents.map((item) => (
+
+ ))}
+
+ ) : (
+ {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:
},
{