studiokit-scaffolding-js 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,16 @@
1
1
  import { Component } from 'react';
2
- import { RoleDescription } from '../../types';
2
+ import { RoleDescriptions } from '../../types';
3
3
  export interface UserRolesAddProps {
4
4
  id: string;
5
- /** optional, if not targeting entityUserRoles */
5
+ disabled?: boolean;
6
6
  entityName?: string;
7
- addUsersToRole: (identifiers: string[], role: string) => void;
8
- shouldReset: boolean;
9
- isAddingUsersToRole: boolean;
10
7
  defaultRole: string;
11
- roleDescriptions: RoleDescription;
8
+ roleDescriptions: RoleDescriptions;
12
9
  renderAddDescription?: (entityName?: string) => JSX.Element;
13
- disabled?: boolean;
14
10
  textForRole?: (role: string) => string;
11
+ isAddingUsersToRole: boolean;
12
+ shouldReset: boolean;
13
+ addUserRoles: (identifiers: string[], role: string) => void;
15
14
  }
16
15
  interface UserRolesAddState {
17
16
  identifiers: string;
@@ -21,7 +20,7 @@ interface UserRolesAddState {
21
20
  }
22
21
  export default class UserRolesAdd extends Component<UserRolesAddProps, UserRolesAddState> {
23
22
  constructor(props: UserRolesAddProps);
24
- updateRole: (e: any) => void;
23
+ updateRole: (role: string) => void;
25
24
  componentDidUpdate(prevProps: UserRolesAddProps): void;
26
25
  handleSubmit: () => void;
27
26
  closeErrorAlert: () => void;
@@ -42,7 +42,7 @@ var react_1 = __importStar(require("react"));
42
42
  var react_bootstrap_1 = require("react-bootstrap");
43
43
  var AlertDialog_1 = __importDefault(require("../../components/AlertDialog"));
44
44
  var Inline_1 = __importDefault(require("../../components/RefreshIndicator/Inline"));
45
- var Select_1 = __importDefault(require("../../components/UserRoles/Select"));
45
+ var Select_1 = require("../../components/UserRoles/Select");
46
46
  var baseRole_1 = __importDefault(require("../../constants/baseRole"));
47
47
  var configuration_1 = require("../../constants/configuration");
48
48
  var domainIdentifier_1 = require("../../utils/domainIdentifier");
@@ -54,9 +54,9 @@ var UserRolesAdd = /** @class */ (function (_super) {
54
54
  __extends(UserRolesAdd, _super);
55
55
  function UserRolesAdd(props) {
56
56
  var _this = _super.call(this, props) || this;
57
- _this.updateRole = function (e) {
57
+ _this.updateRole = function (role) {
58
58
  _this.setState({
59
- role: e.target.value
59
+ role: role
60
60
  });
61
61
  };
62
62
  _this.handleSubmit = function () {
@@ -73,7 +73,7 @@ var UserRolesAdd = /** @class */ (function (_super) {
73
73
  });
74
74
  }
75
75
  else {
76
- _this.props.addUsersToRole(identifiersArray, role);
76
+ _this.props.addUserRoles(identifiersArray, role);
77
77
  }
78
78
  };
79
79
  _this.closeErrorAlert = function () {
@@ -140,7 +140,7 @@ var UserRolesAdd = /** @class */ (function (_super) {
140
140
  " (separated by a new line)"),
141
141
  react_1.default.createElement(react_bootstrap_1.FormControl, { type: "text", as: "textarea", name: "account", "aria-label": "The account names to add", onChange: this.updateIdentifiers, value: identifiers, placeholder: domainIdentifier_1.getDomainIdentifierTypeString(), disabled: disabled }))),
142
142
  react_1.default.createElement(react_bootstrap_1.Col, { xs: 12, sm: 4 },
143
- react_1.default.createElement(Select_1.default, { options: roles, value: role, onChange: this.updateRole, labelVisible: true, popoverContentComponent: rolePopover, textForRole: textForRole })),
143
+ react_1.default.createElement(Select_1.RoleSelect, { options: roles, value: role, onChange: this.updateRole, labelVisible: true, popoverContentComponent: rolePopover, textForRole: textForRole })),
144
144
  react_1.default.createElement(react_bootstrap_1.Col, { xs: 12, sm: 2, className: "mt3-ns pt2-ns" },
145
145
  isAddingUsersToRole && react_1.default.createElement(Inline_1.default, { className: "mr3" }),
146
146
  !isAddingUsersToRole && (react_1.default.createElement(Button_1.default, { id: "userRolesAddButton", className: "btn btn-primary w-100-lt-xs", disabled: !identifiers || disabled, color: "primary", onClick: this.handleSubmit },
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { UserRole } from '../../types';
3
+ export interface UserRolesContextType {
4
+ /** disable all user role select dropdowns, if any */
5
+ isUpdateDisabled?: boolean;
6
+ /** disable all user role delete buttons */
7
+ isDeleteDisabled?: boolean;
8
+ /** Is the current user allowed to add, update, and delete user roles? */
9
+ canModify?: boolean;
10
+ /** Is the current user allowed to delete their own user role? */
11
+ canDeleteSelf?: boolean;
12
+ /** If multiple roles allowed, each role is displayed per user, otherwise a select dropdown is displayed. */
13
+ allowMultipleRoles?: boolean;
14
+ roles: string[];
15
+ /** (Optional) If provided, the last user role with the required role will be prevented from being removed. */
16
+ requiredRole?: string;
17
+ /** (Optional) Provide custom user-friendly names for displayed roles */
18
+ textForRole?: (role: string) => string;
19
+ /** The user role being updated */
20
+ userRoleToUpdate?: UserRole;
21
+ /** If a user role is being updated */
22
+ isUpdating: boolean;
23
+ /** Method to update a user role */
24
+ updateUserRole: (userRoleToUpdate: UserRole, newRole: string) => void;
25
+ /** The user role being removed */
26
+ userRoleToRemove?: UserRole;
27
+ /** If a user role is being removed */
28
+ isRemoving: boolean;
29
+ /** Method to remove a user role */
30
+ removeUserRole: (userRoleToRemove: UserRole) => void;
31
+ }
32
+ export declare const UserRolesContext: React.Context<UserRolesContextType>;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.UserRolesContext = void 0;
7
+ var react_1 = __importDefault(require("react"));
8
+ /*
9
+ * Context provided to the children of UserRoles. Default values should be initialized
10
+ * by the context provider.
11
+ */
12
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
13
+ exports.UserRolesContext = react_1.default.createContext(undefined);
@@ -1,21 +1,7 @@
1
- import React, { FunctionComponent } from 'react';
2
- import { BaseReduxState } from '../../types/BaseReduxState';
3
- import { UserRole, UserWithRoles } from '../../types/UserRole';
4
- export interface RoleCellOwnProps {
1
+ import { FunctionComponent } from 'react';
2
+ import { UserWithRoles } from '../../types/UserRole';
3
+ export interface RoleCellProps {
5
4
  user: UserWithRoles;
6
- readOnly?: boolean;
7
- canModifySelf?: boolean;
8
- textForRole?: (role: string) => string;
9
- removeUserRole?: (user: UserRole) => void;
10
- roles: string[];
11
- entityOwnerRole: string;
12
- }
13
- export interface RoleCellReduxProps {
14
- currentUserId: string;
15
- }
16
- export interface RoleCellProps extends RoleCellOwnProps, RoleCellReduxProps {
5
+ requiredRoleCount?: number;
17
6
  }
18
7
  export declare const RoleCell: FunctionComponent<RoleCellProps>;
19
- export declare const mapStateToProps: (state: BaseReduxState) => RoleCellReduxProps;
20
- declare const _default: import("react-redux").ConnectedComponent<React.FunctionComponent<RoleCellProps>, Pick<RoleCellProps, "user" | "readOnly" | "textForRole" | "canModifySelf" | "removeUserRole" | "roles" | "entityOwnerRole">>;
21
- export default _default;
@@ -1,35 +1,97 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
5
24
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.mapStateToProps = exports.RoleCell = void 0;
25
+ exports.RoleCell = void 0;
7
26
  var core_1 = require("@material-ui/core");
8
27
  var Delete_1 = __importDefault(require("@material-ui/icons/Delete"));
9
- var react_1 = __importDefault(require("react"));
28
+ var react_1 = __importStar(require("react"));
10
29
  var react_bootstrap_1 = require("react-bootstrap");
11
30
  var react_redux_1 = require("react-redux");
12
31
  var userRole_1 = require("../../utils/userRole");
13
32
  var IconExternalUser_1 = require("../Icons/IconExternalUser");
33
+ var Inline_1 = __importDefault(require("../RefreshIndicator/Inline"));
34
+ var Context_1 = require("./Context");
35
+ var Select_1 = require("./Select");
14
36
  var RoleCell = function (_a) {
15
- var user = _a.user, currentUserId = _a.currentUserId, readOnly = _a.readOnly, canModifySelf = _a.canModifySelf, textForRole = _a.textForRole, removeUserRole = _a.removeUserRole, roles = _a.roles, entityOwnerRole = _a.entityOwnerRole;
37
+ var user = _a.user, requiredRoleCount = _a.requiredRoleCount;
38
+ var _b = react_1.useContext(Context_1.UserRolesContext), isUpdateDisabled = _b.isUpdateDisabled, isDeleteDisabled = _b.isDeleteDisabled, canModify = _b.canModify, canDeleteSelf = _b.canDeleteSelf, allowMultipleRoles = _b.allowMultipleRoles, roles = _b.roles, requiredRole = _b.requiredRole, textForRole = _b.textForRole, userRoleToUpdate = _b.userRoleToUpdate, isUpdating = _b.isUpdating, updateUserRole = _b.updateUserRole, userRoleToRemove = _b.userRoleToRemove, isRemoving = _b.isRemoving, removeUserRole = _b.removeUserRole;
39
+ var currentUserId = react_redux_1.useSelector(function (state) {
40
+ if (!state.models.user || !state.models.user.userInfo) {
41
+ throw new Error('Current user id is not stored in redux');
42
+ }
43
+ return state.models.user.userInfo.id;
44
+ });
16
45
  return (react_1.default.createElement("ul", { className: "mb0 list pa0 tr" }, user.roles.sort(userRole_1.sortByRole(roles)).map(function (userRole) {
17
46
  var roleText = userRole_1.getRoleText(textForRole)(userRole);
18
47
  var isUserRoleExternal = userRole_1.isExternal(userRole);
19
48
  var userId = userRole_1.getUserId(userRole);
20
- var popover = (react_1.default.createElement(react_bootstrap_1.Popover, { id: "is-external-popover-" + userId + "-" + userRole.role },
49
+ var canUpdate =
50
+ // current user has modify access
51
+ canModify &&
52
+ // in single role mode (multiple roles are not allowed)
53
+ !allowMultipleRoles &&
54
+ // the userRole is not external (roster synced)
55
+ !isUserRoleExternal &&
56
+ // update is not disabled
57
+ !isUpdateDisabled &&
58
+ // there is more than one possible role
59
+ roles.length > 1 &&
60
+ // the user role is not the last remaining required role user
61
+ (!requiredRole || userRole.role !== requiredRole || !requiredRoleCount || requiredRoleCount > 1);
62
+ var canDelete =
63
+ // the userRole is not external (roster synced)
64
+ !isUserRoleExternal &&
65
+ // delete is not disabled, e.g. course is ended
66
+ !isDeleteDisabled &&
67
+ // the role is not the last required role
68
+ (!requiredRole || userRole.role !== requiredRole || !requiredRoleCount || requiredRoleCount > 1) &&
69
+ // the current user can delete the role for another user, or the current user can delete themselves
70
+ ((canModify && user.id !== currentUserId) || (canDeleteSelf && user.id === currentUserId));
71
+ var shouldDisplayRightAligned =
72
+ // in multiple role mode
73
+ allowMultipleRoles ||
74
+ // only one role option
75
+ roles.length === 1 ||
76
+ // no access to update
77
+ !canModify ||
78
+ // update is disabled
79
+ isUpdateDisabled;
80
+ var isExternalPopover = (react_1.default.createElement(react_bootstrap_1.Popover, { id: "is-external-popover-" + userId + "-" + userRole.role },
21
81
  react_1.default.createElement("h3", null, "Added via Roster Sync"),
22
82
  react_1.default.createElement("p", { className: "mb2" }, "This person was added automatically via roster sync and cannot be manually removed.")));
23
- return (react_1.default.createElement("li", { key: user.id + "-" + roleText, className: "nowrap" },
24
- react_1.default.createElement("span", null, roleText),
25
- !isUserRoleExternal &&
26
- !readOnly &&
27
- (user.id !== currentUserId || canModifySelf || userRole.role !== entityOwnerRole) ? (react_1.default.createElement(core_1.IconButton, { className: "remove-user-button", "aria-label": "Remove " + roleText, onClick: function () {
28
- if (removeUserRole) {
29
- removeUserRole(userRole);
30
- }
31
- } },
32
- react_1.default.createElement(Delete_1.default, { color: "error" }))) : isUserRoleExternal ? (react_1.default.createElement(react_bootstrap_1.OverlayTrigger, { placement: "auto", trigger: ['click', 'hover', 'focus'], overlay: popover },
83
+ var isUserRoleUpdating = isUpdating && (userRoleToUpdate === null || userRoleToUpdate === void 0 ? void 0 : userRoleToUpdate.id) === userRole.id;
84
+ var isUserRoleRemoving = isRemoving && (userRoleToRemove === null || userRoleToRemove === void 0 ? void 0 : userRoleToRemove.id) === userRole.id;
85
+ return (react_1.default.createElement("li", { key: user.id + "-" + roleText, className: "nowrap flex items-center" + (shouldDisplayRightAligned ? ' justify-end' : '') },
86
+ isUserRoleUpdating ? (react_1.default.createElement("div", { className: "flex justify-start flex-grow-1 pl3" },
87
+ react_1.default.createElement(Inline_1.default, { size: 20 }))) : canUpdate ? (react_1.default.createElement(Select_1.RoleSelectControl, { options: roles, value: userRole.role, onChange: function (role) {
88
+ updateUserRole(userRole, role);
89
+ }, textForRole: textForRole, disabled: isUpdating || isRemoving })) : (react_1.default.createElement("span", { className: shouldDisplayRightAligned ? '' : 'pl3' }, roleText)),
90
+ isUserRoleRemoving ? (react_1.default.createElement("div", { className: "flex items-center" },
91
+ react_1.default.createElement(Inline_1.default, { size: 20, className: "ma3" }))) : canDelete ? (react_1.default.createElement(core_1.IconButton, { className: "remove-user-button", "aria-label": "Remove " + roleText, onClick: function () {
92
+ removeUserRole(userRole);
93
+ }, disabled: isUpdating || isRemoving },
94
+ react_1.default.createElement(Delete_1.default, { color: "error" }))) : isUserRoleExternal ? (react_1.default.createElement(react_bootstrap_1.OverlayTrigger, { placement: "auto", trigger: ['click', 'hover', 'focus'], overlay: isExternalPopover },
33
95
  react_1.default.createElement(IconExternalUser_1.IconExternalUser, { tabIndex: 0, style: { margin: '12px', opacity: 0.5 }, className: "external-icon" }))) : (
34
96
  // dummy spacer to keep items aligned
35
97
  react_1.default.createElement("span", { style: {
@@ -42,13 +104,3 @@ var RoleCell = function (_a) {
42
104
  })));
43
105
  };
44
106
  exports.RoleCell = RoleCell;
45
- var mapStateToProps = function (state) {
46
- if (!state.models.user || !state.models.user.userInfo) {
47
- throw new Error('Current user id is not stored in redux');
48
- }
49
- return {
50
- currentUserId: state.models.user.userInfo.id
51
- };
52
- };
53
- exports.mapStateToProps = mapStateToProps;
54
- exports.default = react_redux_1.connect(exports.mapStateToProps)(exports.RoleCell);
@@ -1,27 +1,26 @@
1
- import { ReactElement } from 'react';
1
+ import { FunctionComponent, ReactElement } from 'react';
2
2
  import { OverlayInjectedProps } from 'react-bootstrap/Overlay';
3
- export interface RoleSelectProps {
3
+ export interface RoleSelectControlProps {
4
+ /** CSS class passed to the selector FormControl component */
4
5
  className?: string;
6
+ /** Array of options the user can choose from */
5
7
  options: string[];
8
+ /** Currently selected value */
6
9
  value: string;
7
- onChange: any;
10
+ /** Function that is called when the value changes */
11
+ onChange: (option: string) => void;
12
+ /** (Optional) Provide custom user-friendly names for displayed roles */
13
+ textForRole?: (role: string) => string;
14
+ /** (Optional) Is the control disabled? */
15
+ disabled?: boolean;
16
+ }
17
+ export declare const RoleSelectControl: FunctionComponent<RoleSelectControlProps>;
18
+ export interface RoleSelectProps extends RoleSelectControlProps {
19
+ /** Id for the 'select' form control, defaults to 'RoleSelect' */
8
20
  controlId?: string;
21
+ /** Whether the label text should be visible */
9
22
  labelVisible?: boolean;
23
+ /** (Optional) Content of the popover tooltip */
10
24
  popoverContentComponent?: ReactElement<OverlayInjectedProps>;
11
- textForRole?: (role: string) => string;
12
25
  }
13
- /**
14
- * Create a selector which will be properly labeled based on the number of options and provided content.
15
- *
16
- * @param {string} props.className - CSS class passed to the selector FormControl component
17
- * @param {string[]} props.options - Array of options the user can choose from
18
- * @param {string} props.value - Currently selected value
19
- * @param {function} props.onChange - Function that is called when the value changes
20
- * @param {string} props.controlId - Id for the selector
21
- * @param {boolean} props.labelVisible - Whether the label text should be visible
22
- * @param {ReactNode} props.popoverContentComponent - Content of the popover tooltip
23
- *
24
- * @returns {ReactElement} Selector and label component
25
- */
26
- declare const RoleSelect: (props: RoleSelectProps) => JSX.Element;
27
- export default RoleSelect;
26
+ export declare const RoleSelect: FunctionComponent<RoleSelectProps>;
@@ -1,8 +1,31 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __rest = (this && this.__rest) || function (s, e) {
14
+ var t = {};
15
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
16
+ t[p] = s[p];
17
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
18
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
19
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
20
+ t[p[i]] = s[p[i]];
21
+ }
22
+ return t;
23
+ };
2
24
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
25
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
26
  };
5
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.RoleSelect = exports.RoleSelectControl = void 0;
6
29
  var Help_1 = __importDefault(require("@material-ui/icons/Help"));
7
30
  var react_1 = __importDefault(require("react"));
8
31
  var react_bootstrap_1 = require("react-bootstrap");
@@ -18,27 +41,18 @@ var overlay = function (hasMultipleOptions, labelVisible, rolePopover) {
18
41
  if (labelVisible === void 0) { labelVisible = false; }
19
42
  return rolePopover ? (react_1.default.createElement(react_bootstrap_1.OverlayTrigger, { placement: "right", trigger: ['click', 'hover', 'focus'], overlay: rolePopover }, label(hasMultipleOptions, labelVisible, true))) : (label(hasMultipleOptions, labelVisible, false));
20
43
  };
21
- var selector = function (options, value, onChange, textForRole, className) {
22
- return options.length > 1 ? (react_1.default.createElement(react_bootstrap_1.FormControl, { className: className ? className : '', "aria-label": "Role", as: "select", placeholder: "select", onChange: onChange, value: value }, options.map(function (role, i) { return (react_1.default.createElement("option", { value: role, key: role }, textForRole ? textForRole(role) : baseRole_1.textForBaseRole(role))); }))) : (react_1.default.createElement("div", null, textForRole ? textForRole(options[0]) : baseRole_1.textForBaseRole(options[0])));
44
+ var RoleSelectControl = function (_a) {
45
+ var className = _a.className, options = _a.options, value = _a.value, onChange = _a.onChange, textForRole = _a.textForRole, disabled = _a.disabled;
46
+ return options.length > 1 ? (react_1.default.createElement(react_bootstrap_1.FormControl, { className: className ? className : '', "aria-label": "Role", as: "select", placeholder: "select", onChange: function (e) {
47
+ onChange(e.target.value);
48
+ }, value: value, disabled: disabled }, options.map(function (role, i) { return (react_1.default.createElement("option", { value: role, key: role }, textForRole ? textForRole(role) : baseRole_1.textForBaseRole(role))); }))) : (react_1.default.createElement("div", null, textForRole ? textForRole(options[0]) : baseRole_1.textForBaseRole(options[0])));
23
49
  };
24
- /**
25
- * Create a selector which will be properly labeled based on the number of options and provided content.
26
- *
27
- * @param {string} props.className - CSS class passed to the selector FormControl component
28
- * @param {string[]} props.options - Array of options the user can choose from
29
- * @param {string} props.value - Currently selected value
30
- * @param {function} props.onChange - Function that is called when the value changes
31
- * @param {string} props.controlId - Id for the selector
32
- * @param {boolean} props.labelVisible - Whether the label text should be visible
33
- * @param {ReactNode} props.popoverContentComponent - Content of the popover tooltip
34
- *
35
- * @returns {ReactElement} Selector and label component
36
- */
37
- var RoleSelect = function (props) {
38
- var className = props.className, options = props.options, value = props.value, onChange = props.onChange, controlId = props.controlId, labelVisible = props.labelVisible, popoverContentComponent = props.popoverContentComponent, textForRole = props.textForRole;
50
+ exports.RoleSelectControl = RoleSelectControl;
51
+ var RoleSelect = function (_a) {
52
+ var controlId = _a.controlId, labelVisible = _a.labelVisible, popoverContentComponent = _a.popoverContentComponent, options = _a.options, controlProps = __rest(_a, ["controlId", "labelVisible", "popoverContentComponent", "options"]);
39
53
  var content = (react_1.default.createElement(react_1.default.Fragment, null,
40
54
  overlay(options.length > 1, !!labelVisible, popoverContentComponent),
41
- selector(options, value, onChange, textForRole, className)));
55
+ react_1.default.createElement(exports.RoleSelectControl, __assign({ options: options }, controlProps))));
42
56
  return options.length > 1 ? (react_1.default.createElement(react_bootstrap_1.FormGroup, { controlId: controlId ? controlId : 'RoleSelect' }, content)) : (react_1.default.createElement("span", null, content));
43
57
  };
44
- exports.default = RoleSelect;
58
+ exports.RoleSelect = RoleSelect;
@@ -1,16 +1,7 @@
1
1
  import React from 'react';
2
- import { UserRole, UserWithRoles } from '../../types';
2
+ import { UserWithRoles } from '../../types';
3
3
  export interface UserRolesTableProps {
4
4
  id?: string;
5
5
  users: UserWithRoles[];
6
- readOnly?: boolean;
7
- canModifySelf?: boolean;
8
- removeUserRole?: (userRole: UserRole) => void;
9
- textForRole?: (role: string) => string;
10
- roles: string[];
11
- /** The role for the owner of the entity, i.e. GroupOwner, RubricOwner, ProblemOwner etc.
12
- ** The current user cannot be removed from an ownership role unless they are an admin.
13
- **/
14
- entityOwnerRole: string;
15
6
  }
16
7
  export declare const UserRolesTable: React.NamedExoticComponent<UserRolesTableProps>;
@@ -29,9 +29,11 @@ var react_table_1 = __importDefault(require("react-table"));
29
29
  var userRole_1 = require("../../utils/userRole");
30
30
  var RoleFilter_1 = require("../Tables/RoleFilter");
31
31
  var TextFilter_1 = require("../Tables/TextFilter");
32
- var RoleCell_1 = __importDefault(require("./RoleCell"));
32
+ var Context_1 = require("./Context");
33
+ var RoleCell_1 = require("./RoleCell");
33
34
  var UserRolesTableComponent = function (_a) {
34
- var id = _a.id, users = _a.users, roles = _a.roles, readOnly = _a.readOnly, canModifySelf = _a.canModifySelf, textForRole = _a.textForRole, removeUserRole = _a.removeUserRole, entityOwnerRole = _a.entityOwnerRole;
35
+ var id = _a.id, users = _a.users;
36
+ var _b = react_1.useContext(Context_1.UserRolesContext), roles = _b.roles, requiredRole = _b.requiredRole, textForRole = _b.textForRole;
35
37
  var hasExternal = users.some(function (r) { return r.roles.some(function (r) { return userRole_1.isExternal(r); }); });
36
38
  var columns = [
37
39
  {
@@ -60,7 +62,9 @@ var UserRolesTableComponent = function (_a) {
60
62
  accessor: function (u) { return u; },
61
63
  Cell: function (cell) {
62
64
  var user = cell.value;
63
- return (react_1.default.createElement(RoleCell_1.default, { user: user, readOnly: readOnly, canModifySelf: canModifySelf, textForRole: textForRole, removeUserRole: removeUserRole, roles: roles, entityOwnerRole: entityOwnerRole }));
65
+ return (react_1.default.createElement(RoleCell_1.RoleCell, { user: user, requiredRoleCount: requiredRole
66
+ ? users.filter(function (u) { return u.roles.some(function (r) { return r.role === requiredRole; }); }).length
67
+ : undefined }));
64
68
  },
65
69
  filterMethod: RoleFilter_1.roleFilterMethod,
66
70
  Filter: RoleFilter_1.RoleFilter(roles, textForRole, hasExternal),
@@ -68,8 +72,8 @@ var UserRolesTableComponent = function (_a) {
68
72
  style: { padding: '4px' }
69
73
  });
70
74
  }
71
- var _b = react_1.useState(users), usersState = _b[0], setUsersState = _b[1];
72
- var _c = react_1.useState(), roleFilter = _c[0], setRoleFilter = _c[1];
75
+ var _c = react_1.useState(users), usersState = _c[0], setUsersState = _c[1];
76
+ var _d = react_1.useState(), roleFilter = _d[0], setRoleFilter = _d[1];
73
77
  var onFilteredChange = function (newFiltering, column, value) {
74
78
  var newRoleFilter = newFiltering.find(function (f) { return f.id === 'role'; });
75
79
  setRoleFilter(newRoleFilter);
@@ -1,54 +1,79 @@
1
1
  import React, { Component } from 'react';
2
2
  import { FetchError, Model, ModelCollection } from 'studiokit-net-js';
3
- import { BaseReduxState, ExternalProvider, RoleDescription, UserRole, UserWithRoles } from '../../types';
3
+ import { BaseReduxState, ExternalProvider, RoleDescriptions, UserRole, UserWithRoles } from '../../types';
4
4
  import { CollectionComponentWrappedProps } from '../HOC/CollectionComponent';
5
5
  export interface UserRolesReduxProps {
6
- canModifySelf?: boolean;
6
+ /** Is the current user allowed to add, update, and delete user roles? */
7
7
  canModify?: boolean;
8
+ /** Is the current user allowed to delete their own user role? */
9
+ canDeleteSelf?: boolean;
8
10
  }
9
11
  export interface UserRolesOwnProps extends CollectionComponentWrappedProps<UserRole> {
12
+ /** Is the current user allowed to delete their own user role? Overrides the value from redux. */
13
+ canDeleteSelf?: boolean;
14
+ /** If multiple roles allowed, each role is displayed per user, otherwise a select dropdown is displayed. */
15
+ allowMultipleRoles?: boolean;
16
+ /** disable adding new user roles */
17
+ isAddDisabled?: boolean;
18
+ /** disable all user role select dropdowns, if any */
19
+ isUpdateDisabled?: boolean;
20
+ /** disable all user role delete buttons */
21
+ isDeleteDisabled?: boolean;
22
+ /** The activity the current user must have in order to add, update, or delete user roles. */
10
23
  modifyUserRoleActivityName: string;
11
- filterUsers: (users: UserWithRoles[]) => UserWithRoles[];
12
- /** The role that will be shown first in the list of available roles. Currently assumes that this is the entity owner role, groupOwner, assignmentOwner, rubricOwner, problemOwner etc. */
24
+ /** (Optional) The activity the current user must have in order to delete their own user roles, if they cannot modify all. */
25
+ deleteOwnUserRoleActivityName?: string;
26
+ /** The role that will be shown first in the list of available roles. */
13
27
  defaultRole: string;
28
+ /** (Optional) If provided, the last user role with the required role will be prevented from being removed. */
29
+ requiredRole?: string;
14
30
  /** A dictionary of all possible roles and their descriptions. */
15
- roleDescriptions: RoleDescription;
16
- /** An optional blacklist of roles that should be excluded from the add options. */
17
- addRoleBlacklist?: string[];
18
- canModifySelf?: boolean;
19
- renderTableDescription?: (canModify?: boolean) => JSX.Element;
31
+ roleDescriptions: RoleDescriptions;
32
+ /** (Optional) list of roles that should be excluded from the add options. */
33
+ addRoleExcludeList?: string[];
34
+ /** (Optional) The entity that owns the user roles, when targeting EntityUserRoles */
20
35
  entity?: Model;
36
+ /** (Optional) A user-friendly name for the entity type, when targeting EntityUserRoles */
21
37
  entityName?: string;
38
+ /** (Optional) set if `renderRemoveUserRoleDescription` should check for roster sync */
39
+ externalProviders?: ModelCollection<ExternalProvider>;
40
+ /** (Optional) Allow users to be excluded from being displayed in the table */
41
+ filterUsers?: (users: UserWithRoles[]) => UserWithRoles[];
42
+ /** (Optional) Callback that is called after any change is made */
22
43
  onChange?: () => void;
44
+ /** (Optional) Render custom components between the Add and Table components */
45
+ renderTableDescription?: (canModify?: boolean) => JSX.Element;
46
+ /** (Optional) Render custom components at the start of the Add component */
23
47
  renderAddDescription?: (entityName?: string) => JSX.Element;
24
- disabled?: boolean;
48
+ /** (Optional) Provide custom user-friendly names for displayed roles */
25
49
  textForRole?: (role: string) => string;
50
+ /** (Optional) Provide custom user-friendly articles ("a" vs. "an") for displayed roles */
26
51
  singularArticleForRole?: (role: string) => string;
27
- isDeleteDisabled?: boolean;
28
- /** An optional parameter for `renderRemoveUserDescription` */
29
- externalProviders?: ModelCollection<ExternalProvider>;
30
- allowMultipleRoles?: boolean;
31
52
  }
32
53
  export interface UserRolesProps extends UserRolesReduxProps, UserRolesOwnProps {
33
54
  }
34
55
  interface UserRolesState {
35
56
  sortedUsers: UserWithRoles[];
36
57
  addUsersHookId: any;
37
- isAddingUsers: boolean;
58
+ isAdding: boolean;
38
59
  identifiersToAdd?: string[];
39
60
  roleForAdd?: string;
40
61
  shouldResetAddForm: boolean;
41
- userRoleToUpdate: UserRole | undefined;
62
+ userRoleToUpdate?: UserRole;
42
63
  roleForUpdate?: string;
64
+ isUpdating: boolean;
43
65
  shouldShowRemoveDialog: boolean;
44
- userRoleToRemove: UserRole | undefined;
66
+ userRoleToRemove?: UserRole;
67
+ isRemoving: boolean;
45
68
  successMessage?: string;
46
69
  existingMessage?: string;
70
+ blockedMessage?: string;
47
71
  failMessage?: string;
48
72
  }
49
73
  export interface AddBusinessModel {
50
74
  addedUserRoles: UserRole[];
51
75
  existingUserRoles: UserRole[];
76
+ blockedUserRoles?: UserRole[];
52
77
  invalidIdentifiers: string[];
53
78
  allowedDomains: string;
54
79
  invalidDomainIdentifiers: string[];
@@ -63,11 +88,11 @@ export declare class UserRoles extends Component<UserRolesProps, UserRolesState>
63
88
  setStateFromProps: (props: UserRolesProps) => void;
64
89
  textForRole: (role: string) => string;
65
90
  singularArticleForRole: (role: string) => string;
66
- addUserRoles: (ids: string[], role: string) => void;
91
+ addUserRoles: (identifiers: string[], role: string) => void;
67
92
  didAdd: (data?: FetchError | AddBusinessModel | undefined) => void;
68
- updateUserRole: (userToUpdate: UserRole, newRole: string) => void;
93
+ updateUserRole: (userRoleToUpdate: UserRole, newRole: string) => void;
69
94
  didUpdate: (isSuccess: boolean) => void;
70
- alertRemoveUserRole: (userToRemove: UserRole) => void;
95
+ alertRemoveUserRole: (userRoleToRemove: UserRole) => void;
71
96
  removeUserRoleTitle: (roleForRemove: string) => string;
72
97
  renderRemoveUserRoleDescription: (userRoleToRemove: UserRole, warning?: JSX.Element | undefined) => JSX.Element;
73
98
  removeUserRole: (shouldRemove: boolean) => void;
@@ -75,5 +100,5 @@ export declare class UserRoles extends Component<UserRolesProps, UserRolesState>
75
100
  render(): JSX.Element;
76
101
  }
77
102
  export declare const mapStateToProps: (state: BaseReduxState, ownProps: UserRolesOwnProps) => UserRolesReduxProps;
78
- declare const _default: import("react-redux").ConnectedComponent<typeof UserRoles, Pick<React.ClassAttributes<UserRoles> & UserRolesProps, "externalProviders" | "model" | "ref" | "onChange" | "key" | "disabled" | "guid" | "load" | "modelName" | "pathParams" | "modelStatus" | "queryParams" | "disableAutoLoad" | "modelArray" | "stopPeriodicLoad" | "create" | "update" | "delete" | "previousModelStatus" | "fetchingId" | "textForRole" | "entityName" | "defaultRole" | "roleDescriptions" | "renderAddDescription" | "modifyUserRoleActivityName" | "filterUsers" | "addRoleBlacklist" | "renderTableDescription" | "entity" | "singularArticleForRole" | "isDeleteDisabled" | "allowMultipleRoles"> & UserRolesOwnProps>;
103
+ declare const _default: import("react-redux").ConnectedComponent<typeof UserRoles, Pick<React.ClassAttributes<UserRoles> & UserRolesProps, "externalProviders" | "model" | "ref" | "onChange" | "key" | "guid" | "load" | "modelName" | "pathParams" | "modelStatus" | "queryParams" | "disableAutoLoad" | "modelArray" | "stopPeriodicLoad" | "create" | "update" | "delete" | "previousModelStatus" | "fetchingId" | "textForRole" | "entityName" | "defaultRole" | "roleDescriptions" | "renderAddDescription" | "isUpdateDisabled" | "isDeleteDisabled" | "allowMultipleRoles" | "requiredRole" | "isAddDisabled" | "modifyUserRoleActivityName" | "deleteOwnUserRoleActivityName" | "addRoleExcludeList" | "entity" | "filterUsers" | "renderTableDescription" | "singularArticleForRole"> & UserRolesOwnProps>;
79
104
  export default _default;
@@ -58,6 +58,7 @@ var sort_1 = require("../../utils/sort");
58
58
  var user_1 = require("../../utils/user");
59
59
  var userRole_1 = require("../../utils/userRole");
60
60
  var AlertWithIcon_1 = __importDefault(require("../AlertWithIcon"));
61
+ var Context_1 = require("./Context");
61
62
  /**
62
63
  * Component used to manage either UserRoles (global roles) or EntityUserRoles.
63
64
  */
@@ -69,19 +70,11 @@ var UserRoles = /** @class */ (function (_super) {
69
70
  sortedUsers: [],
70
71
  // add
71
72
  addUsersHookId: uuid_1.v4(),
72
- isAddingUsers: false,
73
- identifiersToAdd: undefined,
74
- roleForAdd: undefined,
73
+ isAdding: false,
74
+ isUpdating: false,
75
+ isRemoving: false,
75
76
  shouldResetAddForm: false,
76
- // update
77
- userRoleToUpdate: undefined,
78
- // remove
79
- shouldShowRemoveDialog: false,
80
- userRoleToRemove: undefined,
81
- // messages
82
- successMessage: undefined,
83
- existingMessage: undefined,
84
- failMessage: undefined
77
+ shouldShowRemoveDialog: false
85
78
  };
86
79
  _this.setStateFromProps = function (props) {
87
80
  var userRoles = props.modelArray, filterUsers = props.filterUsers;
@@ -97,7 +90,8 @@ var UserRoles = /** @class */ (function (_super) {
97
90
  result[userId] = userWithRoles;
98
91
  return result;
99
92
  }, {}));
100
- var sortedUsers = filterUsers(usersWithRoles).sort(sort_1.sortByNames);
93
+ var filteredUsers = filterUsers ? filterUsers(usersWithRoles) : usersWithRoles;
94
+ var sortedUsers = filteredUsers.sort(sort_1.sortByNames);
101
95
  _this.setState({
102
96
  sortedUsers: sortedUsers
103
97
  });
@@ -111,15 +105,17 @@ var UserRoles = /** @class */ (function (_super) {
111
105
  : baseRole_2.singularArticleForBaseRole(role);
112
106
  };
113
107
  //#region Add Users
114
- _this.addUserRoles = function (ids, role) {
108
+ _this.addUserRoles = function (identifiers, role) {
115
109
  var entity = _this.props.entity;
116
110
  _this.setState({
117
- identifiersToAdd: ids,
111
+ identifiersToAdd: identifiers,
118
112
  roleForAdd: role,
119
113
  successMessage: undefined,
120
114
  failMessage: undefined,
115
+ existingMessage: undefined,
116
+ blockedMessage: undefined,
121
117
  shouldResetAddForm: false,
122
- isAddingUsers: true
118
+ isAdding: true
123
119
  });
124
120
  var addUsersHookId = _this.state.addUsersHookId;
125
121
  studiokit_net_js_1.hooks.registerNoStoreActionHook(addUsersHookId, function (data) {
@@ -132,7 +128,7 @@ var UserRoles = /** @class */ (function (_super) {
132
128
  pathParams: entity ? [entity.id] : undefined,
133
129
  body: {
134
130
  entityId: entity ? entity.id : undefined,
135
- identifiers: ids,
131
+ identifiers: identifiers,
136
132
  roleName: role
137
133
  },
138
134
  noStore: true,
@@ -146,18 +142,17 @@ var UserRoles = /** @class */ (function (_super) {
146
142
  throw new Error('didAdd was called in the incorrect state');
147
143
  }
148
144
  if (!data || data.errorData) {
149
- // tslint:disable-next-line: no-shadowed-variable
150
145
  var failMessage_1 = "The following identifiers were not added" + (entityName ? " to your " + entityName : '') + ":\r\n" + identifiersToAdd.join('\r\n');
151
146
  _this.setState({
152
147
  shouldResetAddForm: true,
153
- isAddingUsers: false,
148
+ isAdding: false,
154
149
  identifiersToAdd: undefined,
155
150
  roleForAdd: undefined,
156
151
  failMessage: failMessage_1
157
152
  });
158
153
  return;
159
154
  }
160
- var _c = data, addedUserRoles = _c.addedUserRoles, existingUserRoles = _c.existingUserRoles, invalidIdentifiers = _c.invalidIdentifiers, allowedDomains = _c.allowedDomains, invalidDomainIdentifiers = _c.invalidDomainIdentifiers;
155
+ var _c = data, addedUserRoles = _c.addedUserRoles, existingUserRoles = _c.existingUserRoles, blockedUserRoles = _c.blockedUserRoles, invalidIdentifiers = _c.invalidIdentifiers, allowedDomains = _c.allowedDomains, invalidDomainIdentifiers = _c.invalidDomainIdentifiers;
161
156
  var roleString = _this.textForRole(roleForAdd).toLowerCase();
162
157
  var singularArticleString = _this.singularArticleForRole(roleForAdd);
163
158
  var addedNames = !!addedUserRoles && addedUserRoles.length > 0
@@ -181,6 +176,13 @@ var UserRoles = /** @class */ (function (_super) {
181
176
  ? ''
182
177
  : singularArticleString + " ") + roleString + (hasMultipleExisting ? 's' : '') + ":\r\n" + existingNames
183
178
  : undefined;
179
+ var blockedNames = !!blockedUserRoles && blockedUserRoles.length > 0
180
+ ? blockedUserRoles.map(function (eu) { return "" + user_1.displayName(eu) + (eu.uid ? " (" + eu.uid + ")" : ''); }).join('\r\n')
181
+ : undefined;
182
+ var hasMultipleBlocked = !!blockedUserRoles && blockedUserRoles.length > 1;
183
+ var blockedMessage = blockedNames
184
+ ? "The following " + (hasMultipleBlocked ? 'people' : 'person') + " could not be added " + (entityName ? "to your " + entityName : '') + " as " + (hasMultipleBlocked ? '' : singularArticleString + " ") + roleString + (hasMultipleBlocked ? 's' : '') + " because they already have a role:\r\n" + blockedNames
185
+ : undefined;
184
186
  var failMessage = !!invalidIdentifiers && invalidIdentifiers.length > 0
185
187
  ? "The following " + domainIdentifier_1.getDomainIdentifierTypePluralString() + " are invalid:\r\n" + invalidIdentifiers.join('\r\n')
186
188
  : undefined;
@@ -189,11 +191,12 @@ var UserRoles = /** @class */ (function (_super) {
189
191
  }
190
192
  _this.setState({
191
193
  shouldResetAddForm: true,
192
- isAddingUsers: false,
194
+ isAdding: false,
193
195
  identifiersToAdd: undefined,
194
196
  roleForAdd: undefined,
195
197
  successMessage: successMessage,
196
198
  existingMessage: existingMessage,
199
+ blockedMessage: blockedMessage,
197
200
  failMessage: failMessage
198
201
  });
199
202
  // only reload if we had some successful adds
@@ -213,16 +216,17 @@ var UserRoles = /** @class */ (function (_super) {
213
216
  };
214
217
  //#endregion Add Users
215
218
  //#region Update
216
- _this.updateUserRole = function (userToUpdate, newRole) {
219
+ _this.updateUserRole = function (userRoleToUpdate, newRole) {
217
220
  _this.setState({
218
- userRoleToUpdate: userToUpdate,
221
+ isUpdating: true,
222
+ userRoleToUpdate: userRoleToUpdate,
219
223
  roleForUpdate: newRole,
220
224
  successMessage: undefined,
221
225
  failMessage: undefined
222
226
  });
223
227
  _this.props.update({
224
- id: userToUpdate.id.toString(),
225
- body: undefined,
228
+ id: userRoleToUpdate.id.toString(),
229
+ body: {},
226
230
  queryParams: {
227
231
  roleName: newRole
228
232
  }
@@ -230,19 +234,20 @@ var UserRoles = /** @class */ (function (_super) {
230
234
  };
231
235
  _this.didUpdate = function (isSuccess) {
232
236
  var _a = _this.props, entityName = _a.entityName, onChange = _a.onChange;
233
- var _b = _this.state, userToUpdate = _b.userRoleToUpdate, roleForUpdate = _b.roleForUpdate;
234
- if (userToUpdate === undefined) {
235
- throw new Error('`didUpdate` was called without `userToUpdate` in state');
237
+ var _b = _this.state, userRoleToUpdate = _b.userRoleToUpdate, roleForUpdate = _b.roleForUpdate;
238
+ if (userRoleToUpdate === undefined) {
239
+ throw new Error('`didUpdate` was called without setting `userRoleToUpdate` in state');
236
240
  }
237
241
  if (roleForUpdate === undefined) {
238
- throw new Error('`didUpdate` was called without `roleForUpdate` in state');
242
+ throw new Error('`didUpdate` was called without setting `roleForUpdate` in state');
239
243
  }
240
- var name = user_1.displayName(userToUpdate);
244
+ var name = user_1.displayName(userRoleToUpdate);
241
245
  var roleString = _this.textForRole(roleForUpdate).toLowerCase();
242
246
  var singularArticleString = _this.singularArticleForRole(roleForUpdate);
243
247
  if (!isSuccess) {
244
248
  var failMessage = "Oops! There was an error updating " + name + " to " + singularArticleString + " " + roleString + (entityName ? " in your " + entityName : '') + ". Please try again.";
245
249
  _this.setState({
250
+ isUpdating: false,
246
251
  userRoleToUpdate: undefined,
247
252
  roleForUpdate: undefined,
248
253
  failMessage: failMessage
@@ -251,6 +256,7 @@ var UserRoles = /** @class */ (function (_super) {
251
256
  }
252
257
  var successMessage = name + " was successfully updated to " + singularArticleString + " " + roleString + (entityName ? " in your " + entityName : '') + ".";
253
258
  _this.setState({
259
+ isUpdating: false,
254
260
  userRoleToUpdate: undefined,
255
261
  roleForUpdate: undefined,
256
262
  successMessage: successMessage
@@ -261,9 +267,9 @@ var UserRoles = /** @class */ (function (_super) {
261
267
  };
262
268
  //#endregion Update
263
269
  //#region Remove User
264
- _this.alertRemoveUserRole = function (userToRemove) {
270
+ _this.alertRemoveUserRole = function (userRoleToRemove) {
265
271
  _this.setState({
266
- userRoleToRemove: userToRemove,
272
+ userRoleToRemove: userRoleToRemove,
267
273
  shouldShowRemoveDialog: true
268
274
  });
269
275
  };
@@ -312,6 +318,7 @@ var UserRoles = /** @class */ (function (_super) {
312
318
  throw new Error('`removeUser` was called without setting `userRoleToRemove` in state');
313
319
  }
314
320
  _this.setState({
321
+ isRemoving: shouldRemove,
315
322
  shouldShowRemoveDialog: false,
316
323
  successMessage: undefined,
317
324
  failMessage: undefined,
@@ -335,6 +342,7 @@ var UserRoles = /** @class */ (function (_super) {
335
342
  if (!isSuccess) {
336
343
  var failMessage = "Oops! There was an error removing " + name + (entityName ? " from your " + entityName : '') + ". Please try again.";
337
344
  _this.setState({
345
+ isRemoving: false,
338
346
  userRoleToRemove: undefined,
339
347
  failMessage: failMessage
340
348
  });
@@ -342,6 +350,7 @@ var UserRoles = /** @class */ (function (_super) {
342
350
  }
343
351
  var successMessage = name + " was successfully removed" + (entityName ? " from your " + entityName : '') + ".";
344
352
  _this.setState({
353
+ isRemoving: false,
345
354
  userRoleToRemove: undefined,
346
355
  successMessage: successMessage
347
356
  });
@@ -381,10 +390,10 @@ var UserRoles = /** @class */ (function (_super) {
381
390
  //#endregion Remove User
382
391
  UserRoles.prototype.render = function () {
383
392
  var _this = this;
384
- var _a = this.props, modelStatus = _a.modelStatus, canModify = _a.canModify, canModifySelf = _a.canModifySelf, entityName = _a.entityName, renderTableDescription = _a.renderTableDescription, defaultRole = _a.defaultRole, roleDescriptions = _a.roleDescriptions, renderAddDescription = _a.renderAddDescription, disabled = _a.disabled, isDeleteDisabled = _a.isDeleteDisabled, addRoleBlacklist = _a.addRoleBlacklist;
385
- var _b = this.state, isAddingUsers = _b.isAddingUsers, sortedUsers = _b.sortedUsers, shouldResetAddForm = _b.shouldResetAddForm, shouldShowRemoveDialog = _b.shouldShowRemoveDialog, userRoleToRemove = _b.userRoleToRemove, successMessage = _b.successMessage, existingMessage = _b.existingMessage, failMessage = _b.failMessage;
386
- var addableRoleDescriptions = addRoleBlacklist
387
- ? lodash_1.pickBy(roleDescriptions, function (_, key) { return !addRoleBlacklist.includes(key); })
393
+ var _a = this.props, modelStatus = _a.modelStatus, canModify = _a.canModify, canDeleteSelf = _a.canDeleteSelf, allowMultipleRoles = _a.allowMultipleRoles, isAddDisabled = _a.isAddDisabled, isUpdateDisabled = _a.isUpdateDisabled, isDeleteDisabled = _a.isDeleteDisabled, defaultRole = _a.defaultRole, requiredRole = _a.requiredRole, roleDescriptions = _a.roleDescriptions, addRoleExcludeList = _a.addRoleExcludeList, entityName = _a.entityName, renderTableDescription = _a.renderTableDescription, renderAddDescription = _a.renderAddDescription;
394
+ var _b = this.state, sortedUsers = _b.sortedUsers, isAdding = _b.isAdding, isUpdating = _b.isUpdating, isRemoving = _b.isRemoving, shouldResetAddForm = _b.shouldResetAddForm, shouldShowRemoveDialog = _b.shouldShowRemoveDialog, userRoleToUpdate = _b.userRoleToUpdate, userRoleToRemove = _b.userRoleToRemove, successMessage = _b.successMessage, existingMessage = _b.existingMessage, blockedMessage = _b.blockedMessage, failMessage = _b.failMessage;
395
+ var addableRoleDescriptions = addRoleExcludeList
396
+ ? lodash_1.pickBy(roleDescriptions, function (_, key) { return !addRoleExcludeList.includes(key); })
388
397
  : roleDescriptions;
389
398
  var roles = Object.keys(roleDescriptions);
390
399
  return (react_1.default.createElement(react_1.default.Fragment, null,
@@ -400,18 +409,38 @@ var UserRoles = /** @class */ (function (_super) {
400
409
  });
401
410
  } },
402
411
  react_1.default.createElement("p", { className: "pre-wrap" }, existingMessage))),
412
+ !!blockedMessage && (react_1.default.createElement(AlertWithIcon_1.default, { id: "blockedMessageAlert", variant: "warning", dismissible: true, onClose: function () {
413
+ return _this.setState({
414
+ blockedMessage: undefined
415
+ });
416
+ } },
417
+ react_1.default.createElement("p", { className: "pre-wrap" }, blockedMessage))),
403
418
  !!failMessage && (react_1.default.createElement(AlertWithIcon_1.default, { id: "failMessageAlert", variant: "warning", dismissible: true, onClose: function () {
404
419
  return _this.setState({
405
420
  failMessage: undefined
406
421
  });
407
422
  } },
408
423
  react_1.default.createElement("p", { className: "pre-wrap" }, failMessage))),
409
- canModify && (react_1.default.createElement(Add_1.default, { id: "entityUserRolesAdd", entityName: entityName, addUsersToRole: this.addUserRoles, shouldReset: shouldResetAddForm, isAddingUsersToRole: isAddingUsers, defaultRole: defaultRole, roleDescriptions: addableRoleDescriptions, renderAddDescription: renderAddDescription, disabled: disabled, textForRole: this.textForRole })),
424
+ canModify && (react_1.default.createElement(Add_1.default, { id: "entityUserRolesAdd", disabled: isAddDisabled, entityName: entityName, defaultRole: defaultRole, roleDescriptions: addableRoleDescriptions, renderAddDescription: renderAddDescription, textForRole: this.textForRole, isAddingUsersToRole: isAdding, shouldReset: shouldResetAddForm, addUserRoles: this.addUserRoles })),
410
425
  !!renderTableDescription && renderTableDescription(canModify),
411
426
  react_1.default.createElement(react_bootstrap_1.Row, { className: "mt3" },
412
- react_1.default.createElement(react_bootstrap_1.Col, { xs: 12 }, modelStatus === modelStatus_1.default.LOADING ? (react_1.default.createElement(Loading_1.default, null)) : sortedUsers.length > 0 ? (react_1.default.createElement(Table_1.UserRolesTable, { id: "entityUserRolesTable", users: sortedUsers, readOnly: !canModify || isDeleteDisabled, roles: roles,
413
- // Assumes the default is the groupOwner, assignment/assessmentOwner, rubricOwner, problemOwner etc.
414
- entityOwnerRole: defaultRole, canModifySelf: canModifySelf, removeUserRole: this.alertRemoveUserRole, textForRole: this.textForRole })) : null)),
427
+ react_1.default.createElement(react_bootstrap_1.Col, { xs: 12 }, modelStatus === modelStatus_1.default.LOADING ? (react_1.default.createElement(Loading_1.default, null)) : sortedUsers.length > 0 ? (react_1.default.createElement(Context_1.UserRolesContext.Provider, { value: {
428
+ isUpdateDisabled: isUpdateDisabled,
429
+ isDeleteDisabled: isDeleteDisabled,
430
+ canModify: canModify,
431
+ canDeleteSelf: canDeleteSelf,
432
+ allowMultipleRoles: allowMultipleRoles,
433
+ roles: roles,
434
+ requiredRole: requiredRole,
435
+ textForRole: this.textForRole,
436
+ userRoleToUpdate: userRoleToUpdate,
437
+ isUpdating: isUpdating,
438
+ updateUserRole: this.updateUserRole,
439
+ userRoleToRemove: userRoleToRemove,
440
+ isRemoving: isRemoving,
441
+ removeUserRole: this.alertRemoveUserRole
442
+ } },
443
+ react_1.default.createElement(Table_1.UserRolesTable, { id: "entityUserRolesTable", users: sortedUsers }))) : null)),
415
444
  !!userRoleToRemove && (react_1.default.createElement(AlertDialog_1.default, { id: "removeUserAlert", isOpen: shouldShowRemoveDialog, title: this.removeUserRoleTitle(userRoleToRemove.role), description: this.renderRemoveUserRoleDescription(userRoleToRemove), onDestroy: function () { return _this.removeUserRole(true); }, destroyText: "Yes, remove the user", onCancel: function () { return _this.removeUserRole(false); }, cancelText: "No, I changed my mind" }))));
416
445
  };
417
446
  return UserRoles;
@@ -419,13 +448,16 @@ var UserRoles = /** @class */ (function (_super) {
419
448
  exports.UserRoles = UserRoles;
420
449
  var mapStateToProps = function (state, ownProps) {
421
450
  var canModifyGlobally = baseActivity_1.canPerformActivityGlobally(ownProps.modifyUserRoleActivityName, baseActivity_1.defaultOptions(state));
451
+ var canModify = canModifyGlobally ||
452
+ (ownProps.entity
453
+ ? baseActivity_1.canPerformActivityOnEntity(ownProps.modifyUserRoleActivityName, baseActivity_1.defaultOptions(state, ownProps, 'entity'))
454
+ : false);
422
455
  return {
423
- canModifySelf: Object.prototype.hasOwnProperty.call(ownProps, 'canModifySelf')
424
- ? ownProps.canModifySelf
425
- : canModifyGlobally,
426
- canModify: ownProps.entity
427
- ? baseActivity_1.canPerformActivityGloballyOrOnEntity(ownProps.modifyUserRoleActivityName, baseActivity_1.defaultOptions(state, ownProps, 'entity'))
428
- : canModifyGlobally
456
+ canModify: canModify,
457
+ canDeleteSelf: canModify ||
458
+ (ownProps.entity && ownProps.deleteOwnUserRoleActivityName
459
+ ? baseActivity_1.canPerformActivityOnEntity(ownProps.deleteOwnUserRoleActivityName, baseActivity_1.defaultOptions(state, ownProps, 'entity'))
460
+ : false)
429
461
  };
430
462
  };
431
463
  exports.mapStateToProps = mapStateToProps;
@@ -1,4 +1,4 @@
1
1
  /// <reference types="react" />
2
- export interface RoleDescription {
2
+ export interface RoleDescriptions {
3
3
  [role: string]: React.ReactNode;
4
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "studiokit-scaffolding-js",
3
- "version": "4.4.0",
3
+ "version": "4.5.0",
4
4
  "description": "Common scaffolding for Studio apps at Purdue",
5
5
  "repository": "https://gitlab.com/purdue-informatics/studiokit/studiokit-scaffolding-js",
6
6
  "license": "MIT",