From 43b1ad78a329c077fe7937ba3f7778c40ba20089 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 4 Dec 2024 15:37:08 -0800 Subject: [PATCH] IO-3020 IO-3036 Additional blurred components. --- .../blur-wrapper.component.jsx | 35 +++- .../feature-wrapper.component.jsx | 10 +- .../job-lifecycle/job-lifecycle.component.jsx | 156 +++++++++++------- .../jobs-detail-header-actions.component.jsx | 28 ++-- .../jobs-delivery.page.container.jsx | 28 +++- .../jobs-detail.page.component.jsx | 4 +- .../jobs-intake.page.container.jsx | 38 +++-- 7 files changed, 192 insertions(+), 107 deletions(-) diff --git a/client/src/components/feature-wrapper/blur-wrapper.component.jsx b/client/src/components/feature-wrapper/blur-wrapper.component.jsx index fcf80b04b..67c4b8b82 100644 --- a/client/src/components/feature-wrapper/blur-wrapper.component.jsx +++ b/client/src/components/feature-wrapper/blur-wrapper.component.jsx @@ -4,6 +4,7 @@ import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { HasFeatureAccess } from "./feature-wrapper.component"; +import { DateTimeFormatterFunction } from "../../utils/DateFormatter"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -31,6 +32,7 @@ export function BlurWrapper({ if (import.meta.env.DEV) { if (!ValidateFeatureName(featureName)) console.trace("*** INVALID FEATURE NAME", featureName); } + if (debug) { console.trace("*** DEBUG MODE", featureName); console.log("*** HAS FEATURE ACCESS?", featureName, HasFeatureAccess({ featureName, bodyshop })); @@ -61,8 +63,17 @@ export function BlurWrapper({ } else { if (typeof overrideValueFunction === "function") { newValueProp = overrideValueFunction(); - } else if (overrideValueFunction === "RandomDinero") { + } else if (typeof overrideValueFunction === "string" && overrideValueFunction === "RandomDinero") { newValueProp = RandomDinero(); + } else if (typeof overrideValueFunction === "string" && overrideValueFunction === "RandomAmount") { + newValueProp = RandomAmount(); + } else if ( + typeof overrideValueFunction === "string" && + overrideValueFunction.startsWith("RandomSmallString") + ) { + newValueProp = RandomSmallString(overrideValueFunction.split(":")[1] || 3); //Default back to 3 words, otherwise use the string. + } else if (typeof overrideValueFunction === "string" && overrideValueFunction.startsWith("RandomDate")) { + newValueProp = RandomDate(); } else { newValueProp = "This is some random text. Nothing interesting here."; } @@ -86,6 +97,23 @@ export default connect(mapStateToProps, null)(BlurWrapper); function RandomDinero() { return Dinero({ amount: Math.round(Math.exp(Math.random() * 10, 2)) }).toFormat(); } +function RandomAmount() { + return Math.round(Math.exp(Math.random() * 10)); +} + +function RandomSmallString(maxWords = 3) { + const words = ["lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit"]; + const wordCount = Math.floor(Math.random() * maxWords) + 1; // Random number between 1 and 3 + let result = []; + for (let i = 0; i < wordCount; i++) { + const randomIndex = Math.floor(Math.random() * words.length); + result.push(words[randomIndex]); + } + return result.join(" "); +} +function RandomDate() { + return DateTimeFormatterFunction(new Date(Math.floor(Math.random() * 1000000000000))); +} const featureNameList = [ "mobile", @@ -104,9 +132,10 @@ const featureNameList = [ "checklist", "smartscheduling", "roguard", - "dashboard" + "dashboard", + "lifecycle" ]; -function ValidateFeatureName(featureName) { +export function ValidateFeatureName(featureName) { return featureNameList.includes(featureName); } diff --git a/client/src/components/feature-wrapper/feature-wrapper.component.jsx b/client/src/components/feature-wrapper/feature-wrapper.component.jsx index 693fb5d92..275bca222 100644 --- a/client/src/components/feature-wrapper/feature-wrapper.component.jsx +++ b/client/src/components/feature-wrapper/feature-wrapper.component.jsx @@ -1,11 +1,12 @@ -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 AlertComponent from "../alert/alert.component"; +import dayjs from "../../utils/day"; import InstanceRenderManager from "../../utils/instanceRenderMgr"; +import AlertComponent from "../alert/alert.component"; +import { ValidateFeatureName } from "./blur-wrapper.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -21,9 +22,12 @@ function FeatureWrapper({ ...restProps }) { const { t } = useTranslation(); + if (import.meta.env.DEV) { + if (!ValidateFeatureName(featureName)) console.trace("*** INVALID FEATURE NAME", featureName); + } if (upsellComponent) { - console.error("Upsell component passed in. This is not yet implemented."); + console.error("*** Upsell component passed in. This is not yet implemented."); } if (HasFeatureAccess({ featureName, bodyshop })) return children; diff --git a/client/src/components/job-lifecycle/job-lifecycle.component.jsx b/client/src/components/job-lifecycle/job-lifecycle.component.jsx index e648ad4d5..62b9391cc 100644 --- a/client/src/components/job-lifecycle/job-lifecycle.component.jsx +++ b/client/src/components/job-lifecycle/job-lifecycle.component.jsx @@ -8,6 +8,7 @@ import { isEmpty } from "lodash"; import { useTranslation } from "react-i18next"; import "./job-lifecycle.styles.scss"; +import BlurWrapperComponent from "../feature-wrapper/blur-wrapper.component"; // show text on bar if text can fit export function JobLifecycleComponent({ job, statuses, ...rest }) { @@ -65,14 +66,23 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) { { title: t("job_lifecycle.columns.value"), dataIndex: "value", - key: "value" + key: "value", + render: (text, record) => ( + + {text} + + ) }, { title: t("job_lifecycle.columns.start"), dataIndex: "start", key: "start", - render: (text) => DateTimeFormatterFunction(text), - sorter: (a, b) => dayjs(a.start).unix() - dayjs(b.start).unix() + sorter: (a, b) => dayjs(a.start).unix() - dayjs(b.start).unix(), + render: (text, record) => ( + + {DateTimeFormatterFunction(text)} + + ) }, { title: t("job_lifecycle.columns.relative_start"), @@ -92,7 +102,12 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) { } return dayjs(a.end).unix() - dayjs(b.end).unix(); }, - render: (text) => (isEmpty(text) ? t("job_lifecycle.content.not_available") : DateTimeFormatterFunction(text)) + + render: (text, record) => ( + + {isEmpty(text) ? t("job_lifecycle.content.not_available") : DateTimeFormatterFunction(text)} + + ) }, { title: t("job_lifecycle.columns.relative_end"), @@ -122,67 +137,72 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) { } style={{ width: "100%" }} > -
- {lifecycleData.durations.summations.map((key, index, array) => { - const isFirst = index === 0; - const isLast = index === array.length - 1; - return ( -
+
+ {lifecycleData.durations.summations.map((key, index, array) => { + const isFirst = index === 0; + const isLast = index === array.length - 1; + return ( +
- {key.percentage > 15 ? ( - <> -
{key.roundedPercentage}
-
- {key.status} -
- - ) : null} -
- ); - })} -
+ backgroundColor: key.color, + width: `${key.percentage}%` + }} + aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`} + title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`} + > + {key.percentage > 15 ? ( + <> +
{key.roundedPercentage}
+
+ {key.status} +
+ + ) : null} +
+ ); + })} +
+
{lifecycleData.durations.summations.map((key) => ( @@ -197,7 +217,15 @@ export function JobLifecycleComponent({ job, statuses, ...rest }) { textAlign: "center" }} > - {key.status} ({key.roundedPercentage}) + {key.status} ( + + {key.roundedPercentage} + + )
))} diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx index acbd86f16..d68854fef 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx @@ -729,15 +729,16 @@ export function JobsDetailHeaderActions({ onClick: () => { logImEXEvent("job_header_enter_time_ticekts"); - setTimeTicketContext({ - actions: {}, - context: { - jobId: job.id, - created_by: currentUser.displayName - ? currentUser.email.concat(" | ", currentUser.displayName) - : currentUser.email - } - }); + HasFeatureAccess({ featureName: "timetickets", bodyshop }) && + setTimeTicketContext({ + actions: {}, + context: { + jobId: job.id, + created_by: currentUser.displayName + ? currentUser.email.concat(" | ", currentUser.displayName) + : currentUser.email + } + }); } } ]; @@ -765,10 +766,11 @@ export function JobsDetailHeaderActions({ onClick: () => { logImEXEvent("job_header_enter_payment"); - setPaymentContext({ - actions: {}, - context: { jobid: job.id } - }); + HasFeatureAccess({ featureName: "payments", bodyshop }) && + setPaymentContext({ + actions: {}, + context: { jobid: job.id } + }); } }); diff --git a/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx b/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx index 44f82fd21..f3836831b 100644 --- a/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx +++ b/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx @@ -12,6 +12,7 @@ import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/appli import { selectBodyshop } from "../../redux/user/user.selectors"; import JobchecklistComponent from "../../components/job-checklist/job-checklist.component"; import InstanceRenderManager from "../../utils/instanceRenderMgr"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -59,15 +60,24 @@ export function JobsDeliverContainer({ bodyshop, setBreadcrumbs, setSelectedHead if (data && !!!data.bodyshops_by_pk.deliverchecklist) return ; 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 f415ee2f4..20dba22c7 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.component.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.component.jsx @@ -402,7 +402,9 @@ export function JobsDetailPage({ key: "lifecycle", icon: , id: "job-details-lifecycle", - label: t("menus.jobsdetail.lifecycle"), + label: ( + {t("menus.jobsdetail.lifecycle")} + ), children: }, { diff --git a/client/src/pages/jobs-intake/jobs-intake.page.container.jsx b/client/src/pages/jobs-intake/jobs-intake.page.container.jsx index 507a23cf7..75e7beef0 100644 --- a/client/src/pages/jobs-intake/jobs-intake.page.container.jsx +++ b/client/src/pages/jobs-intake/jobs-intake.page.container.jsx @@ -13,6 +13,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import { Result } from "antd"; import InstanceRenderManager from "../../utils/instanceRenderMgr"; +import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -63,20 +64,29 @@ export function JobsIntakeContainer({ bodyshop, setBreadcrumbs, setSelectedHeade return ; return ( - -
- {!!data.jobs_by_pk.intakechecklist || - !bodyshop.md_ro_statuses.pre_production_statuses.includes(data.jobs_by_pk.status) ? ( - - ) : ( - - )} -
-
+ + +
+ {!!data.jobs_by_pk.intakechecklist || + !bodyshop.md_ro_statuses.pre_production_statuses.includes(data.jobs_by_pk.status) ? ( + + ) : ( + + )} +
+
+
); }