IO-909 RBAC Shift Editing

This commit is contained in:
Patrick Fic
2021-04-22 09:52:49 -07:00
parent e6c7599813
commit cdf99a16fe
11 changed files with 207 additions and 55 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1"> <babeledit_project be_version="2.7.1" version="1.2">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -3817,6 +3817,27 @@
<folder_node> <folder_node>
<name>accounting</name> <name>accounting</name>
<children> <children>
<concept_node>
<name>exportlog</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> <concept_node>
<name>payables</name> <name>payables</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -4795,6 +4816,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>shiftedit</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>
</children> </children>
</folder_node> </folder_node>
<folder_node> <folder_node>

View File

@@ -27,6 +27,7 @@ const FormDatePicker = ({ value, onChange, onBlur, ...restProps }, ref) => {
onChange={handleChange} onChange={handleChange}
format={dateFormat} format={dateFormat}
onBlur={onBlur} onBlur={onBlur}
disabledTime
{...restProps} {...restProps}
/> />
</div> </div>

View File

@@ -57,6 +57,7 @@ const ret = {
"timetickets:enter": 3, "timetickets:enter": 3,
"timetickets:list": 3, "timetickets:list": 3,
"timetickets:edit": 4, "timetickets:edit": 4,
"timetickets:shiftedit": 5,
"users:editaccess": 4, "users:editaccess": 4,
}; };

View File

