diff --git a/src/app/RootLayout.tsx b/src/app/RootLayout.tsx index 9d0d3259..3be129f6 100644 --- a/src/app/RootLayout.tsx +++ b/src/app/RootLayout.tsx @@ -42,6 +42,7 @@ import ManageApisPage from './pages/ManageApis'; import ManageRolesPage from './pages/ManageRoles'; import ManageUserRolesPage from './pages/ManageUserRoles'; import ManageSheriffsPage from './pages/ManageSheriffs'; +import ManageTeamPage from './pages/ManageTeam'; import ManageUsersPage from './pages/ManageUsers'; import SchedulingPage from './pages/Scheduling'; import PublishSchedulePage from './pages/PublishSchedule/PublishSchedule'; @@ -142,6 +143,7 @@ class Layout extends React.Component { + diff --git a/src/app/api/Api.ts b/src/app/api/Api.ts index c442ce57..a4a06f29 100644 --- a/src/app/api/Api.ts +++ b/src/app/api/Api.ts @@ -433,6 +433,9 @@ export interface User { systemAccountInd?: number; sheriffId?: IdType; sheriff?: Sheriff; + effectiveDate?: string; + expiryDate?: string; + isExpired?: boolean; // Only used on the client-side createdBy?: string; updatedBy?: string; createdDtm?: string; @@ -457,9 +460,10 @@ export interface UserRole { id?: IdType; userId?: IdType; roleId?: IdType; + locationId?: IdType; effectiveDate?: string; expiryDate?: string; - locationId?: IdType; + isExpired?: boolean; // Only used on the client-side createdBy?: string; updatedBy?: string; createdDtm?: string; @@ -771,15 +775,19 @@ export interface API { deleteUser(userId: IdType): Promise; getUsers(): Promise; deleteUsers(ids: IdType[]): Promise; + expireUsers(ids: IdType[]): Promise; + unexpireUsers(ids: IdType[]): Promise; getUserRole(): Promise; createUserRole(newUserRole: Partial): Promise; updateUserRole(updatedUserRole: UserRole): Promise; deleteUserRole(id: IdType): Promise; expireUserRole(id: IdType): Promise; + unexpireUserRole(id: IdType): Promise; getUserRoles(): Promise; deleteUserRoles(ids: IdType[]): Promise; expireUserRoles(ids: IdType[]): Promise; + unexpireUserRoles(ids: IdType[]): Promise; getRole(): Promise; createRole(newRole: Partial): Promise; diff --git a/src/app/api/Client.ts b/src/app/api/Client.ts index 8be78e97..61c1fcaa 100644 --- a/src/app/api/Client.ts +++ b/src/app/api/Client.ts @@ -927,6 +927,30 @@ export default class Client implements API { return Promise.resolve(); } + async expireUser(userId: IdType): Promise { + return await this._client.ExpireUser(userId); + } + + async expireUsers(ids: IdType[]): Promise { + if (ids.length > 0) { + ids.forEach(id => this._client.ExpireUser(id)); + } + + return Promise.resolve(); + } + + async unexpireUser(userId: IdType): Promise { + return await this._client.ExpireUser(userId); + } + + async unexpireUsers(ids: IdType[]): Promise { + if (ids.length > 0) { + ids.forEach(id => this._client.UnexpireUser(id)); + } + + return Promise.resolve(); + } + // Methods for roles async getRoles(): Promise { const list = await this._client.GetRoles(); @@ -1249,6 +1273,22 @@ export default class Client implements API { return Promise.resolve(); } + async unexpireUserRole(userRoleId: IdType): Promise { + if (userRoleId === undefined) { + return; + } + + return await this._client.UnexpireUserRole(userRoleId); + } + + async unexpireUserRoles(ids: IdType[]): Promise { + if (ids.length > 0) { + ids.forEach(id => this._client.UnexpireUserRole(id)); + } + + return Promise.resolve(); + } + async deleteUserRole(userRoleId: IdType): Promise { return Promise.resolve(); } diff --git a/src/app/components/Navigation.tsx b/src/app/components/Navigation.tsx index d667b915..5494bdc9 100644 --- a/src/app/components/Navigation.tsx +++ b/src/app/components/Navigation.tsx @@ -80,9 +80,13 @@ export default class Navigation extends React.Component - + + {currentUserRoleScopes.authScopes + && currentUserRoleScopes.authScopes.indexOf('users:manage') > -1 && ( + + )} {currentUserRoleScopes.authScopes && currentUserRoleScopes.authScopes.indexOf('roles:manage') > -1 && ( <> diff --git a/src/app/components/TableColumnActions/EditRow.tsx b/src/app/components/TableColumnActions/EditRow.tsx index adbd849b..db48e3b5 100644 --- a/src/app/components/TableColumnActions/EditRow.tsx +++ b/src/app/components/TableColumnActions/EditRow.tsx @@ -10,7 +10,7 @@ const EditRow = ({ model, onClick, showComponent = false }: TableColumnActionPro return ( Edit)} placement={'left'}> - diff --git a/src/app/modules/roles/actions.ts b/src/app/modules/roles/actions.ts index 23cfa22b..92de2c06 100644 --- a/src/app/modules/roles/actions.ts +++ b/src/app/modules/roles/actions.ts @@ -72,6 +72,7 @@ export const updateUserRole = userRoleRequests.updateUserRoleRequest.actionCreat export const createOrUpdateUserRoles = userRoleRequests.createOrUpdateUserRolesRequest.actionCreator; export const deleteUserRoles = userRoleRequests.deleteUserRolesRequest.actionCreator; export const expireUserRoles = userRoleRequests.expireUserRolesRequest.actionCreator; +export const unexpireUserRoles = userRoleRequests.unexpireUserRolesRequest.actionCreator; type IActionMap = { 'ADMIN_ROLES_SELECT_SECTION': string | undefined; diff --git a/src/app/modules/roles/requests/userRoles.ts b/src/app/modules/roles/requests/userRoles.ts index ec7d769b..aa3842b1 100644 --- a/src/app/modules/roles/requests/userRoles.ts +++ b/src/app/modules/roles/requests/userRoles.ts @@ -127,13 +127,38 @@ class ExpireUserRolesRequest extends RequestAction delete newMap[id]); + roleScopeIds.forEach(id => newMap[id]); return userRoleMapRequest.setRequestData(moduleState, newMap); } } export const expireUserRolesRequest = new ExpireUserRolesRequest(); +class UnexpireUserRolesRequest extends RequestAction { + constructor() { + super({ + namespace: STATE_KEY, + actionName: 'unexpireUserRoles', + toasts: { + success: (ids) => `${ids.length} user role(s) un-expired`, + error: (err) => `Problem encountered while un-expiring user roles: ${err ? err.toString() : 'Unknown Error'}` + } + }); + } + public async doWork(request: IdType[], { api }: ThunkExtra): Promise { + await api.unexpireUserRoles(request); + return request; + } + + setRequestData(moduleState: RoleModuleState, roleScopeIds: IdType[]) { + const newMap = { ...userRoleMapRequest.getRequestData(moduleState) }; + roleScopeIds.forEach(id => newMap[id]); + return userRoleMapRequest.setRequestData(moduleState, newMap); + } +} + +export const unexpireUserRolesRequest = new UnexpireUserRolesRequest(); + class DeleteUserRolesRequest extends RequestAction { constructor() { super({ diff --git a/src/app/modules/roles/selectors/userRoles.ts b/src/app/modules/roles/selectors/userRoles.ts index 83137374..948efb5c 100644 --- a/src/app/modules/roles/selectors/userRoles.ts +++ b/src/app/modules/roles/selectors/userRoles.ts @@ -3,7 +3,8 @@ import * as userRoleRequests from '../requests/userRoles'; import mapToArray from '../../../infrastructure/mapToArray'; import { RootState } from '../../../store'; -import { func as selectorFunctions } from '../../common'; +import { EffectiveSelector } from '../../../infrastructure/EffectiveSelector'; +import { UserRole } from '../../../api'; export const getUserRoles = createSelector( userRoleRequests.userRoleMapRequest.getData, @@ -13,16 +14,25 @@ export const getUserRoles = createSelector( } ); +const userRoleSelector = new EffectiveSelector( + userRoleRequests.userRoleMapRequest.getData, + (ur) => ur.expiryDate +); + +export const allUserRoles = userRoleSelector.all; + +export const allEffectiveUserRoles = userRoleSelector.effective; + export const getAllUserRoles = (state: RootState) => { if (state) { - return getUserRoles(state); + return allUserRoles(state); } return undefined; }; export const findAllUserRoles = (filters: any) => (state: RootState) => { if (state) { - return getUserRoles(state); + return allUserRoles(state); } return undefined; }; diff --git a/src/app/modules/users/actions.ts b/src/app/modules/users/actions.ts index 21d6ce81..943bc85e 100644 --- a/src/app/modules/users/actions.ts +++ b/src/app/modules/users/actions.ts @@ -6,3 +6,5 @@ export const uploadUserImage = userRequests.uploadUserImageRequest.actionCreator export const updateUser = userRequests.updateUserRequest.actionCreator; export const createOrUpdateUsers = userRequests.createOrUpdateUsersRequest.actionCreator; export const deleteUsers = userRequests.deleteUsersRequest.actionCreator; +export const expireUsers = userRequests.expireUsersRequest.actionCreator; +export const unexpireUsers = userRequests.unexpireUsersRequest.actionCreator; diff --git a/src/app/modules/users/requests.ts b/src/app/modules/users/requests.ts index 8886d875..731e5308 100644 --- a/src/app/modules/users/requests.ts +++ b/src/app/modules/users/requests.ts @@ -19,6 +19,8 @@ import CreateEntityRequest from '../../infrastructure/Requests/CreateEntityReque import UpdateEntityRequest from '../../infrastructure/Requests/UpdateEntityRequest'; import toTitleCase from '../../infrastructure/toTitleCase'; import DeleteEntityRequest from '../../infrastructure/Requests/DeleteEntityRequest'; +import { RoleModuleState } from '../roles/common'; +import { userRoleMapRequest } from '../roles/requests/userRoles'; // User Map class UserMapRequest extends GetEntityMapRequest { @@ -175,6 +177,64 @@ class CreateOrUpdateUsersRequest extends CreateOrUpdateEntitiesRequest { + constructor() { + super( + { + namespace: STATE_KEY, + actionName: 'expireUsers', + toasts: { + success: (ids) => `${ids.length} user(s) expired`, + // tslint:disable-next-line:max-line-length + error: (err) => `Problem encountered while expiring users: ${err ? err.toString() : 'Unknown Error'}` + } + } + ); + } + + public async doWork(request: IdType[], { api }: ThunkExtra): Promise { + await api.expireUsers(request); + return request; + } + + setRequestData(moduleState: UserModuleState, userIds: IdType[]) { + const newMap = { ...userMapRequest.getRequestData(moduleState) }; + userIds.forEach(id => newMap[id]); + return userMapRequest.setRequestData(moduleState, newMap); + } +} + +export const expireUsersRequest = new ExpireUsersRequest(); + +class UnexpireUsersRequest extends RequestAction { + constructor() { + super( + { + namespace: STATE_KEY, + actionName: 'unexpireUsers', + toasts: { + success: (ids) => `${ids.length} user(s) un-expired`, + // tslint:disable-next-line:max-line-length + error: (err) => `Problem encountered while un-expiring users: ${err ? err.toString() : 'Unknown Error'}` + } + } + ); + } + + public async doWork(request: IdType[], { api }: ThunkExtra): Promise { + await api.unexpireUsers(request); + return request; + } + + setRequestData(moduleState: UserModuleState, userIds: IdType[]) { + const newMap = { ...userMapRequest.getRequestData(moduleState) }; + userIds.forEach(id => newMap[id]); + return userMapRequest.setRequestData(moduleState, newMap); + } +} + +export const unexpireUsersRequest = new UnexpireUsersRequest(); + class DeleteUsersRequest extends RequestAction { constructor() { super({ @@ -188,9 +248,15 @@ class DeleteUsersRequest extends RequestAction { - await api.deleteRoles(request); + await api.deleteUsers(request); return request; } + + setRequestData(moduleState: RoleModuleState, userIds: IdType[]) { + const newMap = { ...userMapRequest.getRequestData(moduleState) }; + userIds.forEach(id => delete newMap[id]); + return userMapRequest.setRequestData(moduleState, newMap); + } } export const deleteUsersRequest = new DeleteUsersRequest(); diff --git a/src/app/modules/users/selectors.ts b/src/app/modules/users/selectors.ts index 61c8c92e..63c2a548 100644 --- a/src/app/modules/users/selectors.ts +++ b/src/app/modules/users/selectors.ts @@ -3,15 +3,12 @@ import * as requests from './requests'; import { RootState } from '../../store'; import { IdType, MapType, User -} from '../../api/Api'; +} from '../../api'; import mapToArray from '../../infrastructure/mapToArray'; import { currentLocation as currentLocationSelector } from '../user/selectors'; import { getUserRoles } from '../roles/selectors'; -// TODO: Leaving these in here cause they could be useful -// import { ErrorMap } from './common'; -// import { CodeSelector } from '../../infrastructure/CodeSelector'; -// import { getLocationById } from '../system/selectors'; +import { EffectiveSelector } from '../../infrastructure/EffectiveSelector'; export const getUsers = createSelector( requests.userMapRequest.getData, @@ -20,6 +17,15 @@ export const getUsers = createSelector( } ); +const userSelector = new EffectiveSelector( + requests.userMapRequest.getData, + (u) => u.expiryDate +); + +export const allUsers = userSelector.all; + +export const allEffectiveUsers = userSelector.effective; + export const usersForCurrentLocation = createSelector( getUsers, currentLocationSelector, @@ -30,7 +36,7 @@ export const usersForCurrentLocation = createSelector( export const getAllUsers = (state: RootState) => { if (state) { - const users = getUsers(state); + const users = allEffectiveUsers()(state); // Don't return ALL users, just a slice or we can blow up the DOM if we render too many elements let { min = 0, max = 10 } = { min: 0, max: 25 }; @@ -77,7 +83,7 @@ export const findAllUsers = (filters: any) => (state: RootState) => { if (state) { // console.log('finding all users (findAllUsers) using filters'); // console.log(filters); - let users = getUsers(state); + let users = allUsers(state); /*.sort((a: any, b: any) => (a.lastName < b.lastName) ? -1 : (a.lastName > b.lastName) ? 1 : 0);*/ @@ -141,14 +147,14 @@ export const findUserRolesGroupedByUserId = (filters: any) => (state: RootState) export const getUser = (id?: IdType) => (state: RootState) => { if (state && id !== undefined) { - return getUsers(state).find((user: User) => user.id === id as string); + return allUsers(state).find((user: User) => user.id === id as string); } return undefined; }; export const getUserByAuthId = (userAuthId?: IdType) => (state: RootState) => { if (state && userAuthId !== undefined) { - const users = getUsers(state); + const users = allUsers(state); if (users && users.length > 0) { console.log('we have users'); } diff --git a/src/app/pages/ManageTeam.tsx b/src/app/pages/ManageTeam.tsx new file mode 100644 index 00000000..d313415d --- /dev/null +++ b/src/app/pages/ManageTeam.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { Button, Glyphicon, Table, Well } from 'react-bootstrap'; +import { reduxForm } from 'redux-form'; + +import Page, { PageToolbar } from '../components/Page/Page'; +import AdminForm from '../containers/AdminForm'; +import PageTitle from '../containers/PageTitle'; + +import { AdminFormProps } from '../components/AdminForm/AdminForm'; + +// Import plugins +import AdminUsersPlugin from '../plugins/AdminUsers/AdminUsers'; + +import SheriffListComposable from '../containers/SheriffListComposable'; +import SheriffProfileCreateModal from '../containers/SheriffProfileCreateModal'; +import HeaderSaveButton from '../plugins/AdminRoles/containers/HeaderSaveButton'; +import HeaderCancelButton from '../plugins/AdminRoles/containers/HeaderCancelButton'; + +export interface ManageTeamProps extends RouteComponentProps{} + +const DISPLAY_CARDS = 'CARDS'; +const DISPLAY_LIST = 'LIST'; + +class ManageTeam extends React.PureComponent> { + state = { + isEditing: true, + displayMode: DISPLAY_LIST, + displayFilters: true + }; + + constructor(props: AdminFormProps) { + super(props); + + this.toggleDisplayMode = this.toggleDisplayMode.bind(this); + this.toggleFilters = this.toggleFilters.bind(this); + this.toggleEditMode = this.toggleEditMode.bind(this); + } + + toggleDisplayMode() { + const { displayMode } = this.state; + let mode = displayMode === DISPLAY_LIST ? DISPLAY_CARDS : displayMode === DISPLAY_CARDS ? DISPLAY_LIST : null; + this.setState({ + displayMode: mode + }); + } + + toggleFilters() { + this.setState({ + displayFilters: !this.state.displayFilters + }); + } + + toggleEditMode() { + this.setState({ + isEditing: !this.state.isEditing + }); + } + + render() { + const { history, location } = this.props; + const { isEditing, displayMode, displayFilters = true } = this.state; + + return ( + +   + + + )} + /> + } + > + + `My Team - ${currentLocationName}`} /> + + )} + + + ); + } +} + +export default reduxForm({ form: 'UsersGrid' })(withRouter(ManageTeam) as any); diff --git a/src/app/pages/ManageUserRoles.tsx b/src/app/pages/ManageUserRoles.tsx index cb0b660e..91a44909 100644 --- a/src/app/pages/ManageUserRoles.tsx +++ b/src/app/pages/ManageUserRoles.tsx @@ -19,15 +19,23 @@ export interface ManageUserRolesProps extends RouteComponentProps{}; class ManageUserRoles extends React.PureComponent> { state = { - isEditing: true + isEditing: true, + displayFilters: true }; constructor(props: AdminFormProps) { super(props); + this.toggleFilters = this.toggleFilters.bind(this); this.toggleEditMode = this.toggleEditMode.bind(this); } + toggleFilters() { + this.setState({ + displayFilters: !this.state.displayFilters + }); + } + toggleEditMode() { this.setState({ isEditing: !this.state.isEditing @@ -36,46 +44,28 @@ class ManageUserRoles extends React.PureComponent -
-   Display User Search Filters +
+ {!displayFilters && ( + <>  Display User Search Filters + )} + {displayFilters && ( + <>  Display User Search Filters + )}
)} right={(
- {/* -    */} - -    - {/* - Try This - */}       @@ -86,7 +76,7 @@ class ManageUserRoles extends React.PureComponent `Assign ${currentLocationName} User Roles`} /> diff --git a/src/app/pages/ManageUsers.tsx b/src/app/pages/ManageUsers.tsx index d68940d9..1bbfe41d 100644 --- a/src/app/pages/ManageUsers.tsx +++ b/src/app/pages/ManageUsers.tsx @@ -14,6 +14,8 @@ import AdminUsersPlugin from '../plugins/AdminUsers/AdminUsers'; import SheriffListComposable from '../containers/SheriffListComposable'; import SheriffProfileCreateModal from '../containers/SheriffProfileCreateModal'; +import HeaderSaveButton from '../plugins/AdminRoles/containers/HeaderSaveButton'; +import HeaderCancelButton from '../plugins/AdminRoles/containers/HeaderCancelButton'; export interface ManageUsersProps extends RouteComponentProps{} @@ -24,7 +26,7 @@ class ManageUsers extends React.PureComponent   - + */}    - {/* -    */}    - {/* - Try This - */} + +    +
)} /> @@ -126,7 +114,7 @@ class ManageUsers extends React.PureComponent `Manage ${currentLocationName} Users`} /> {displayMode === DISPLAY_LIST && ( { + return (colIndex === 1); +}; + export default class AdminAssignUserRoles extends FormContainerBase { // NOTICE! // This key maps to the [appScope: FrontendScope] (in the token) @@ -154,9 +165,16 @@ export default class AdminAssignUserRoles extends FormContainerBase = ({ parentModelId, parentModel, getPluginPermissions }) => { const { grantAll, permissions } = buildPluginPermissions(getPluginPermissions); + // We can't use React hooks yet, and not sure if this project will ever be upgraded to 16.8 + // This is a quick n' dirty way to achieve the same thing + let dataTableInstance: any; + const onButtonClicked = (ev: React.SyntheticEvent, context: any, model: any) => { context.setActiveRow(model.id); }; @@ -172,8 +190,10 @@ export default class AdminAssignUserRoles extends FormContainerBase { - return (model && model.id && model.id !== '') - ? () + return (model && model.id && model.id !== '' && !model.isExpired) + ? ( dataTableInstance.forceUpdate()} />) + : (model && model.isExpired) + ? ( dataTableInstance.forceUpdate()} />) : null; }, ({ fields, index, model }) => { @@ -185,28 +205,29 @@ export default class AdminAssignUserRoles extends FormContainerBase dataTableInstance = dt} fieldName={`${this.formFieldNames.userRolesGrouped}['${parentModelId}']`} title={''} // Leave this blank - buttonLabel={'Assign New Role'} + // TODO: Button action to forward to user roles page and expand row + buttonLabel={'Assign User Roles'} displayHeaderActions={true} displayHeaderSave={false} actionsColumn={DataTable.ActionsColumn({ actions: userRoleActions })} columns={[ - DataTable.SelectorFieldColumn('Assigned Role', { fieldName: 'roleId', colStyle: { width: '275px' }, selectorComponent: RoleSelector, displayInfo: true }), - // DataTable.StaticTextColumn('Description', { fieldName: 'description', colStyle: { width: '350px' }, displayInfo: false }), - // DataTable.StaticTextColumn('Role Code', { fieldName: 'roleCode', colStyle: { width: '180px' }, displayInfo: false }), - DataTable.DateColumn('Effective Date', 'effectiveDate', { colStyle: { width: '150px'}, displayInfo: true }), - // DataTable.DateColumn('Date Created', 'createdDtm'), - DataTable.DateColumn('Expiry Date', 'expiryDate', { colStyle: { width: '150px'}, displayInfo: true }), - DataTable.SelectorFieldColumn('Status', { colStyle: { width: '175px' }, displayInfo: true }), - DataTable.StaticTextColumn('Assigned By', { fieldName: 'createdBy', colStyle: { width: '200px' }, displayInfo: false }), - DataTable.StaticDateColumn('Date Assigned', { fieldName: 'createdDtm', colStyle: { width: '200px' }, displayInfo: false }), + DataTable.SelectorFieldColumn('Assigned Role', { fieldName: 'roleId', colStyle: { width: '18%' }, selectorComponent: RoleSelector, displayInfo: true }), + DataTable.DateColumn('Effective Date', 'effectiveDate', { colStyle: { width: '15%'}, displayInfo: true }), + DataTable.DateColumn('Expiry Date', 'expiryDate', { colStyle: { width: '15%'}, displayInfo: true }), + DataTable.StaticTextColumn('Assigned By', { fieldName: 'createdBy', colStyle: { width: '15%' }, displayInfo: false }), + DataTable.StaticDateColumn('Date Assigned', { fieldName: 'createdDtm', colStyle: { width: '15%' }, displayInfo: false }), ]} expandable={false} - rowComponent={EmptyDetailRow} shouldDisableRow={() => parentModel.systemAccountInd === 1} + shouldMarkRowAsDeleted={(model) => { + return model.isExpired; + }} + rowComponent={EmptyDetailRow} initialValue={{ userId: parentModelId }} @@ -218,10 +239,20 @@ export default class AdminAssignUserRoles extends FormContainerBase) => { const { showSheriffProfileModal } = props; + const { getPluginPermissions, setPluginFilters, displayFilters } = props; + const { grantAll, permissions } = buildPluginPermissions(getPluginPermissions); + + // We can't use React hooks yet, and not sure if this project will ever be upgraded to 16.8 + // This is a quick n' dirty way to achieve the same thing + let dataTableInstance: any; + + const onButtonClicked = (ev: React.SyntheticEvent<{}>, context?: any, model?: any) => { + // Executes in DataTable's context + if (model) context.setActiveRow(model.id); + }; // TODO: We need to find a way to make sorting on multiple columns work, which probably involves figuring how to grab all the field values at once... const onFilterDisplayName = (event: Event, newValue: any, previousValue: any, name: string) => { - const { setPluginFilters } = props; if (setPluginFilters) { // console.log('setting plugin filters'); setPluginFilters({ @@ -233,7 +264,6 @@ export default class AdminAssignUserRoles extends FormContainerBase { - const { setPluginFilters } = props; if (setPluginFilters) { // console.log('setting plugin filters'); setPluginFilters({ @@ -248,7 +278,6 @@ export default class AdminAssignUserRoles extends FormContainerBase { - const { setPluginFilters } = props; if (setPluginFilters) { // console.log('setting plugin filters'); setPluginFilters({ @@ -263,7 +292,6 @@ export default class AdminAssignUserRoles extends FormContainerBase { - const { setPluginFilters } = props; if (setPluginFilters) { // console.log('setting plugin filters'); setPluginFilters({ @@ -278,7 +306,6 @@ export default class AdminAssignUserRoles extends FormContainerBase { - const { setPluginFilters } = props; if (setPluginFilters) { // console.log('setting plugin filters'); setPluginFilters({ @@ -293,9 +320,9 @@ export default class AdminAssignUserRoles extends FormContainerBase { - const { setPluginFilters } = props; if (setPluginFilters) { - // console.log('setting plugin filters'); + console.log('setting plugin filters'); + console.log(newValue); setPluginFilters({ users: { sheriff: { @@ -307,7 +334,6 @@ export default class AdminAssignUserRoles extends FormContainerBase { - const { setPluginFilters } = props; if (setPluginFilters) { // console.log('reset plugin filters'); setPluginFilters({ @@ -318,9 +344,19 @@ export default class AdminAssignUserRoles extends FormContainerBase { + return (model && model.id && model.id !== '') + ? ( + + ) + : null; + }, + /* ({ fields, index, model }) => { return (model && model.id) ? ( { return (model && !model.id || model && model.id === '') - ? () + ? () : null; }, - /*({ fields, index, model }) => { - return (model && model.id && model.id !== '') - ? () + ({ fields, index, model }) => { + return (model && model.id && model.id !== '' && !model.isExpired) + ? ( dataTableInstance.forceUpdate()} />) + : (model && model.isExpired) + ? ( dataTableInstance.forceUpdate()} />) : null; - },*/ + }, ({ fields, index, model }) => { return (model && model.id && model.id !== '') - ? () + ? () : null; - } + } */ ] as React.ReactType[]; + const imageUrl = null; + + // @ts-ignore + let imageSrc = (imageUrl) + ? imageUrl + : avatarImg; + return (
dataTableInstance = dt} fieldName={this.formFieldNames.users} filterFieldName={(this.filterFieldNames) ? `${this.filterFieldNames.users}` : undefined} title={''} // Leave this blank buttonLabel={'Add User'} displayHeaderActions={false} + displayHeaderSave={false} onResetClicked={onResetFilters} displayActionsColumn={true} actionsColumn={DataTable.ActionsColumn({ actions: userActions })} columns={[ + // TODO: We temporarily disabled filtering on badgeNo, it's tied to the sheriff, not sure how to handle that case yet... + DataTable.HtmlColumn('', { + colStyle: { width: '32px' }, + component: ( +
+ {'Profile false} + src={imageSrc} + width="24" + height="24" + style={{ + width: '24px', + height: '24px' + }} + /> +
+ ) + }), + // TODO: We temporarily disabled filtering on badgeNo, it's tied to the sheriff, not sure how to handle that case yet... + DataTable.StaticTextColumn('Badge No.', { + fieldName: 'sheriff.badgeNo', + colStyle: { width: '8%' }, + displayInfo: false, + filterable: true, + filterColumn: onFilterBadgeNo + }), DataTable.StaticTextColumn('Full Name', { fieldName: 'displayName', - colStyle: { width: '15%' }, + colStyle: { width: '22%' }, displayInfo: false, filterable: true, filterColumn: onFilterDisplayName }), - // DataTable.StaticTextColumn('Last Name', { fieldName: 'lastName', colStyle: { width: '175px' }, displayInfo: false, filterable: true }), - // TODO: We temporarily disabled filtering on badgeNo, it's tied to the sheriff, not sure how to handle that case yet... - DataTable.StaticTextColumn('Badge No.', { - fieldName: 'sheriff.badgeNo', + // TODO: Searcg by all locations + DataTable.MappedTextColumn('Location', { + fieldName: 'sheriff.homeLocationId', colStyle: { width: '15%' }, + selectorComponent: LocationDisplay, + filterSelectorComponent: LocationSelector, displayInfo: false, filterable: true, - filterColumn: onFilterBadgeNo + filterColumn: onFilterCurrentLocation }), DataTable.MappedTextColumn('Rank', { fieldName: 'sheriff.rankCode', @@ -392,6 +468,7 @@ export default class AdminAssignUserRoles extends FormContainerBase { + return model.isExpired; + }} rowComponent={this.renderDetail()} modalComponent={EmptyDetailRow} + shouldSortBy={shouldSortByFunc} + />
); @@ -499,6 +571,11 @@ export default class AdminAssignUserRoles extends FormContainerBase val.isExpired === isExpired) + .map((val: any) => val.id); + + expiredUserIds.push(...userIds); + } + + const expiredUserRoleIds: IdType[] = []; + + if (map.userRolesGrouped) { + const values = map.userRolesGrouped.values; + + const expireUserRoleIds = Object.keys(values).reduce((acc: any, cur: any) => { + const userRoleIds = values[cur] + .filter((val: any) => val.isExpired === isExpired) + .map((val: any) => val.id); + + return acc.concat(userRoleIds); + }, []); + + expiredUserRoleIds.push(...expireUserRoleIds); + } + + console.log('expired user role ids'); + console.log(expiredUserRoleIds); + + return { + users: expiredUserIds, + userRoles: expiredUserRoleIds }; } async onSubmit(formValues: any, initialValues: any, dispatch: Dispatch) { const data: any = this.getDataFromFormValues(formValues, initialValues) || {}; + const dataToExpire: any = this.getDataToExpireFromFormValues(formValues, initialValues, true) || {}; + const dataToUnexpire: any = this.getDataToExpireFromFormValues(formValues, initialValues, false) || {}; const dataToDelete: any = this.getDataToDeleteFromFormValues(formValues, initialValues) || {}; // Delete records before saving new ones! const deletedUsers: IdType[] = dataToDelete.users as IdType[]; const deletedUserRoles: IdType[] = dataToDelete.userRoles as IdType[]; + // Expire records before saving new ones! + const expiredUsers: IdType[] = dataToExpire.users as IdType[]; + const expiredUserRoles: IdType[] = dataToExpire.userRoles as IdType[]; + const unexpiredUsers: IdType[] = dataToUnexpire.users as IdType[]; + const unexpiredUserRoles: IdType[] = dataToUnexpire.userRoles as IdType[]; + const users: Partial[] = (data.users) ? data.users.map((u: User) => ({ ...u, // TODO: Need a way to set this stuff... createdBy, updated by fields should really be set in the backend using the current user @@ -587,12 +706,24 @@ export default class AdminAssignUserRoles extends FormContainerBase 0) { - // await dispatch(deleteUserRoles(deletedUserRoles)); + if (expiredUsers.length > 0) { + await dispatch(expireUsers(expiredUsers)); + } + + if (unexpiredUsers.length > 0) { + await dispatch(unexpireUsers(unexpiredUsers)); } if (deletedUserRoles.length > 0) { - await dispatch(expireUserRoles(deletedUserRoles)); + await dispatch(deleteUserRoles(deletedUserRoles)); + } + + if (expiredUserRoles.length > 0) { + await dispatch(expireUserRoles(expiredUserRoles)); + } + + if (unexpiredUserRoles.length > 0) { + await dispatch(unexpireUserRoles(unexpiredUserRoles)); } // We don't update users here, unless we're deleting them... diff --git a/src/app/plugins/AdminUsers/AdminUsers.tsx b/src/app/plugins/AdminUsers/AdminUsers.tsx index ce6aa7f0..8097755d 100644 --- a/src/app/plugins/AdminUsers/AdminUsers.tsx +++ b/src/app/plugins/AdminUsers/AdminUsers.tsx @@ -12,7 +12,9 @@ import { Dispatch } from 'redux'; import { getUsers, createOrUpdateUsers, - deleteUsers + deleteUsers, + expireUsers, + unexpireUsers } from '../../modules/users/actions'; import { @@ -37,6 +39,7 @@ import { createOrUpdateUserRoles, deleteUserRoles, expireUserRoles, + unexpireUserRoles, setAdminRolesPluginFilters } from '../../modules/roles/actions'; @@ -168,6 +171,10 @@ export default class AdminUsers extends FormContainerBase { DetailComponent: React.SFC = ({ parentModelId, parentModel, getPluginPermissions }) => { const { grantAll, permissions } = buildPluginPermissions(getPluginPermissions); + // We can't use React hooks yet, and not sure if this project will ever be upgraded to 16.8 + // This is a quick n' dirty way to achieve the same thing + let dataTableInstance: any; + const onButtonClicked = (ev: React.SyntheticEvent, context: any, model: any) => { context.setActiveRow(model.id); }; @@ -183,8 +190,10 @@ export default class AdminUsers extends FormContainerBase { : null; }, ({ fields, index, model }) => { - return (model && model.id && model.id !== '') - ? () + return (model && model.id && model.id !== '' && !model.isExpired) + ? ( dataTableInstance.forceUpdate()} />) + : (model && model.isExpired) + ? ( dataTableInstance.forceUpdate()} />) : null; }, ({ fields, index, model }) => { @@ -196,29 +205,29 @@ export default class AdminUsers extends FormContainerBase { return ( dataTableInstance = dt} fieldName={`${this.formFieldNames.userRolesGrouped}['${parentModelId}']`} title={''} // Leave this blank // TODO: Button action to forward to user roles page and expand row buttonLabel={'Assign User Roles'} displayHeaderActions={true} - displayHeaderSave={true} + displayHeaderSave={false} actionsColumn={DataTable.ActionsColumn({ actions: userRoleActions })} columns={[ - DataTable.SelectorFieldColumn('Assigned Role', { fieldName: 'roleId', colStyle: { width: '275px' }, selectorComponent: RoleSelector, displayInfo: true }), - // DataTable.StaticTextColumn('Description', { fieldName: 'description', colStyle: { width: '350px' }, displayInfo: false }), - // DataTable.StaticTextColumn('Role Code', { fieldName: 'roleCode', colStyle: { width: '180px' }, displayInfo: false }), - DataTable.DateColumn('Effective Date', 'effectiveDate', { colStyle: { width: '150px'}, displayInfo: true }), - // DataTable.DateColumn('Date Created', 'createdDtm'), - DataTable.DateColumn('Expiry Date', 'expiryDate', { colStyle: { width: '150px'}, displayInfo: true }), - DataTable.SelectorFieldColumn('Status', { colStyle: { width: '175px' }, displayInfo: true }), - DataTable.StaticTextColumn('Assigned By', { fieldName: 'createdBy', colStyle: { width: '200px' }, displayInfo: false }), - DataTable.StaticDateColumn('Date Assigned', { fieldName: 'createdDtm', colStyle: { width: '200px' }, displayInfo: false }), + DataTable.SelectorFieldColumn('Assigned Role', { fieldName: 'roleId', colStyle: { width: '18%' }, selectorComponent: RoleSelector, displayInfo: true }), + DataTable.DateColumn('Effective Date', 'effectiveDate', { colStyle: { width: '15%'}, displayInfo: true }), + DataTable.DateColumn('Expiry Date', 'expiryDate', { colStyle: { width: '15%'}, displayInfo: true }), + DataTable.StaticTextColumn('Assigned By', { fieldName: 'createdBy', colStyle: { width: '15%' }, displayInfo: false }), + DataTable.StaticDateColumn('Date Assigned', { fieldName: 'createdDtm', colStyle: { width: '15%' }, displayInfo: false }), ]} expandable={false} - rowComponent={EmptyDetailRow} shouldDisableRow={() => parentModel.systemAccountInd === 1} + shouldMarkRowAsDeleted={(model) => { + return model.isExpired; + }} + rowComponent={EmptyDetailRow} initialValue={{ userId: parentModelId }} @@ -334,15 +343,15 @@ export default class AdminUsers extends FormContainerBase { }; const userActions = [ - ({ fields, index, model }) => { + /* ({ fields, index, model }) => { return (model && model.id && model.id !== '') ? ( - ) : null; - }, + }, */ ({ fields, index, model }) => { return (model && model.id) ? ( @@ -480,14 +489,17 @@ export default class AdminUsers extends FormContainerBase { }), */ // DataTable.DateColumn('Date Created', 'createdDtm'), // DataTable.SelectorFieldColumn('Status', { displayInfo: true }), // No point really in setting the status here - ]} filterable={displayFilters} expandable={true} // expandedRows={[1, 2]} + shouldMarkRowAsDeleted={(model) => { + return model.isExpired; + }} rowComponent={this.renderDetail()} modalComponent={EmptyDetailRow} shouldSortBy={shouldSortByFunc} + /> ); @@ -604,31 +616,59 @@ export default class AdminUsers extends FormContainerBase { mapExpiredFromFormValues(map: any, isExpired?: boolean) { isExpired = isExpired || false; - const expiredCourtroomIds: IdType[] = []; + const expiredUserIds: IdType[] = []; - if (map.courtrooms) { - const values = map.courtrooms.values; + if (map.users) { + const values = map.users.values; - const courtroomIds = values + const userIds = values .filter((val: any) => val.isExpired === isExpired) .map((val: any) => val.id); - expiredCourtroomIds.push(...courtroomIds); + expiredUserIds.push(...userIds); } + const expiredUserRoleIds: IdType[] = []; + + if (map.userRolesGrouped) { + const values = map.userRolesGrouped.values; + + const expireUserRoleIds = Object.keys(values).reduce((acc: any, cur: any) => { + const userRoleIds = values[cur] + .filter((val: any) => val.isExpired === isExpired) + .map((val: any) => val.id); + + return acc.concat(userRoleIds); + }, []); + + expiredUserRoleIds.push(...expireUserRoleIds); + } + + console.log('expired user role ids'); + console.log(expiredUserRoleIds); + return { - courtrooms: expiredCourtroomIds + users: expiredUserIds, + userRoles: expiredUserRoleIds }; } async onSubmit(formValues: any, initialValues: any, dispatch: Dispatch) { const data: any = this.getDataFromFormValues(formValues, initialValues) || {}; + const dataToExpire: any = this.getDataToExpireFromFormValues(formValues, initialValues, true) || {}; + const dataToUnexpire: any = this.getDataToExpireFromFormValues(formValues, initialValues, false) || {}; const dataToDelete: any = this.getDataToDeleteFromFormValues(formValues, initialValues) || {}; // Delete records before saving new ones! const deletedUsers: IdType[] = dataToDelete.users as IdType[]; const deletedUserRoles: IdType[] = dataToDelete.userRoles as IdType[]; + // Expire records before saving new ones! + const expiredUsers: IdType[] = dataToExpire.users as IdType[]; + const expiredUserRoles: IdType[] = dataToExpire.userRoles as IdType[]; + const unexpiredUsers: IdType[] = dataToUnexpire.users as IdType[]; + const unexpiredUserRoles: IdType[] = dataToUnexpire.userRoles as IdType[]; + const users: Partial[] = (data.users) ? data.users.map((u: User) => ({ ...u, // TODO: Need a way to set this stuff... createdBy, updated by fields should really be set in the backend using the current user @@ -666,12 +706,24 @@ export default class AdminUsers extends FormContainerBase { await dispatch(deleteUsers(deletedUsers)); } - if (deletedUserRoles.length > 0) { - // await dispatch(deleteUserRoles(deletedUserRoles)); + if (expiredUsers.length > 0) { + await dispatch(expireUsers(expiredUsers)); + } + + if (unexpiredUsers.length > 0) { + await dispatch(unexpireUsers(unexpiredUsers)); } if (deletedUserRoles.length > 0) { - await dispatch(expireUserRoles(deletedUserRoles)); + await dispatch(deleteUserRoles(deletedUserRoles)); + } + + if (expiredUserRoles.length > 0) { + await dispatch(expireUserRoles(expiredUserRoles)); + } + + if (unexpiredUserRoles.length > 0) { + await dispatch(unexpireUserRoles(unexpiredUserRoles)); } // We don't update users here, unless we're deleting them... diff --git a/yarn.lock b/yarn.lock index 20fd757b..714e4aa9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17,11 +17,6 @@ dependencies: regenerator-runtime "^0.13.2" -"@koa/multer@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@koa/multer/-/multer-2.0.2.tgz#2bb5a5c5fa22329a174f6a2afb2d0710ef16a16f" - integrity sha512-cWZUbpRNcLjLWDQ6QTWMX3ucXS+xHpJh7ngrBnsywhL7XygU5M090dDeiC9Sq1jM7IwKn9WkFZA1LyPU0q89qA== - "@snyk/cli-interface@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@snyk/cli-interface/-/cli-interface-1.5.0.tgz#b9dbe6ebfb86e67ffabf29d4e0d28a52670ac456" @@ -756,11 +751,6 @@ anymatch@^1.3.0: micromatch "^2.1.5" normalize-path "^2.0.0" -append-field@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" - integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= - append-transform@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" @@ -2255,14 +2245,6 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" -busboy@^0.2.11: - version "0.2.14" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" - integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM= - dependencies: - dicer "0.2.5" - readable-stream "1.1.x" - bytes@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.1.0.tgz#ac93c410e2ffc9cc7cf4b464b38289067f5e47b4" @@ -2760,16 +2742,6 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - configstore@^3.0.0, configstore@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" @@ -3472,14 +3444,6 @@ detect-port-alt@1.1.3: address "^1.0.1" debug "^2.6.0" -dicer@0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" - integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8= - dependencies: - readable-stream "1.1.x" - streamsearch "0.1.2" - diff@^3.2.0: version "3.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" @@ -4983,13 +4947,14 @@ handlebars@^4.0.1, handlebars@^4.0.3: uglify-js "^2.6" handlebars@^4.5.3: - version "4.7.3" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.3.tgz#8ece2797826886cf8082d1726ff21d2a022550ee" - integrity sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg== + version "4.7.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" + integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== dependencies: + minimist "^1.2.5" neo-async "^2.6.0" - optimist "^0.6.1" source-map "^0.6.1" + wordwrap "^1.0.0" optionalDependencies: uglify-js "^3.1.4" @@ -6024,9 +5989,8 @@ istanbul@^0.4.5: "jag-shuber-api@github:bcgov/jag-shuber-api": version "2.0.0" - resolved "https://codeload.github.com/bcgov/jag-shuber-api/tar.gz/3dd94b187096028300dbf065df29a7dfe61c1e85" + resolved "https://codeload.github.com/bcgov/jag-shuber-api/tar.gz/d296fbaefd083ac6ceda8542b9dc7b40cffd4112" dependencies: - "@koa/multer" "^2.0.2" jsonwebtoken "^8.3.0" jwt-decode "^2.2.0" koa "^2.5.0" @@ -6035,7 +5999,6 @@ istanbul@^0.4.5: koa-router "^7.4.0" moment "^2.22.1" moment-timezone "^0.5.17" - multer "^1.4.2" pg "^7.4.1" squel "^5.12.1" superagent "^3.8.3" @@ -7163,6 +7126,11 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" @@ -7267,20 +7235,6 @@ ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" -multer@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.2.tgz#2f1f4d12dbaeeba74cb37e623f234bf4d3d2057a" - integrity sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg== - dependencies: - append-field "^1.0.0" - busboy "^0.2.11" - concat-stream "^1.5.2" - mkdirp "^0.5.1" - object-assign "^4.1.1" - on-finished "^2.3.0" - type-is "^1.6.4" - xtend "^4.0.0" - multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" @@ -7764,9 +7718,9 @@ p-limit@^1.1.0: resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" p-limit@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" @@ -10570,11 +10524,6 @@ stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" -streamsearch@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= - strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -11147,7 +11096,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-is@^1.6.16, type-is@^1.6.4, type-is@~1.6.6: +type-is@^1.6.16, type-is@~1.6.6: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==