IO-3020 IO-3036 Add basic upsell component and blur out reports.
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Card, Checkbox, Input, Space, Table } from "antd";
|
import { Button, Card, Checkbox, Input, Space, Table } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { FaTasks } from "react-icons/fa";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
@@ -13,10 +14,11 @@ import { alphaSort, dateSort } from "../../utils/sorters";
|
|||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
||||||
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
|
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
|
||||||
import { FaTasks } from "react-icons/fa";
|
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import LockerWrapperComponent from "../lock-wrapper/lock-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.enum";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
@@ -59,6 +61,7 @@ export function BillsListTableComponent({
|
|||||||
// const search = queryString.parse(useLocation().search);
|
// const search = queryString.parse(useLocation().search);
|
||||||
// const selectedBill = search.billid;
|
// const selectedBill = search.billid;
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
const Templates = TemplateList("bill");
|
const Templates = TemplateList("bill");
|
||||||
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
||||||
@@ -237,14 +240,7 @@ export function BillsListTableComponent({
|
|||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
locale={{
|
locale={{
|
||||||
...(!hasBillsAccess && {
|
...(!hasBillsAccess && {
|
||||||
emptyText: (
|
emptyText: <UpsellComponent upsell={upsellEnum.bills.table} />
|
||||||
<div>
|
|
||||||
Upsell
|
|
||||||
{
|
|
||||||
//TODO:Upsell
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { TemplateList } from "../../utils/TemplateConstants";
|
|||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import EmployeeSearchSelectEmail from "../employee-search-select/employee-search-select-email.component";
|
import EmployeeSearchSelectEmail from "../employee-search-select/employee-search-select-email.component";
|
||||||
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
||||||
|
import BlurWrapperComponent from "../feature-wrapper/blur-wrapper.component";
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||||
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
||||||
@@ -130,9 +131,12 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
const grouped = _.groupBy(FilteredReportsList, "group");
|
const grouped = _.groupBy(FilteredReportsList, "group");
|
||||||
|
|
||||||
const groupExcludeKeyFilter = [
|
const groupExcludeKeyFilter = [
|
||||||
...(!HasFeatureAccess({ featureName: "bills", bodyshop }) ? ["purchases"] : []),
|
...(!HasFeatureAccess({ featureName: "bills", bodyshop }) ? [{ key: "purchases", featureName: "bills" }] : []),
|
||||||
...(!HasFeatureAccess({ featureName: "timetickets", bodyshop }) ? ["payroll"] : [])
|
...(!HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||||
|
? [{ key: "payroll", featureName: "timetickets" }]
|
||||||
|
: [])
|
||||||
];
|
];
|
||||||
|
|
||||||
//TODO: Find a way to filter out / blur on demand.
|
//TODO: Find a way to filter out / blur on demand.
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -165,15 +169,31 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography.Title level={4}>{t(`reportcenter.labels.groups.${key}`)}</Typography.Title>
|
<Typography.Title level={4}>{t(`reportcenter.labels.groups.${key}`)}</Typography.Title>
|
||||||
<ul style={{ listStyleType: "none", columns: "2 auto" }}>
|
{groupExcludeKeyFilter.find((g) => g.key === key) ? (
|
||||||
{grouped[key].map((item) => (
|
<BlurWrapperComponent
|
||||||
<li key={item.key}>
|
featureName={groupExcludeKeyFilter.find((g) => g.key === key).featureName}
|
||||||
<Radio key={item.key} value={item.key}>
|
>
|
||||||
{item.title}
|
<ul style={{ listStyleType: "none", columns: "2 auto" }}>
|
||||||
</Radio>
|
{grouped[key].map((item) => (
|
||||||
</li>
|
<li key={item.key}>
|
||||||
))}
|
<Radio key={item.key} value={item.key}>
|
||||||
</ul>
|
{item.title}
|
||||||
|
</Radio>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</BlurWrapperComponent>
|
||||||
|
) : (
|
||||||
|
<ul style={{ listStyleType: "none", columns: "2 auto" }}>
|
||||||
|
{grouped[key].map((item) => (
|
||||||
|
<li key={item.key}>
|
||||||
|
<Radio key={item.key} value={item.key}>
|
||||||
|
{item.title}
|
||||||
|
</Radio>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
</Card.Grid>
|
</Card.Grid>
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
))}
|
||||||
@@ -278,7 +298,8 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
{
|
{
|
||||||
validator: (_, value) => {
|
validator: (_, value) => {
|
||||||
if (
|
if (
|
||||||
(!import.meta.env.VITE_APP_IS_TEST && import.meta.env.PROD) &&
|
!import.meta.env.VITE_APP_IS_TEST &&
|
||||||
|
import.meta.env.PROD &&
|
||||||
value &&
|
value &&
|
||||||
value[0] &&
|
value[0] &&
|
||||||
value[1]
|
value[1]
|
||||||
|
|||||||
39
client/src/components/upsell/upsell.component.jsx
Normal file
39
client/src/components/upsell/upsell.component.jsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { AppstoreAddOutlined } from "@ant-design/icons";
|
||||||
|
import { Result } from "antd";
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import upsellEnum from "./upsell.enum";
|
||||||
|
|
||||||
|
export default function UpsellComponent({ featureName, subFeatureName, upsell, disableMask = false }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const resultProps = upsell || upsellEnum[featureName][subFeatureName];
|
||||||
|
const componentRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (disableMask) return;
|
||||||
|
const parentElement = componentRef.current?.parentElement;
|
||||||
|
if (parentElement) {
|
||||||
|
const mask = document.createElement("div");
|
||||||
|
mask.style.position = "absolute";
|
||||||
|
mask.style.top = 0;
|
||||||
|
mask.style.left = 0;
|
||||||
|
mask.style.width = "100%";
|
||||||
|
mask.style.height = "100%";
|
||||||
|
mask.style.backgroundColor = "rgba(0, 0, 0, 0.25)";
|
||||||
|
mask.style.zIndex = 9999;
|
||||||
|
parentElement.style.position = "relative";
|
||||||
|
parentElement.appendChild(mask);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
parentElement.removeChild(mask);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!resultProps) return <Result status="info" title={t("upsell.messages.generic")} />;
|
||||||
|
return (
|
||||||
|
<div ref={componentRef}>
|
||||||
|
<Result status="info" icon={<AppstoreAddOutlined />} {...resultProps} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
34
client/src/components/upsell/upsell.enum.js
Normal file
34
client/src/components/upsell/upsell.enum.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import i18n from "i18next";
|
||||||
|
|
||||||
|
const upsellEnum = {
|
||||||
|
bills: {
|
||||||
|
postbills: {
|
||||||
|
icon: null,
|
||||||
|
title: i18n.t("upsell.messages.bills.postbills.title"),
|
||||||
|
subTitle: i18n.t("upsell.messages.bills.postbills.subtitle"),
|
||||||
|
status: null //Nullable
|
||||||
|
},
|
||||||
|
reconcile: {
|
||||||
|
icon: null,
|
||||||
|
title: i18n.t("upsell.messages.bills.reconcile.title"),
|
||||||
|
subTitle: i18n.t("upsell.messages.bills.reconcile.subtitle"),
|
||||||
|
status: null //Nullable
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
//icon: null,
|
||||||
|
title: i18n.t("upsell.messages.bills.table.title"),
|
||||||
|
subTitle: i18n.t("upsell.messages.bills.table.subtitle"),
|
||||||
|
//status: null //Nullable
|
||||||
|
}
|
||||||
|
},
|
||||||
|
timetickets: {
|
||||||
|
table: {
|
||||||
|
icon: null,
|
||||||
|
title: i18n.t("upsell.messages.timetickets.table.title"),
|
||||||
|
subTitle: i18n.t("upsell.messages.timetickets.table.subtitle"),
|
||||||
|
status: null //Nullable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default upsellEnum;
|
||||||
Reference in New Issue
Block a user