@@ -5,19 +5,16 @@ import { createStructuredSelector } from "reselect";
import { import {
selectAuthLevel, selectAuthLevel,
selectBodyshop, selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import rbacDefaults from "./rbac-defaults"; import rbacDefaults from "./rbac-defaults";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
authLevel: selectAuthLevel, authLevel: selectAuthLevel,
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
function RbacWrapper({ function RbacWrapper({
currentUser,
authLevel, authLevel,
bodyshop, bodyshop,
requiredAuthLevel, requiredAuthLevel,
@@ -33,7 +30,9 @@ function RbacWrapper({
(requiredAuthLevel && requiredAuthLevel <= authLevel) || (requiredAuthLevel && requiredAuthLevel <= authLevel) ||
((bodyshop.md_rbac && bodyshop.md_rbac[action]) || rbacDefaults[action]) <= ((bodyshop.md_rbac && bodyshop.md_rbac[action]) || rbacDefaults[action]) <=
authLevel || authLevel ||
(!bodyshop.md_rbac && rbacDefaults[action] <= authLevel) (bodyshop.md_rbac &&
!bodyshop.md_rbac[action] &&
rbacDefaults[action] <= authLevel)
) )
return children; return children;
//return <div>{React.cloneElement(children, restProps)}</div>; //return <div>{React.cloneElement(children, restProps)}</div>;
@@ -48,12 +47,28 @@ function RbacWrapper({
); );
} }
// RbacWrapper.propTypes = { export function HasRbacAccess({
// currentUser: PropTypes.object.isRequired, authLevel,
// authLevel: PropTypes.number.isRequired, bodyshop,
// noauth: PropTypes.oneOfType(PropTypes.string, PropTypes.func), requiredAuthLevel,
// requiredAuthLevel: PropTypes.number, action,
// action: PropTypes.string, }) {
// }; console.log(
"Checking access for action",
action,
bodyshop.md_rbac[action],
authLevel,
bodyshop.md_rbac[action] <= authLevel
);
return (
(requiredAuthLevel && requiredAuthLevel <= authLevel) ||
((bodyshop.md_rbac && bodyshop.md_rbac[action]) || rbacDefaults[action]) <=
authLevel ||
(bodyshop.md_rbac &&
!bodyshop.md_rbac[action] &&
rbacDefaults[action] <= authLevel)
);
}
export default connect(mapStateToProps, null)(RbacWrapper); export default connect(mapStateToProps, null)(RbacWrapper);

View File

@@ -226,6 +226,12 @@ export function ShopEmployeesFormComponent({
]} ]}
> >
<Select> <Select>
<Select.Option
key={"shift"}
value="timetickets.labels.shift"
>
{t("timetickets.labels.shift")}
</Select.Option>
{bodyshop.md_responsibility_centers.costs.map((c) => ( {bodyshop.md_responsibility_centers.costs.map((c) => (
<Select.Option key={c.name} value={c.name}> <Select.Option key={c.name} value={c.name}>
{c.name} {c.name}

View File

@@ -465,6 +465,18 @@ export default function ShopInfoRbacComponent({ form }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </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={[

View File

@@ -2,13 +2,32 @@ import { Card, Space, Table } from "antd";
import moment from "moment"; import moment from "moment";
import React, { useMemo, useState } from "react"; import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectAuthLevel,
selectBodyshop,
} from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper"; import { onlyUnique } from "../../utils/arrayHelper";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter"; import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; import RbacWrapper, {
HasRbacAccess,
} from "../rbac-wrapper/rbac-wrapper.component";
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component"; import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
export default function TimeTicketList({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
authLevel: selectAuthLevel,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketList);
export function TimeTicketList({
bodyshop,
authLevel,
disabled, disabled,
loading, loading,
timetickets, timetickets,
@@ -154,7 +173,7 @@ export default function TimeTicketList({
<TimeTicketEnterButton <TimeTicketEnterButton
actions={{ refetch }} actions={{ refetch }}
context={{ id: record.id, timeticket: record }} context={{ id: record.id, timeticket: record }}
disabled={!!!record.job || disabled} disabled={!record.job || disabled}
> >
{t("general.actions.edit")} {t("general.actions.edit")}
</TimeTicketEnterButton> </TimeTicketEnterButton>
@@ -172,7 +191,15 @@ export default function TimeTicketList({
id: record.id, id: record.id,
timeticket: record, timeticket: record,
}} }}
disabled={!!!record.jobid || disabled} disabled={
HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:shiftedit",
})
? disabled
: !record.jobid
}
> >
{t("general.actions.edit")} {t("general.actions.edit")}
</TimeTicketEnterButton> </TimeTicketEnterButton>

View File

@@ -2,18 +2,37 @@ import { useQuery } from "@apollo/client";
import { Form, Input, InputNumber, Select } from "antd"; import { Form, Input, InputNumber, Select } 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 { createStructuredSelector } from "reselect";
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries"; import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
import {
selectAuthLevel,
selectBodyshop,
} from "../../redux/user/user.selectors";
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component"; import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import JobSearchSelect from "../job-search-select/job-search-select.component"; import JobSearchSelect from "../job-search-select/job-search-select.component";
import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component"; import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
import TimeTicketList from "../time-ticket-list/time-ticket-list.component"; import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
export default function TimeTicketModalComponent({ const mapStateToProps = createStructuredSelector({
form, bodyshop: selectBodyshop,
authLevel: selectAuthLevel,
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TimeTicketModalComponent);
export function TimeTicketModalComponent({
form,
bodyshop,
authLevel,
employeeAutoCompleteOptions, employeeAutoCompleteOptions,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -21,17 +40,23 @@ export default function TimeTicketModalComponent({
return ( return (
<div> <div>
<LayoutFormRow grow> <LayoutFormRow grow>
<Form.Item <Form.Item shouldUpdate>
name="jobid" {() => (
label={t("timetickets.fields.ro_number")} <Form.Item
rules={[ name="jobid"
{ label={t("timetickets.fields.ro_number")}
required: true, rules={[
message: t("general.validation.required"), {
}, required:
]} !form.getFieldValue("cost_center") ===
> "timetickets.labels.shift",
<JobSearchSelect convertedOnly notExported={false} /> message: t("general.validation.required"),
},
]}
>
<JobSearchSelect convertedOnly notExported={false} />
</Form.Item>
)}
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("timetickets.fields.date")} label={t("timetickets.fields.date")}
@@ -104,34 +129,51 @@ export default function TimeTicketModalComponent({
> >
<InputNumber min={0} precision={1} /> <InputNumber min={0} precision={1} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item label={t("timetickets.fields.actualhrs")} name="actualhrs">
label={t("timetickets.fields.actualhrs")}
name="actualhrs"
// rules={[
// {
// required: true,
// message: t("general.validation.required"),
// },
// ]}
>
<InputNumber min={0} precision={1} /> <InputNumber min={0} precision={1} />
</Form.Item> </Form.Item>
<Form.Item label={t("timetickets.fields.clockon")} name="clockon">
<FormDateTimePicker
disabled={
!HasRbacAccess({
bodyshop,
authLevel,
action: "timetickets:shiftedit",
})
}
/>
</Form.Item>
<Form.Item label={t("timetickets.fields.clockoff")} name="clockoff">
<FormDateTimePicker
disabled={
!HasRbacAccess({
bodyshop,
authLevel,
action: "timetickets:shiftedit",
})
}
/>
</Form.Item>
<Form.Item label={t("timetickets.fields.memo")} name="memo"> <Form.Item label={t("timetickets.fields.memo")} name="memo">
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item shouldUpdate>
<Form.Item {() => (
name="ciecacode" <Form.Item
label={t("timetickets.fields.ciecacode")} name="ciecacode"
rules={[ label={t("timetickets.fields.ciecacode")}
{ rules={[
required: true, {
message: t("general.validation.required"), required:
}, !form.getFieldValue("cost_center") ===
]} "timetickets.labels.shift",
> message: t("general.validation.required"),
<Input disabled /> },
]}
>
<Input disabled />
</Form.Item>
)}
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<Form.Item dependencies={["jobid"]}> <Form.Item dependencies={["jobid"]}>

View File

@@ -246,6 +246,7 @@
"prodtargethrs": "Production Target Hours", "prodtargethrs": "Production Target Hours",
"rbac": { "rbac": {
"accounting": { "accounting": {
"exportlog": "Accounting -> Export Log",
"payables": "Accounting -> Payables", "payables": "Accounting -> Payables",
"payments": "Accounting -> Payments", "payments": "Accounting -> Payments",
"receivables": "Accounting -> Receivables" "receivables": "Accounting -> Receivables"
@@ -318,7 +319,8 @@
"timetickets": { "timetickets": {
"edit": "Time Tickets -> Edit", "edit": "Time Tickets -> Edit",
"enter": "Time Tickets -> Enter", "enter": "Time Tickets -> Enter",
"list": "Time Tickets -> List" "list": "Time Tickets -> List",
"shiftedit": "Time Tickets -> Shift Edit"
}, },
"users": { "users": {
"editaccess": "Users -> Edit access" "editaccess": "Users -> Edit access"

View File

@@ -246,6 +246,7 @@
"prodtargethrs": "", "prodtargethrs": "",
"rbac": { "rbac": {
"accounting": { "accounting": {
"exportlog": "",
"payables": "", "payables": "",
"payments": "", "payments": "",
"receivables": "" "receivables": ""
@@ -318,7 +319,8 @@
"timetickets": { "timetickets": {
"edit": "", "edit": "",
"enter": "", "enter": "",
"list": "" "list": "",
"shiftedit": ""
}, },
"users": { "users": {
"editaccess": "" "editaccess": ""

View File

@@ -246,6 +246,7 @@
"prodtargethrs": "", "prodtargethrs": "",
"rbac": { "rbac": {
"accounting": { "accounting": {
"exportlog": "",
"payables": "", "payables": "",
"payments": "", "payments": "",
"receivables": "" "receivables": ""
@@ -318,7 +319,8 @@
"timetickets": { "timetickets": {
"edit": "", "edit": "",
"enter": "", "enter": "",
"list": "" "list": "",
"shiftedit": ""
}, },
"users": { "users": {
"editaccess": "" "editaccess": ""