Merge branch 'release/AIO/2024-05-10' into test-AIO

This commit is contained in:
Patrick Fic
2024-05-08 15:16:55 -07:00
6 changed files with 837 additions and 815 deletions

View File

@@ -13,6 +13,7 @@ import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-forma
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr"; import InstanceRenderManager from "../../utils/instanceRenderMgr";
import FeatureWrapper, { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
// TODO: Client Update, this might break // TODO: Client Update, this might break
const timeZonesList = Intl.supportedValuesOf("timeZone"); const timeZonesList = Intl.supportedValuesOf("timeZone");
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -144,6 +145,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<FeatureWrapper featureName="export" noauth={() => null}>
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup"> <LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}> <Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
<Switch /> <Switch />
@@ -364,6 +366,8 @@ export function ShopInfoGeneral({ form, bodyshop }) {
</> </>
)} )}
</LayoutFormRow> </LayoutFormRow>
</FeatureWrapper>
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup"> <LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
<Form.Item <Form.Item
label={t("bodyshop.fields.dailypainttarget")} label={t("bodyshop.fields.dailypainttarget")}
@@ -423,6 +427,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
<InputNumber min={1} precision={1} /> <InputNumber min={1} precision={1} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
</FeatureWrapper>
<LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings"> <LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings">
<Form.Item <Form.Item
name={["md_referral_sources"]} name={["md_referral_sources"]}
@@ -567,6 +572,9 @@ export function ShopInfoGeneral({ form, bodyshop }) {
> >
<Select mode="tags" /> <Select mode="tags" />
</Form.Item> </Form.Item>
{HasFeatureAccess({ featureName: "timetickets", bodyshop }) && (
<>
<Form.Item <Form.Item
name={["tt_allow_post_to_invoiced"]} name={["tt_allow_post_to_invoiced"]}
label={t("bodyshop.fields.tt_allow_post_to_invoiced")} label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
@@ -588,6 +596,8 @@ export function ShopInfoGeneral({ form, bodyshop }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
</>
)}
<Form.Item <Form.Item
name={["md_ded_notes"]} name={["md_ded_notes"]}
label={t("bodyshop.fields.md_ded_notes")} label={t("bodyshop.fields.md_ded_notes")}
@@ -1042,6 +1052,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<FeatureWrapper featureName="courtesycars" noauth={() => null}>
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates"> <LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates">
<Form.List name={["md_ccc_rates"]}> <Form.List name={["md_ccc_rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
@@ -1053,7 +1064,11 @@ export function ShopInfoGeneral({ form, bodyshop }) {
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}> <Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label={t("contracts.fields.actax")} key={`${index}actax`} name={[field.name, "actax"]}> <Form.Item
label={t("contracts.fields.actax")}
key={`${index}actax`}
name={[field.name, "actax"]}
>
<InputNumber precision={2} /> <InputNumber precision={2} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@@ -1147,6 +1162,8 @@ export function ShopInfoGeneral({ form, bodyshop }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
</FeatureWrapper>
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")} id="md_jobline_presets"> <LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")} id="md_jobline_presets">
<Form.List name={["md_jobline_presets"]}> <Form.List name={["md_jobline_presets"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {

View File

@@ -1,12 +1,13 @@
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Form, InputNumber } from "antd"; import { Form, InputNumber } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop bodyshop: selectBodyshop
@@ -29,6 +30,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
return ( return (
<RbacWrapper action="shop:rbac"> <RbacWrapper action="shop:rbac">
<LayoutFormRow> <LayoutFormRow>
{HasFeatureAccess({ featureName: "export", bodyshop }) && (
<>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.exportlog")} label={t("bodyshop.fields.rbac.accounting.exportlog")}
rules={[ rules={[
@@ -77,6 +80,10 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
</>
)}
{HasFeatureAccess({ featureName: "bills", bodyshop }) && (
<>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.delete")} label={t("bodyshop.fields.rbac.bills.delete")}
rules={[ rules={[
@@ -137,6 +144,11 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
</>
)}
{HasFeatureAccess({ featureName: "courtesycars", bodyshop }) && (
<>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.contracts.create")} label={t("bodyshop.fields.rbac.contracts.create")}
rules={[ rules={[
@@ -209,6 +221,10 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
</>
)}
{HasFeatureAccess({ featureName: "csi", bodyshop }) && (
<>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.csi.export")} label={t("bodyshop.fields.rbac.csi.export")}
rules={[ rules={[
@@ -233,6 +249,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
</>
)}
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.employees.page")} label={t("bodyshop.fields.rbac.employees.page")}
rules={[ rules={[
@@ -425,18 +443,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:list"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.employees.page")} label={t("bodyshop.fields.rbac.employees.page")}
rules={[ rules={[
@@ -510,6 +516,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
{HasFeatureAccess({ featureName: "visualboard", bodyshop }) && (
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.production.board")} label={t("bodyshop.fields.rbac.production.board")}
rules={[ rules={[
@@ -522,6 +529,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
)}
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.production.list")} label={t("bodyshop.fields.rbac.production.list")}
rules={[ rules={[
@@ -546,6 +555,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
{HasFeatureAccess({ featureName: "scoreboard", bodyshop }) && (
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.scoreboard.view")} label={t("bodyshop.fields.rbac.scoreboard.view")}
rules={[ rules={[
@@ -558,6 +568,9 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
)}
{HasFeatureAccess({ featureName: "timetickets", bodyshop }) && (
<>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shiftclock.view")} label={t("bodyshop.fields.rbac.shiftclock.view")}
rules={[ rules={[
@@ -642,6 +655,44 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.enter")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:enter"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:list"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>
</>
)}
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shop.vendors")} label={t("bodyshop.fields.rbac.shop.vendors")}
rules={[ rules={[
@@ -690,7 +741,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item {/* <Form.Item
label={t("bodyshop.fields.rbac.shop.templates")} label={t("bodyshop.fields.rbac.shop.templates")}
rules={[ rules={[
{ {
@@ -701,19 +752,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "shop:templates"]} name={["md_rbac", "shop:templates"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item> */}
<Form.Item {HasFeatureAccess({ featureName: "media", bodyshop }) && (
label={t("bodyshop.fields.rbac.shop.vendors")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "shop:vendors"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.temporarydocs.view")} label={t("bodyshop.fields.rbac.temporarydocs.view")}
rules={[ rules={[
@@ -726,54 +766,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item )}
label={t("bodyshop.fields.rbac.timetickets.edit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:edit"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.enter")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:enter"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:list"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.users.editaccess")} label={t("bodyshop.fields.rbac.users.editaccess")}
rules={[ rules={[

View File

@@ -339,7 +339,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<div> <div>
<LayoutFormRow> <Space size='large' wrap>
{fields.map((field, index) => ( {fields.map((field, index) => (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<Space direction="vertical"> <Space direction="vertical">
@@ -386,7 +386,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
</Space> </Space>
</Form.Item> </Form.Item>
))} ))}
</LayoutFormRow> </Space>
<Form.Item> <Form.Item>
<Button <Button
type="dashed" type="dashed"

View File

@@ -1,4 +1,4 @@
import { Button, Result } from "antd"; import { Result } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -16,19 +16,5 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShopSubStatus);
export function ShopSubStatus({ bodyshop }) { export function ShopSubStatus({ bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { sub_status } = bodyshop; const { sub_status } = bodyshop;
return ( return <Result status="403" title={t(`general.labels.sub_status.${sub_status}`)} />;
<Result
status="403"
title={t(`general.labels.sub_status.${sub_status}`)}
extra={
<Button
onClick={() => {
alert("Not implemented yet.");
}}
>
{t("general.actions.submitticket")}
</Button>
}
/>
);
} }

View File

@@ -72,11 +72,18 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
}); });
} }
InstanceRenderManager({
executeFunction: true,
args: [],
imex: () => {
items.push({ items.push({
key: "licensing", key: "licensing",
label: t("bodyshop.labels.licensing"), label: t("bodyshop.labels.licensing"),
children: <ShopInfoUsersComponent /> children: <ShopInfoUsersComponent />
}); });
},
rome: "USE_IMEX"
});
if (HasFeatureAccess({ featureName: "csi", bodyshop })) { if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
items.push({ items.push({

View File

@@ -15,6 +15,7 @@ import { getToken } from "firebase/messaging";
import i18next from "i18next"; import i18next from "i18next";
import LogRocket from "logrocket"; import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects"; import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
import { Userpilot } from "userpilot";
import { factory } from "../../App/App.container"; import { factory } from "../../App/App.container";
import { import {
analytics, analytics,
@@ -25,6 +26,10 @@ import {
messaging, messaging,
updateCurrentUser updateCurrentUser
} from "../../firebase/firebase.utils"; } from "../../firebase/firebase.utils";
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
import client from "../../utils/GraphQLClient";
import day from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { import {
checkInstanceId, checkInstanceId,
sendPasswordResetFailure, sendPasswordResetFailure,
@@ -43,11 +48,6 @@ import {
validatePasswordResetSuccess validatePasswordResetSuccess
} from "./user.actions"; } from "./user.actions";
import UserActionTypes from "./user.types"; import UserActionTypes from "./user.types";
import client from "../../utils/GraphQLClient";
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
import day from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { Userpilot } from "userpilot";
const fpPromise = FingerprintJS.load(); const fpPromise = FingerprintJS.load();
@@ -310,10 +310,28 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
updateUserDetailsSuccess(authRecord[0] ? { validemail: authRecord[0].user.validemail } : { validemail: false }) updateUserDetailsSuccess(authRecord[0] ? { validemail: authRecord[0].user.validemail } : { validemail: false })
); );
if (payload.features.singleDeviceOnly) {
const user = yield select((state) => state.user.currentUser); const user = yield select((state) => state.user.currentUser);
if (payload.features.singleDeviceOnly) {
if (!(user.email.includes("@imex.") || user.email.includes("@rome.") || user.email.includes("@promanager.")))
yield put(setInstanceId(user.uid));
}
if (!(user.email.includes("@imex.") || user.email.includes("@rome."))) yield put(setInstanceId(user.uid)); //For Rome, check to make sure it's not a PM shop.
try {
InstanceRenderManager({
executeFunction: true,
args: [],
rome: () => {
if (
payload.imexshopid.toLowerCase().startsWith("pm_") &&
!(user.email.includes("@imex.") || user.email.includes("@rome.") || user.email.includes("@promanager."))
) {
throw new Error("You are not authorized to use this application.");
}
}
});
} catch (error) {
yield put(setInstanceConflict());
} }
try { try {