Added basic RBAC component BOD-232
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -7309,6 +7309,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>rbacunauth</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>unsavedchanges</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20385,6 +20406,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>accounting-payments</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>accounting-receivables</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20451,6 +20493,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>accounting-payments</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>accounting-receivables</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"moment-business-days": "^1.2.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"phone": "^2.4.14",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^6.13.1",
|
||||
"react": "^16.13.1",
|
||||
"react-apollo": "^3.1.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon, {
|
||||
ClockCircleFilled,
|
||||
CarFilled,
|
||||
ClockCircleFilled,
|
||||
DollarCircleFilled,
|
||||
FileAddFilled,
|
||||
FileFilled,
|
||||
@@ -16,15 +16,15 @@ import { FaCalendarAlt, FaCarCrash, FaCreditCard } from "react-icons/fa";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectRecentItems } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { signOutStart } from "../../redux/user/user.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import "./header.styles.scss";
|
||||
import GlobalSearch from "../global-search/global-search.component";
|
||||
import { selectRecentItems } from "../../redux/application/application.selectors";
|
||||
import "./header.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -270,11 +270,13 @@ function Header({
|
||||
<Menu.Item key="shop">
|
||||
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item key="shop-templates">
|
||||
<Link to="/manage/shop/templates">
|
||||
{t("menus.header.shop_templates")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item key="shop-vendors">
|
||||
<Link to="/manage/shop/vendors">
|
||||
{t("menus.header.shop_vendors")}
|
||||
|
||||
62
client/src/components/rbac-wrapper/rbac-defaults.js
Normal file
62
client/src/components/rbac-wrapper/rbac-defaults.js
Normal file
@@ -0,0 +1,62 @@
|
||||
export default {
|
||||
"accounting:payables": 1,
|
||||
"accounting:payments": 1,
|
||||
"accounting:receivables": 1,
|
||||
|
||||
"csi:page": 6,
|
||||
"csi:export": 5,
|
||||
|
||||
"contracts:create": 2,
|
||||
"contracts:detail": 2,
|
||||
"contracts:list": 2,
|
||||
|
||||
"courtesycar:create": 2,
|
||||
"courtesycar:detail": 2,
|
||||
"courtesycar:list": 2,
|
||||
|
||||
"jobs:list-active": 1,
|
||||
"jobs:list-all": 2,
|
||||
"jobs:available-list": 2,
|
||||
"jobs:create": 1,
|
||||
"jobs:intake": 1,
|
||||
"jobs:line-edit": 3,
|
||||
"jobs:scoreboard-add": 3,
|
||||
"jobs:close": 5,
|
||||
"jobs:documents-upload": 3,
|
||||
"jobs:documents-view": 2,
|
||||
"jobs:audit-trail": 5,
|
||||
"jobs:detail": 1,
|
||||
|
||||
"invoices:enter": 2,
|
||||
"invoices:view": 2,
|
||||
"invoices:list": 2,
|
||||
|
||||
"employees:rate": 5,
|
||||
"employees:page": 5,
|
||||
|
||||
"messaging:affix": 2,
|
||||
|
||||
"owners:list": 2,
|
||||
"owners:detail": 3,
|
||||
|
||||
"payments:enter": 3,
|
||||
"payments:list": 3,
|
||||
|
||||
"production:board": 1,
|
||||
"production:list": 1,
|
||||
|
||||
"sendemail:send": 1,
|
||||
|
||||
"schedule:view": 2,
|
||||
|
||||
"scoreboard:view": 3,
|
||||
|
||||
"shiftclock:view": 2,
|
||||
|
||||
"shop:vendors": 2,
|
||||
"shop:rbac": 5,
|
||||
"shop:templates": 4,
|
||||
|
||||
"timetickets:enter": 3,
|
||||
"timetickets:list": 3,
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
selectAuthLevel,
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import rbacDefaults from "./rbac-defaults";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
authLevel: selectAuthLevel,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
function RbacWrapper({
|
||||
currentUser,
|
||||
authLevel,
|
||||
bodyshop,
|
||||
requiredAuthLevel,
|
||||
noauth,
|
||||
children,
|
||||
action,
|
||||
dispatch,
|
||||
...restProps
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
if (
|
||||
(requiredAuthLevel && requiredAuthLevel <= authLevel) ||
|
||||
(bodyshop.md_rbac && bodyshop.md_rbac[action] <= authLevel) ||
|
||||
(!!!bodyshop.md_rbac && rbacDefaults[action] <= authLevel)
|
||||
)
|
||||
return <div>{React.cloneElement(children, restProps)}</div>;
|
||||
|
||||
return (
|
||||
noauth || (
|
||||
<AlertComponent
|
||||
message={t("general.messages.rbacunauth")}
|
||||
type="warning"
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
RbacWrapper.propTypes = {
|
||||
currentUser: PropTypes.object.isRequired,
|
||||
authLevel: PropTypes.number.isRequired,
|
||||
noauth: PropTypes.oneOfType(PropTypes.string, PropTypes.func),
|
||||
requiredAuthLevel: PropTypes.number,
|
||||
action: PropTypes.string,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, null)(RbacWrapper);
|
||||
@@ -1,4 +1,7 @@
|
||||
import Icon from "@ant-design/icons";
|
||||
import { Statistic } from "antd";
|
||||
import React from "react";
|
||||
import { MdFileDownload, MdFileUpload } from "react-icons/md";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
@@ -6,14 +9,6 @@ import {
|
||||
selectScheduleLoadCalculating,
|
||||
} from "../../redux/application/application.selectors";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import { Progress, Statistic } from "antd";
|
||||
import {
|
||||
MdCallReceived,
|
||||
MdCallMissedOutgoing,
|
||||
MdFileDownload,
|
||||
MdFileUpload,
|
||||
} from "react-icons/md";
|
||||
import Icon from "@ant-design/icons";
|
||||
import ScheduleBlockDay from "../schedule-block-day/schedule-block-day.component";
|
||||
|
||||
const ShopTargetHrs = 100;
|
||||
|
||||
@@ -4,6 +4,8 @@ export const QUERY_BODYSHOP = gql`
|
||||
query QUERY_BODYSHOP {
|
||||
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
||||
associations {
|
||||
authlevel
|
||||
useremail
|
||||
user {
|
||||
authid
|
||||
email
|
||||
|
||||
@@ -8,7 +8,7 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_INVOICES_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -35,10 +35,12 @@ export function AccountingPayablesContainer({ bodyshop, setBreadcrumbs }) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AccountingPayablesTable
|
||||
loadaing={loading}
|
||||
invoices={data ? data.invoices : []}
|
||||
/>
|
||||
<RbacWrapper action="accounting:payables">
|
||||
<AccountingPayablesTable
|
||||
loadaing={loading}
|
||||
invoices={data ? data.invoices : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_PAYMENTS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -35,10 +36,12 @@ export function AccountingPaymentsContainer({ bodyshop, setBreadcrumbs }) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AccountingPaymentsTable
|
||||
loadaing={loading}
|
||||
payments={data ? data.payments : []}
|
||||
/>
|
||||
<RbacWrapper action="accounting:payments">
|
||||
<AccountingPaymentsTable
|
||||
loadaing={loading}
|
||||
payments={data ? data.payments : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_JOBS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -38,10 +39,12 @@ export function AccountingReceivablesContainer({ bodyshop, setBreadcrumbs }) {
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<div>
|
||||
<AccountingReceivablesTable
|
||||
loadaing={loading}
|
||||
jobs={data ? data.jobs : []}
|
||||
/>
|
||||
<RbacWrapper action="accounting:receivables">
|
||||
<AccountingReceivablesTable
|
||||
loadaing={loading}
|
||||
jobs={data ? data.jobs : []}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import ContractCreatePageComponent from "./contract-create.page.component";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { Form, notification } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { INSERT_NEW_CONTRACT } from "../../graphql/cccontracts.queries";
|
||||
import { useMutation } from "@apollo/react-hooks";
|
||||
import { Form, notification } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { INSERT_NEW_CONTRACT } from "../../graphql/cccontracts.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ContractCreatePageComponent from "./contract-create.page.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -79,18 +79,20 @@ export function ContractCreatePageContainer({ bodyshop, setBreadcrumbs }) {
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
>
|
||||
<ContractCreatePageComponent
|
||||
<RbacWrapper action="contracts:create">
|
||||
<Form
|
||||
form={form}
|
||||
selectedJobState={selectedJobState}
|
||||
selectedCarState={selectedCarState}
|
||||
/>
|
||||
</Form>
|
||||
layout="vertical"
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
>
|
||||
<ContractCreatePageComponent
|
||||
form={form}
|
||||
selectedJobState={selectedJobState}
|
||||
selectedCarState={selectedCarState}
|
||||
/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
|
||||
@@ -7,17 +7,18 @@ import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import CourtesyCarReturnModalContainer from "../../components/courtesy-car-return-modal/courtesy-car-return-modal.container";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
QUERY_CONTRACT_BY_PK,
|
||||
UPDATE_CONTRACT,
|
||||
} from "../../graphql/cccontracts.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
} from "../../redux/application/application.actions";
|
||||
import ContractDetailPageComponent from "./contract-detail.page.component";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import ContractDetailPageComponent from "./contract-detail.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -94,41 +95,43 @@ export function ContractDetailPageContainer({ setBreadcrumbs, addRecentItem }) {
|
||||
if (loading) return <LoadingSpinner />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CourtesyCarReturnModalContainer />
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="no"
|
||||
layout="vertical"
|
||||
onFinish={handleFinish}
|
||||
initialValues={{
|
||||
...data.cccontracts_by_pk,
|
||||
start: data.cccontracts_by_pk.start
|
||||
? moment(data.cccontracts_by_pk.start)
|
||||
: null,
|
||||
scheduledreturn: data.cccontracts_by_pk.scheduledreturn
|
||||
? moment(data.cccontracts_by_pk.scheduledreturn)
|
||||
: null,
|
||||
actualreturn: data.cccontracts_by_pk.actualreturn
|
||||
? moment(data.cccontracts_by_pk.actualreturn)
|
||||
: null,
|
||||
driver_dlexpiry: data.cccontracts_by_pk.driver_dlexpiry
|
||||
? moment(data.cccontracts_by_pk.driver_dlexpiry)
|
||||
: null,
|
||||
driver_dob: data.cccontracts_by_pk.driver_dob
|
||||
? moment(data.cccontracts_by_pk.driver_dob)
|
||||
: null,
|
||||
}}
|
||||
>
|
||||
<ContractDetailPageComponent
|
||||
contract={data ? data.cccontracts_by_pk : null}
|
||||
job={data ? data.cccontracts_by_pk.job : null}
|
||||
courtesyCar={data ? data.cccontracts_by_pk.courtesycar : null}
|
||||
refetch={refetch}
|
||||
<RbacWrapper action="contracts:detail">
|
||||
<div>
|
||||
<CourtesyCarReturnModalContainer />
|
||||
<Form
|
||||
form={form}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
autoComplete="no"
|
||||
layout="vertical"
|
||||
onFinish={handleFinish}
|
||||
initialValues={{
|
||||
...data.cccontracts_by_pk,
|
||||
start: data.cccontracts_by_pk.start
|
||||
? moment(data.cccontracts_by_pk.start)
|
||||
: null,
|
||||
scheduledreturn: data.cccontracts_by_pk.scheduledreturn
|
||||
? moment(data.cccontracts_by_pk.scheduledreturn)
|
||||
: null,
|
||||
actualreturn: data.cccontracts_by_pk.actualreturn
|
||||
? moment(data.cccontracts_by_pk.actualreturn)
|
||||
: null,
|
||||
driver_dlexpiry: data.cccontracts_by_pk.driver_dlexpiry
|
||||
? moment(data.cccontracts_by_pk.driver_dlexpiry)
|
||||
: null,
|
||||
driver_dob: data.cccontracts_by_pk.driver_dob
|
||||
? moment(data.cccontracts_by_pk.driver_dob)
|
||||
: null,
|
||||
}}
|
||||
>
|
||||
<ContractDetailPageComponent
|
||||
contract={data ? data.cccontracts_by_pk : null}
|
||||
job={data ? data.cccontracts_by_pk.job : null}
|
||||
courtesyCar={data ? data.cccontracts_by_pk.courtesycar : null}
|
||||
refetch={refetch}
|
||||
form={form}
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(ContractDetailPageContainer);
|
||||
|
||||
@@ -9,6 +9,8 @@ import ContractsPageComponent from "./contracts.page.component";
|
||||
import queryString from "query-string";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import moment from "moment";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
});
|
||||
@@ -53,14 +55,16 @@ export function ContractsPageContainer({ setBreadcrumbs }) {
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<div>
|
||||
<ContractsPageComponent
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
data={data ? data.cccontracts : []}
|
||||
total={data ? data.cccontracts_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</div>
|
||||
<RbacWrapper action="contracts:list">
|
||||
<div>
|
||||
<ContractsPageComponent
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
data={data ? data.cccontracts : []}
|
||||
total={data ? data.cccontracts_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(ContractsPageContainer);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CourtesyCarCreateComponent from "./courtesy-car-create.page.component";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -47,9 +48,11 @@ export function CourtesyCarCreateContainer({ bodyshop, setBreadcrumbs }) {
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return (
|
||||
<Form form={form} autoComplete="new-password" onFinish={handleFinish}>
|
||||
<CourtesyCarCreateComponent />
|
||||
</Form>
|
||||
<RbacWrapper action="courtesycar:create">
|
||||
<Form form={form} autoComplete="new-password" onFinish={handleFinish}>
|
||||
<CourtesyCarCreateComponent />
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
|
||||
import {
|
||||
addRecentItem,
|
||||
@@ -82,44 +83,46 @@ export function CourtesyCarDetailPageContainer({
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
initialValues={
|
||||
data
|
||||
? {
|
||||
...data.courtesycars_by_pk,
|
||||
purchasedate: data.courtesycars_by_pk.purchasedate
|
||||
? moment(data.courtesycars_by_pk.purchasedate)
|
||||
: null,
|
||||
servicestartdate: data.courtesycars_by_pk.servicestartdate
|
||||
? moment(data.courtesycars_by_pk.servicestartdate)
|
||||
: null,
|
||||
serviceenddate: data.courtesycars_by_pk.serviceenddate
|
||||
? moment(data.courtesycars_by_pk.serviceenddate)
|
||||
: null,
|
||||
leaseenddate: data.courtesycars_by_pk.leaseenddate
|
||||
? moment(data.courtesycars_by_pk.leaseenddate)
|
||||
: null,
|
||||
nextservicedate: data.courtesycars_by_pk.nextservicedate
|
||||
? moment(data.courtesycars_by_pk.nextservicedate)
|
||||
: null,
|
||||
registrationexpires: data.courtesycars_by_pk.registrationexpires
|
||||
? moment(data.courtesycars_by_pk.registrationexpires)
|
||||
: null,
|
||||
insuranceexpires: data.courtesycars_by_pk.insuranceexpires
|
||||
? moment(data.courtesycars_by_pk.insuranceexpires)
|
||||
: null,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<CourtesyCarDetailPageComponent
|
||||
contracts={data ? data.courtesycars_by_pk.cccontracts : []}
|
||||
<RbacWrapper action="courtesycar:detail">
|
||||
<Form
|
||||
form={form}
|
||||
/>
|
||||
</Form>
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
initialValues={
|
||||
data
|
||||
? {
|
||||
...data.courtesycars_by_pk,
|
||||
purchasedate: data.courtesycars_by_pk.purchasedate
|
||||
? moment(data.courtesycars_by_pk.purchasedate)
|
||||
: null,
|
||||
servicestartdate: data.courtesycars_by_pk.servicestartdate
|
||||
? moment(data.courtesycars_by_pk.servicestartdate)
|
||||
: null,
|
||||
serviceenddate: data.courtesycars_by_pk.serviceenddate
|
||||
? moment(data.courtesycars_by_pk.serviceenddate)
|
||||
: null,
|
||||
leaseenddate: data.courtesycars_by_pk.leaseenddate
|
||||
? moment(data.courtesycars_by_pk.leaseenddate)
|
||||
: null,
|
||||
nextservicedate: data.courtesycars_by_pk.nextservicedate
|
||||
? moment(data.courtesycars_by_pk.nextservicedate)
|
||||
: null,
|
||||
registrationexpires: data.courtesycars_by_pk.registrationexpires
|
||||
? moment(data.courtesycars_by_pk.registrationexpires)
|
||||
: null,
|
||||
insuranceexpires: data.courtesycars_by_pk.insuranceexpires
|
||||
? moment(data.courtesycars_by_pk.insuranceexpires)
|
||||
: null,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<CourtesyCarDetailPageComponent
|
||||
contracts={data ? data.courtesycars_by_pk.cccontracts : []}
|
||||
form={form}
|
||||
/>
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { useEffect } from "react";
|
||||
import CourtesyCarsPageComponent from "./courtesy-cars.page.component";
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_ALL_CC } from "../../graphql/courtesy-car.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CourtesyCarsPageComponent from "./courtesy-cars.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -21,13 +22,15 @@ export function CourtesyCarsPageContainer({ setBreadcrumbs }) {
|
||||
{ link: "/manage/courtesycars", label: t("titles.bc.courtesycars") },
|
||||
]);
|
||||
}, [setBreadcrumbs, t]);
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<CourtesyCarsPageComponent
|
||||
loading={loading}
|
||||
data={(data && data.courtesycars) || []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<RbacWrapper action="courtesycar:list">
|
||||
<CourtesyCarsPageComponent
|
||||
loading={loading}
|
||||
data={(data && data.courtesycars) || []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { QUERY_ALL_INVOICES_PAGINATED } from "../../graphql/invoices.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import InvoicesPageComponent from "./invoices.page.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -48,16 +49,18 @@ export function InvoicesPageContainer({ setBreadcrumbs }) {
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<div>
|
||||
<InvoicesPageComponent
|
||||
data={data ? data.search_invoices : []}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
total={data ? data.search_invoices_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
<RbacWrapper action="invoices:list">
|
||||
<div>
|
||||
<InvoicesPageComponent
|
||||
data={data ? data.search_invoices : []}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
total={data ? data.search_invoices_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
|
||||
<InvoiceDetailEditContainer />
|
||||
</div>
|
||||
<InvoiceDetailEditContainer />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(InvoicesPageContainer);
|
||||
|
||||
@@ -9,17 +9,17 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobsListPaginated from "../../components/jobs-list-paginated/jobs-list-paginated.component";
|
||||
import { QUERY_ALL_JOBS_PAGINATED } from "../../graphql/jobs.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
//bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
});
|
||||
|
||||
export function AllJobs({ bodyshop, setBreadcrumbs }) {
|
||||
export function AllJobs({ setBreadcrumbs }) {
|
||||
const searchParams = queryString.parse(useLocation().search);
|
||||
const { page, sortcolumn, sortorder, search } = searchParams;
|
||||
|
||||
@@ -48,15 +48,17 @@ export function AllJobs({ bodyshop, setBreadcrumbs }) {
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<div>
|
||||
<JobsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.search_jobs_aggregate.aggregate.count : 0}
|
||||
jobs={data ? data.search_jobs : []}
|
||||
/>
|
||||
</div>
|
||||
<RbacWrapper action="jobs:list-all">
|
||||
<div>
|
||||
<JobsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.search_jobs_aggregate.aggregate.count : 0}
|
||||
jobs={data ? data.search_jobs : []}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import JobsAvailablePageComponent from "./jobs-available.page.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
@@ -27,12 +28,14 @@ export function JobsAvailablePageContainer({ setBreadcrumbs }) {
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<JobsAvailablePageComponent
|
||||
deleteJob={deleteJob}
|
||||
estDataLazyLoad={estDataLazyLoad}
|
||||
/>
|
||||
</div>
|
||||
<RbacWrapper action="jobs:available-list">
|
||||
<div>
|
||||
<JobsAvailablePageComponent
|
||||
deleteJob={deleteJob}
|
||||
estDataLazyLoad={estDataLazyLoad}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(JobsAvailablePageContainer);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { CalculateJob } from "../../components/job-totals-table/job-totals.utility";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_JOB_CLOSE_DETAILS } from "../../graphql/jobs.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
@@ -54,12 +55,14 @@ export function JobsCloseContainer({ setBreadcrumbs, bodyshop }) {
|
||||
|
||||
const jobTotals = CalculateJob(data.jobs_by_pk, bodyshop.shoprates);
|
||||
return (
|
||||
<div>
|
||||
<JobsCloseComponent
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
jobTotals={jobTotals}
|
||||
/>
|
||||
</div>
|
||||
<RbacWrapper action="jobs:close">
|
||||
<div>
|
||||
<JobsCloseComponent
|
||||
job={data ? data.jobs_by_pk : {}}
|
||||
jobTotals={jobTotals}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsCloseContainer);
|
||||
|
||||
@@ -10,6 +10,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -131,13 +132,16 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs }) {
|
||||
|
||||
return (
|
||||
<JobCreateContext.Provider value={contextState}>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleFinish}
|
||||
layout='vertical'
|
||||
autoComplete={"off"}>
|
||||
<JobsCreateComponent form={form} />
|
||||
</Form>
|
||||
<RbacWrapper action="jobs:create">
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
autoComplete={"off"}
|
||||
>
|
||||
<JobsCreateComponent form={form} />
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
</JobCreateContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from "../../redux/application/application.actions";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import JobsDetailPage from "./jobs-detail.page.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -90,13 +91,15 @@ function JobsDetailPageContainer({ match, setBreadcrumbs, addRecentItem }) {
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return data.jobs_by_pk ? (
|
||||
<JobsDetailPage
|
||||
job={data.jobs_by_pk}
|
||||
mutationConvertJob={mutationConvertJob}
|
||||
mutationUpdateJob={mutationUpdateJob}
|
||||
refetch={refetch}
|
||||
updateJobStatus={updateJobStatus}
|
||||
/>
|
||||
<RbacWrapper action="jobs:detail">
|
||||
<JobsDetailPage
|
||||
job={data.jobs_by_pk}
|
||||
mutationConvertJob={mutationConvertJob}
|
||||
mutationUpdateJob={mutationUpdateJob}
|
||||
refetch={refetch}
|
||||
updateJobStatus={updateJobStatus}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
) : (
|
||||
<AlertComponent message={t("jobs.errors.noaccess")} type="error" />
|
||||
);
|
||||
|
||||
@@ -10,6 +10,8 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com
|
||||
import { QUERY_INTAKE_CHECKLIST } from "../../graphql/bodyshop.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -42,19 +44,21 @@ export function JobsIntakeContainer({ bodyshop, setBreadcrumbs }) {
|
||||
}, [t, setBreadcrumbs, jobId]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (data && !!!data.bodyshops_by_pk.intakechecklist)
|
||||
return (
|
||||
<AlertComponent message={t("intake.errors.nochecklist")} type='error' />
|
||||
<AlertComponent message={t("intake.errors.nochecklist")} type="error" />
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<JobIntakeComponent
|
||||
intakeChecklistConfig={
|
||||
(data && data.bodyshops_by_pk.intakechecklist) || {}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<RbacWrapper action="jobs:intake">
|
||||
<div>
|
||||
<JobIntakeComponent
|
||||
intakeChecklistConfig={
|
||||
(data && data.bodyshops_by_pk.intakechecklist) || {}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { connect } from "react-redux";
|
||||
import JobDetailCards from "../../components/job-detail-cards/job-detail-cards.component";
|
||||
import JobsList from "../../components/jobs-list/jobs-list.component";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -20,10 +21,12 @@ export function JobsPage({ setBreadcrumbs }) {
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return (
|
||||
<div className='jobs-list-container'>
|
||||
<JobsList />
|
||||
<JobDetailCards />
|
||||
</div>
|
||||
<RbacWrapper action="jobs:list-active">
|
||||
<div className="jobs-list-container">
|
||||
<JobsList />
|
||||
<JobDetailCards />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_OWNER_BY_ID } from "../../graphql/owners.queries";
|
||||
import {
|
||||
addRecentItem,
|
||||
@@ -71,7 +72,9 @@ export function OwnersDetailContainer({
|
||||
|
||||
if (data.owners_by_pk)
|
||||
return (
|
||||
<OwnersDetailComponent owner={data.owners_by_pk} refetch={refetch} />
|
||||
<RbacWrapper action="owners:detail">
|
||||
<OwnersDetailComponent owner={data.owners_by_pk} refetch={refetch} />
|
||||
</RbacWrapper>
|
||||
);
|
||||
else
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,7 @@ import OwnersPageComponent from "./owners.page.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { connect } from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -15,6 +16,10 @@ export function OwnersPageContainer({ setBreadcrumbs }) {
|
||||
setBreadcrumbs([{ link: "/manage/owners", label: t("titles.bc.owners") }]);
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return <OwnersPageComponent />;
|
||||
return (
|
||||
<RbacWrapper action="owners:list">
|
||||
<OwnersPageComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(OwnersPageContainer);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import PaymentsListPaginated from "../../components/payments-list-paginated/payment-list-paginated.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_ALL_PAYMENTS_PAGINATED } from "../../graphql/payments.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
@@ -53,15 +54,17 @@ export function AllJobs({ bodyshop, setBreadcrumbs }) {
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<div>
|
||||
<PaymentsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.search_payments_aggregate.aggregate.count : 0}
|
||||
payments={data ? data.search_payments : []}
|
||||
/>
|
||||
</div>
|
||||
<RbacWrapper action="payments:list">
|
||||
<div>
|
||||
<PaymentsListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
searchParams={searchParams}
|
||||
total={data ? data.search_payments_aggregate.aggregate.count : 0}
|
||||
payments={data ? data.search_payments : []}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ProductionBoardComponent from "./production-board.component";
|
||||
@@ -27,7 +28,11 @@ export function ProductionBoardContainer({ setBreadcrumbs, bodyshop }) {
|
||||
]);
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return <ProductionBoardComponent />;
|
||||
return (
|
||||
<RbacWrapper action="production-board">
|
||||
<ProductionBoardComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ProductionListComponent from "./production-list.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -25,9 +26,9 @@ export function ProductionListContainer({ setBreadcrumbs, bodyshop }) {
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="production-list">
|
||||
<ProductionListComponent />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useEffect } from "react";
|
||||
import SchedulePageComponent from "./schedule.page.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import SchedulePageComponent from "./schedule.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
});
|
||||
@@ -17,6 +19,10 @@ export function SchedulePageContainer({ setBreadcrumbs }) {
|
||||
]);
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return <SchedulePageComponent />;
|
||||
return (
|
||||
<RbacWrapper action="schedule:view">
|
||||
<SchedulePageComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(SchedulePageContainer);
|
||||
|
||||
@@ -8,6 +8,7 @@ import ScoreboardPageComponent from "./scoreboard.page.component";
|
||||
import { useSubscription } from "@apollo/react-hooks";
|
||||
import { SUBSCRIPTION_SCOREBOARD } from "../../graphql/scoreboard.queries";
|
||||
import moment from "moment";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -38,7 +39,11 @@ export function ScoreboardContainer({ setBreadcrumbs }) {
|
||||
}, [t, setBreadcrumbs]);
|
||||
|
||||
return (
|
||||
<ScoreboardPageComponent scoreboardSubscription={scoreboardSubscription} />
|
||||
<RbacWrapper action="scoreboard:view">
|
||||
<ScoreboardPageComponent
|
||||
scoreboardSubscription={scoreboardSubscription}
|
||||
/>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React from "react";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import TimeTicketShift from "../../components/time-ticket-shift/time-ticket-shift.container";
|
||||
|
||||
export default function ShiftClock() {
|
||||
return (
|
||||
<div>
|
||||
<TimeTicketShift />
|
||||
</div>
|
||||
<RbacWrapper action="shiftclock:view">
|
||||
<div>
|
||||
<TimeTicketShift />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import CsiResponseListPaginated from "../../components/csi-response-list-paginat
|
||||
import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -61,19 +61,26 @@ export function ShopCsiContainer({ bodyshop, setBreadcrumbs }) {
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col span={10}>
|
||||
<CsiResponseListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
responses={data ? data.csi : []}
|
||||
total={data ? data.csi_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={13} offset={1}>
|
||||
<CsiResponseFormContainer />
|
||||
</Col>
|
||||
</Row>
|
||||
<RbacWrapper
|
||||
action="csi:page"
|
||||
// noauth={
|
||||
// <AlertComponent message="You don't have acess to see this screen." />
|
||||
// }
|
||||
>
|
||||
<Row>
|
||||
<Col span={10}>
|
||||
<CsiResponseListPaginated
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
responses={data ? data.csi : []}
|
||||
total={data ? data.csi_aggregate.aggregate.count : 0}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={13} offset={1}>
|
||||
<CsiResponseFormContainer />
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopCsiContainer);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Col, Row } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import ShopTemplateEditor from "../../components/shop-template-editor/shop-template-editor.container";
|
||||
import ShopTemplatesListContainer from "../../components/shop-templates-list/shop-templates-list.container";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ShopTemplatesListContainer from "../../components/shop-templates-list/shop-templates-list.container";
|
||||
import ShopTemplateEditor from "../../components/shop-template-editor/shop-template-editor.container";
|
||||
import { Row, Col } from "antd";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -28,16 +30,18 @@ export function ShopTemplatesContainer({ setBreadcrumbs, bodyshop }) {
|
||||
}, [t, setBreadcrumbs, bodyshop.shopname]);
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<ShopTemplatesListContainer />
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<div>
|
||||
<ShopTemplateEditor />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<RbacWrapper action="shop:templates">
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<ShopTemplatesListContainer />
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<div>
|
||||
<ShopTemplateEditor />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import ShopVendorPageComponent from "./shop-vendor.page.component";
|
||||
|
||||
export default function ShopVendorPageContainer() {
|
||||
@@ -8,5 +9,9 @@ export default function ShopVendorPageContainer() {
|
||||
document.title = t("titles.shop_vendors");
|
||||
}, [t]);
|
||||
|
||||
return <ShopVendorPageComponent />;
|
||||
return (
|
||||
<RbacWrapper action="shop:vendors">
|
||||
<ShopVendorPageComponent />
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import TimeTicketsDatesSelector from "../../components/ticket-tickets-dates-selector/time-tickets-dates-selector.component";
|
||||
import TimeTicketList from "../../components/time-ticket-list/time-ticket-list.component";
|
||||
import TimeTicketsSummary from "../../components/time-tickets-summary/time-tickets-summary.component";
|
||||
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
|
||||
@@ -46,23 +47,25 @@ export function TimeTicketsContainer({ bodyshop, setBreadcrumbs }) {
|
||||
},
|
||||
});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TimeTicketsDatesSelector />
|
||||
<RbacWrapper action="timetickets:list">
|
||||
<div>
|
||||
<TimeTicketsDatesSelector />
|
||||
|
||||
<TimeTicketList
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
/>
|
||||
<TimeTicketsSummary
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
</div>
|
||||
<TimeTicketList
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
/>
|
||||
<TimeTicketsSummary
|
||||
loading={loading}
|
||||
timetickets={data ? data.timetickets : []}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
/>
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,3 +98,8 @@ export const validatePasswordResetFailure = (error) => ({
|
||||
type: UserActionTypes.VALIDATE_PASSWORD_RESET_FAILURE,
|
||||
payload: error,
|
||||
});
|
||||
|
||||
export const setAuthlevel = (authlevel) => ({
|
||||
type: UserActionTypes.SET_AUTH_LEVEL,
|
||||
payload: authlevel,
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ const INITIAL_STATE = {
|
||||
error: null,
|
||||
success: false,
|
||||
},
|
||||
authLevel: 0,
|
||||
};
|
||||
|
||||
const userReducer = (state = INITIAL_STATE, action) => {
|
||||
@@ -84,6 +85,8 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
||||
...state,
|
||||
error: action.payload,
|
||||
};
|
||||
case UserActionTypes.SET_AUTH_LEVEL:
|
||||
return { ...state, authLevel: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
sendPasswordResetSuccess,
|
||||
validatePasswordResetSuccess,
|
||||
validatePasswordResetFailure,
|
||||
setAuthlevel,
|
||||
} from "./user.actions";
|
||||
import UserActionTypes from "./user.types";
|
||||
|
||||
@@ -199,6 +200,26 @@ export function* validatePasswordResetStart({ payload: { password, code } }) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* onSetShopDetails() {
|
||||
yield takeLatest(
|
||||
UserActionTypes.SET_SHOP_DETAILS,
|
||||
SetAuthLevelFromShopDetails
|
||||
);
|
||||
}
|
||||
export function* SetAuthLevelFromShopDetails({ payload }) {
|
||||
try {
|
||||
const userEmail = yield select((state) => state.user.currentUser.email);
|
||||
|
||||
const authRecord = payload.associations.filter(
|
||||
(a) => a.useremail === userEmail
|
||||
);
|
||||
|
||||
yield put(setAuthlevel(authRecord[0] ? authRecord[0].authlevel : 0));
|
||||
} catch (error) {
|
||||
yield put(signInFailure(error.message));
|
||||
}
|
||||
}
|
||||
|
||||
export function* userSagas() {
|
||||
yield all([
|
||||
call(onEmailSignInStart),
|
||||
@@ -210,5 +231,6 @@ export function* userSagas() {
|
||||
call(onSignInSuccess),
|
||||
call(onSendPasswordResetStart),
|
||||
call(onValidatePasswordResetStart),
|
||||
call(onSetShopDetails),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -26,3 +26,8 @@ export const selectPasswordReset = createSelector(
|
||||
[selectUser],
|
||||
(user) => user.passwordreset
|
||||
);
|
||||
|
||||
export const selectAuthLevel = createSelector(
|
||||
[selectUser],
|
||||
(user) => user.authLevel
|
||||
);
|
||||
|
||||
@@ -26,5 +26,6 @@ const UserActionTypes = {
|
||||
VALIDATE_PASSWORD_RESET_START: "VALIDATE_PASSWORD_RESET_START",
|
||||
VALIDATE_PASSWORD_RESET_SUCCESS: "VALIDATE_PASSWORD_RESET_SUCCESS",
|
||||
VALIDATE_PASSWORD_RESET_FAILURE: "VALIDATE_PASSWORD_RESET_FAILURE",
|
||||
SET_AUTH_LEVEL: "SET_AUTH_LEVEL",
|
||||
};
|
||||
export default UserActionTypes;
|
||||
|
||||
@@ -474,6 +474,7 @@
|
||||
},
|
||||
"messages": {
|
||||
"exception": "$t(titles.app) has encountered an error. Please try again. If the problem persists, please submit a support ticket or contact us.",
|
||||
"rbacunauth": "You are not authorized to view this content. Please reach out to your shop manager to change your access level.",
|
||||
"unsavedchanges": "You have unsaved changes.",
|
||||
"unsavedchangespopup": "You have unsaved changes. Are you sure you want to leave?"
|
||||
},
|
||||
@@ -1272,10 +1273,12 @@
|
||||
},
|
||||
"titles": {
|
||||
"accounting-payables": "Payables | $t(titles.app)",
|
||||
"accounting-payments": "Payments | $t(titles.app)",
|
||||
"accounting-receivables": "Receivables | $t(titles.app)",
|
||||
"app": "ImEX Online",
|
||||
"bc": {
|
||||
"accounting-payables": "Payables",
|
||||
"accounting-payments": "Payments",
|
||||
"accounting-receivables": "Receivables",
|
||||
"availablejobs": "Available Jobs",
|
||||
"contracts": "Contracts",
|
||||
|
||||
@@ -474,6 +474,7 @@
|
||||
},
|
||||
"messages": {
|
||||
"exception": "",
|
||||
"rbacunauth": "",
|
||||
"unsavedchanges": "Usted tiene cambios no guardados.",
|
||||
"unsavedchangespopup": ""
|
||||
},
|
||||
@@ -1272,10 +1273,12 @@
|
||||
},
|
||||
"titles": {
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"app": "ImEX Online",
|
||||
"bc": {
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"availablejobs": "",
|
||||
"contracts": "",
|
||||
|
||||
@@ -474,6 +474,7 @@
|
||||
},
|
||||
"messages": {
|
||||
"exception": "",
|
||||
"rbacunauth": "",
|
||||
"unsavedchanges": "Vous avez des changements non enregistrés.",
|
||||
"unsavedchangespopup": ""
|
||||
},
|
||||
@@ -1272,10 +1273,12 @@
|
||||
},
|
||||
"titles": {
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"app": "ImEX Online",
|
||||
"bc": {
|
||||
"accounting-payables": "",
|
||||
"accounting-payments": "",
|
||||
"accounting-receivables": "",
|
||||
"availablejobs": "",
|
||||
"contracts": "",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."associations" DROP COLUMN "authlevel";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,6 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."associations" ADD COLUMN "authlevel" integer NOT NULL
|
||||
DEFAULT 0;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,24 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- id
|
||||
- shopid
|
||||
- useremail
|
||||
- active
|
||||
computed_fields: []
|
||||
filter:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,25 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
- id
|
||||
- shopid
|
||||
- useremail
|
||||
computed_fields: []
|
||||
filter:
|
||||
user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
role: user
|
||||
table:
|
||||
name: associations
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -195,10 +195,11 @@ tables:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- active
|
||||
- authlevel
|
||||
- id
|
||||
- shopid
|
||||
- useremail
|
||||
- active
|
||||
filter:
|
||||
user:
|
||||
authid:
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"moment": "^2.27.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"node-mailjet": "^3.3.1",
|
||||
"nodemailer": "^6.4.11",
|
||||
"phone": "^2.4.14",
|
||||
"stripe": "^8.82.0",
|
||||
"twilio": "^3.48.1",
|
||||
|
||||
35
sendemail.js
35
sendemail.js
@@ -1,4 +1,3 @@
|
||||
var nodemailer = require("nodemailer");
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
@@ -77,21 +76,21 @@ exports.sendEmail = (req, res) => {
|
||||
// );
|
||||
};
|
||||
|
||||
var transporter = nodemailer.createTransport({
|
||||
host: process.env.email_server,
|
||||
port: 465,
|
||||
secure: true, // upgrade later with STARTTLS
|
||||
auth: {
|
||||
user: process.env.email_api,
|
||||
pass: process.env.email_secret,
|
||||
},
|
||||
});
|
||||
// var transporter = nodemailer.createTransport({
|
||||
// host: process.env.email_server,
|
||||
// port: 465,
|
||||
// secure: true, // upgrade later with STARTTLS
|
||||
// auth: {
|
||||
// user: process.env.email_api,
|
||||
// pass: process.env.email_secret,
|
||||
// },
|
||||
// });
|
||||
|
||||
// verify connection configuration
|
||||
transporter.verify(function (error, success) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
} else {
|
||||
console.log("[EMAIL] Succesfully connected to SMTP server.");
|
||||
}
|
||||
});
|
||||
// // verify connection configuration
|
||||
// transporter.verify(function (error, success) {
|
||||
// if (error) {
|
||||
// console.log(error);
|
||||
// } else {
|
||||
// console.log("[EMAIL] Succesfully connected to SMTP server.");
|
||||
// }
|
||||
// });
|
||||
|
||||
@@ -2052,11 +2052,6 @@ node-mailjet@^3.3.1:
|
||||
superagent "^3.5.2"
|
||||
superagent-proxy "^1.0.2"
|
||||
|
||||
nodemailer@^6.4.11:
|
||||
version "6.4.11"
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.11.tgz#1f00b4ffd106403f17c03f3d43d5945b2677046c"
|
||||
integrity sha512-BVZBDi+aJV4O38rxsUh164Dk1NCqgh6Cm0rQSb9SK/DHGll/DrCMnycVDD7msJgZCnmVa8ASo8EZzR7jsgTukQ==
|
||||
|
||||
normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||
|
||||
Reference in New Issue
Block a user