From 7e7f814dbe25fe26c35537b65a37d23c404ef6fe Mon Sep 17 00:00:00 2001 From: nidhigarg-bmw <101316912+nidhigarg-bmw@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:02:30 +0530 Subject: [PATCH] Feature/cplp 3120/user bulk upload (#214) --- DEPENDENCIES | 4 +- package.json | 4 +- public/user-bulk-load.csv | 3 + src/assets/locales/de/main.json | 41 +- src/assets/locales/en/main.json | 41 +- .../AddMultipleUser/AddMultipleUser.scss | 141 ++++++ .../overlays/AddMultipleUser/index.tsx | 440 ++++++++++++++++++ .../UserManagement/ActiveUserTable/index.tsx | 6 + .../shared/frame/UserList/index.tsx | 6 + .../appManagement/userManagementApiSlice.ts | 57 +++ src/features/store.ts | 3 + src/services/AccessService.tsx | 3 + src/types/Config.tsx | 4 + src/types/Constants.ts | 1 + yarn.lock | 20 +- 15 files changed, 766 insertions(+), 8 deletions(-) create mode 100644 public/user-bulk-load.csv create mode 100644 src/components/overlays/AddMultipleUser/AddMultipleUser.scss create mode 100644 src/components/overlays/AddMultipleUser/index.tsx create mode 100644 src/features/appManagement/userManagementApiSlice.ts diff --git a/DEPENDENCIES b/DEPENDENCIES index d5a86e858..bbf0a7a55 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -604,6 +604,7 @@ npm/npmjs/-/p-locate/4.1.0, MIT, approved, clearlydefined npm/npmjs/-/p-locate/5.0.0, MIT, approved, clearlydefined npm/npmjs/-/p-retry/4.6.2, MIT, approved, clearlydefined npm/npmjs/-/p-try/2.2.0, MIT, approved, clearlydefined +npm/npmjs/-/papaparse/5.4.1, MIT, approved, clearlydefined npm/npmjs/-/param-case/3.0.4, MIT, approved, clearlydefined npm/npmjs/-/parent-module/1.0.1, MIT, approved, clearlydefined npm/npmjs/-/parse-json/5.2.0, MIT, approved, clearlydefined @@ -1123,7 +1124,7 @@ npm/npmjs/@babel/template/7.22.5, MIT, approved, #9017 npm/npmjs/@babel/traverse/7.22.5, MIT, approved, #8954 npm/npmjs/@babel/types/7.22.5, MIT, approved, #8967 npm/npmjs/@bcoe/v8-coverage/0.2.3, ISC AND MIT, approved, clearlydefined -npm/npmjs/@catena-x/portal-shared-components/2.0.19, Apache-2.0 AND (BSD-3-Clause AND MIT), approved, #9187 +npm/npmjs/@catena-x/portal-shared-components/2.0.22, Apache-2.0 AND (BSD-3-Clause AND MIT), approved, #9187 npm/npmjs/@csstools/normalize.css/12.0.0, CC0-1.0, approved, clearlydefined npm/npmjs/@csstools/postcss-cascade-layers/1.1.1, CC0-1.0, approved, clearlydefined npm/npmjs/@csstools/postcss-color-function/1.1.1, CC0-1.0 AND (MIT AND W3C-20150513) AND W3C-20150513 AND MIT, approved, #3022 @@ -1281,6 +1282,7 @@ npm/npmjs/@types/mime/1.3.2, MIT, approved, clearlydefined npm/npmjs/@types/mime/3.0.1, MIT, approved, clearlydefined npm/npmjs/@types/node/16.18.36, MIT, approved, #7082 npm/npmjs/@types/node/20.3.1, MIT, approved, clearlydefined +npm/npmjs/@types/papaparse/5.3.7, MIT, approved, clearlydefined npm/npmjs/@types/parse-json/4.0.0, MIT, approved, clearlydefined npm/npmjs/@types/prettier/2.7.3, MIT, approved, #9030 npm/npmjs/@types/prop-types/15.7.5, MIT, approved, clearlydefined diff --git a/package.json b/package.json index de33f4905..4ea021a1b 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ ] }, "dependencies": { - "@catena-x/portal-shared-components": "^2.0.19", + "@catena-x/portal-shared-components": "^2.0.22", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@hookform/error-message": "^2.0.1", @@ -34,6 +34,7 @@ "@mui/material": "^5.13.4", "@react-hook/cache": "^1.1.1", "@reduxjs/toolkit": "^1.9.5", + "@types/papaparse": "^5.3.7", "axios": "^1.4.0", "buffer": "^6.0.3", "dayjs": "^1.11.8", @@ -43,6 +44,7 @@ "lodash.debounce": "^4.0.8", "lodash.uniq": "^4.5.0", "nanoid": "^4.0.2", + "papaparse": "^5.4.1", "qs": "^6.11.2", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/public/user-bulk-load.csv b/public/user-bulk-load.csv new file mode 100644 index 000000000..918634afc --- /dev/null +++ b/public/user-bulk-load.csv @@ -0,0 +1,3 @@ +FirstName,LastName,Email +Clark1,Kent1,ckent1@yahoo.de +Clark2,Kent2,ckent2@yahoo.de diff --git a/src/assets/locales/de/main.json b/src/assets/locales/de/main.json index 99484d969..dd5e78335 100644 --- a/src/assets/locales/de/main.json +++ b/src/assets/locales/de/main.json @@ -638,6 +638,7 @@ "title": "User Liste", "details": "Details", "add": "User hinzufügen", + "addMultiple": "Add Multiple users", "noRowsMsg": "No unassigned user available. All users have at least one app role assigned." }, "appUserDetails": { @@ -666,6 +667,43 @@ "subtitle": "The user cannot enter his account until he has set a new password." } }, + "addMultipleUsers": { + "heading": "Add user account by bulk upload", + "intro": "Bulk upload process", + "step1": { + "heading": "Download this upload template by clicking the icon", + "buttonLabel": "Download template" + }, + "step2": { + "heading": "Enter user data in upload form", + "linkText": "Guide on how to fill the template" + }, + "step3": { + "heading": "Upload form by selecting the form file with the user data" + }, + "fileSizeError": "Uploaded file is too big. Maximum 100KB is allowed", + "uploadedFile": { + "fileHeading": "Prepared for upload:", + "addUserRolesHeading": "Add user roles for all users in bulk upload:", + "usersToBeUpload": "users to be uploaded", + "note": "Note:", + "note1": "Only assign roles that apply to all users in the bulk upload file - individual roles can be applied individually later.", + "note2": "Roles for applications can be assigned after the user(s) have been successfully created.", + "note3": "We suggest to assign CX User initially to all bulk uploaded users and set specific user permissions separately.", + "availableRoles": "Available Roles: ", + "roleDesc": "Role Descriptions" + }, + "success": { + "userUploaded": "Users successfully uploaded", + "userFailed": "User uploads failed", + "errorUsersLabel": "Errors have occured for the following usernames:
Please check their entries in the upload file and try upload it again." + }, + "error": { + "note": "Note:", + "description": "The upload failed due to technical issue. Please try it later once again or contact your administrator.", + "details": "Details: " + } + }, "technicalUser": { "serviceaccount": "Technischer Benutzer", "create": "Technischen Nutzer anlegen", @@ -1652,7 +1690,8 @@ "next": "Next", "success": "Success", "error": "Error", - "retry": "Retry" + "retry": "Retry", + "exit": "exit" }, "state": { "enabled": "aktiv", diff --git a/src/assets/locales/en/main.json b/src/assets/locales/en/main.json index 6e4d6daff..1a88d5c16 100644 --- a/src/assets/locales/en/main.json +++ b/src/assets/locales/en/main.json @@ -637,6 +637,7 @@ "title": "User List", "details": "Details", "add": "Add user", + "addMultiple": "Add Multiple users", "noRowsMsg": "No unassigned user available. All users have at least one app role assigned." }, "appUserDetails": { @@ -665,6 +666,43 @@ "subtitle": "The user cannot enter his account until he has set a new password." } }, + "addMultipleUsers": { + "heading": "Add user account by bulk upload", + "intro": "Bulk upload process", + "step1": { + "heading": "Download this upload template by clicking the icon", + "buttonLabel": "Download template" + }, + "step2": { + "heading": "Enter user data in upload form", + "linkText": "Guide on how to fill the template" + }, + "step3": { + "heading": "Upload form by selecting the form file with the user data" + }, + "fileSizeError": "Uploaded file is too big. Maximum 100KB is allowed", + "uploadedFile": { + "fileHeading": "Prepared for upload:", + "addUserRolesHeading": "Add user roles for all users in bulk upload:", + "usersToBeUpload": "users to be uploaded", + "note": "Note:", + "note1": "Only assign roles that apply to all users in the bulk upload file - individual roles can be applied individually later.", + "note2": "Roles for applications can be assigned after the user(s) have been successfully created.", + "note3": "We suggest to assign CX User initially to all bulk uploaded users and set specific user permissions separately.", + "availableRoles": "Available Roles: ", + "roleDesc": "Role Descriptions" + }, + "success": { + "userUploaded": "Users successfully uploaded", + "userFailed": "User uploads failed", + "errorUsersLabel": "Errors have occured for the following usernames:
Please check their entries in the upload file and try upload it again." + }, + "error": { + "note": "Note:", + "description": "The upload failed due to technical issue. Please try it later once again or contact your administrator.", + "details": "Details: " + } + }, "technicalUser": { "serviceaccount": "Service Account", "create": "Create technical user", @@ -1615,7 +1653,8 @@ "next": "Next", "success": "Success", "error": "Error", - "retry": "Retry" + "retry": "Retry", + "exit": "exit" }, "state": { "enabled": "enabled", diff --git a/src/components/overlays/AddMultipleUser/AddMultipleUser.scss b/src/components/overlays/AddMultipleUser/AddMultipleUser.scss new file mode 100644 index 000000000..0f8d6363c --- /dev/null +++ b/src/components/overlays/AddMultipleUser/AddMultipleUser.scss @@ -0,0 +1,141 @@ +.mb-20 { + margin-bottom: 20px !important; +} + +.addMultipleUsers { + text-align: center !important; + margin: 0 auto; + + .number { + border: 1px solid #0f71cb; + color: #0f71cb; + border-radius: 50%; + display: inline-block; + width: 20px; + height: 20px; + line-height: 20px; + text-align: center; + font-size: 15px; + margin-bottom: 10px; + } + + .secondStep { + margin: 30px; + + .linkText { + width: 100%; + display: inline-block; + } + + .editIcon { + color: #0f71cb; + cursor: pointer; + } + } +} + +.uploadedFileMain { + .fileDetailsSection { + display: flex; + + .documentMain { + display: flex; + width: 50%; + + .documentIcon { + color: #0f71cb; + background-color: #dbe9f7; + height: 50px; + width: 50px; + border-radius: 5px; + + .icon { + margin: 14px; + } + .number { + text-align: center; + line-height: 50px; + } + } + + .documentName { + margin: 10px 20px; + } + } + } + + .roleDescLink { + display: flex; + font-size: 14px; + text-decoration: none; + } + + .rolesSection { + display: grid; + grid-template-columns: repeat(2, 1fr); + } +} + +.successSection { + .uploadedDetailsSection { + display: flex; + + .userDetailsMain { + display: flex; + width: 50%; + + .userSuccess { + color: #5bc785; + background-color: #f0f5d5; + height: 50px; + width: 50px; + border-radius: 5px; + } + + .userError { + color: #e62722; + background-color: #fee7e2; + height: 50px; + width: 50px; + border-radius: 5px; + } + + .number { + text-align: center; + line-height: 50px; + } + + .detailLabel { + margin: 10px 20px; + } + } + } + + .errorUsersLabel { + color: #da241e; + margin-left: 10px; + } + + table { + max-height: 225px; + overflow: auto; + display: block; + + tbody { + display: table; + width: 100%; + } + } + + /* scrollbar width */ + ::-webkit-scrollbar { + width: 5px; + height: 10px; + } + + /* scrollbar Handle */ + ::-webkit-scrollbar-thumb { + background: #4d73d5; + border-radius: 10px; + } +} diff --git a/src/components/overlays/AddMultipleUser/index.tsx b/src/components/overlays/AddMultipleUser/index.tsx new file mode 100644 index 000000000..d68252550 --- /dev/null +++ b/src/components/overlays/AddMultipleUser/index.tsx @@ -0,0 +1,440 @@ +/******************************************************************************** + * Copyright (c) 2021, 2023 BMW Group AG + * Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +import { useEffect, useState } from 'react' +import { Link } from 'react-router-dom' +import { useDispatch, useSelector } from 'react-redux' +import { store } from 'features/store' +import { Typography } from '@mui/material' +import { Trans, useTranslation } from 'react-i18next' +import { + Button, + DialogActions, + DialogContent, + DialogHeader, + DropArea, + DropAreaProps, + Checkbox, + Alert, + LoadingButton, + TableType, + StaticTable, +} from '@catena-x/portal-shared-components' +import EditIcon from '@mui/icons-material/Edit' +import ArticleOutlinedIcon from '@mui/icons-material/ArticleOutlined' +import ChevronRightIcon from '@mui/icons-material/ChevronRight' +import { OVERLAYS, PAGES } from 'types/Constants' +import { show } from 'features/control/overlay' +import { Dropzone } from '../../shared/basic/Dropzone' +import { rolesToAddSelector } from 'features/admin/userDeprecated/slice' +import { + AppRole, + useFetchCoreoffersRolesQuery, +} from 'features/admin/appuserApiSlice' +import { setRolesToAdd } from 'features/admin/userDeprecated/actions' +import { + MultipleUsersResponse, + useAddMutipleUsersMutation, +} from 'features/appManagement/userManagementApiSlice' +import { + useFetchIDPListQuery, + IdentityProvider, +} from 'features/admin/idpApiSlice' +import './AddMultipleUser.scss' +import Papa from 'papaparse' +import { AddUserDeny } from '../AddUser/AddUserDeny' + +const HelpPageURL = + '/documentation/?path=docs%2F03.+User+Management%2F01.+User+Account%2F04.+Create+new+user+account+%28bulk%29.md' + +export default function AddMultipleUser() { + const dispatch = useDispatch() + const { t } = useTranslation() + + const roles = useSelector(rolesToAddSelector) + + const { data } = useFetchCoreoffersRolesQuery() + const [addMutipleUsers] = useAddMutipleUsersMutation() + const { data: idpsData } = useFetchIDPListQuery() + + const [loading, setLoading] = useState(false) + const [allRoles, setAllRoles] = useState([]) + const [uploadedFile, setUploadedFile] = useState() + const [isFileUploaded, setIsFileUploaded] = useState(false) + const [totalRowsInCSV, setTotalRowsInCSV] = useState(0) + const [isSuccess, setIsSuccess] = useState(false) + const [isError, setIsError] = useState('') + const [idps, setIdps] = useState([]) + const [uploadAPIRResponse, setUploadAPIRResponse] = + useState() + const [tableErrorData, setTableErrorData] = useState() + + useEffect(() => { + const rolesArray: AppRole[] = [] + data?.map((a) => rolesArray.push(...a.roles)) + setAllRoles(rolesArray) + dispatch(setRolesToAdd([])) + }, [data, dispatch]) + + useEffect( + () => + setIdps( + idpsData ? idpsData.filter((idp: IdentityProvider) => idp.enabled) : [] + ), + [idpsData] + ) + + useEffect(() => { + uploadAPIRResponse && + setTableErrorData({ + head: [''], + body: [uploadAPIRResponse.errors], + }) + }, [uploadAPIRResponse]) + + const handleSelectRole = (role: string, select: boolean) => { + const isSelected = roles.includes(role) + if (!isSelected && select) { + dispatch(setRolesToAdd([...roles, role])) + } else if (isSelected && !select) { + const oldRoles = [...roles] + oldRoles.splice(oldRoles.indexOf(role), 1) + dispatch(setRolesToAdd([...oldRoles])) + } + } + + const renderDropArea = (props: DropAreaProps) => { + return + } + + const handleAddUserAPICall = async (csvData: any) => { + try { + if (uploadedFile) { + let blob = new Blob([Papa.unparse(csvData)], { type: 'text/csv' }) + let file = new File([blob], uploadedFile.name, { type: 'text/csv' }) + const response = await addMutipleUsers({ + identityProviderId: idps[0].identityProviderId, + csvFile: file, + }).unwrap() + setLoading(false) + setIsSuccess(true) + setUploadAPIRResponse(response) + } + } catch (error: any) { + setLoading(false) + setIsError(error.data.errors.document[0]) + console.log(error) + } + } + + const handleConfirm = async () => { + if (isSuccess || isError) dispatch(show(OVERLAYS.NONE, '')) + if (uploadedFile && !roles.length) setIsFileUploaded(true) + else if (uploadedFile && roles.length) { + setLoading(true) + uploadedFile && + Papa.parse(uploadedFile, { + skipEmptyLines: true, + complete: async (results) => { + const csvData: any = results.data + csvData[0].push('Roles') + for (let i = 0; i < csvData.length; i++) { + if (i !== 0) csvData[i].push(roles.toString()) + } + handleAddUserAPICall(csvData) + }, + }) + } + } + + const onChangeFile = async (selectedFile: File) => { + setUploadedFile(selectedFile) + + Papa.parse(selectedFile, { + skipEmptyLines: true, + complete: function (results) { + const csvData: any = results.data + setTotalRowsInCSV(csvData.length - 1) + }, + }) + } + + const renderContent = () => { + if (isError) { + return ( +
+ + {t('content.usermanagement.addMultipleUsers.error.note')} + + + {t('content.usermanagement.addMultipleUsers.error.description')} + + + {t('content.usermanagement.addMultipleUsers.error.details')} + + + {isError} + +
+ ) + } else if (isSuccess) { + return ( +
+
+
+
+ + {uploadAPIRResponse?.created} + +
+ + {t( + 'content.usermanagement.addMultipleUsers.success.userUploaded' + )} + +
+
+
+ + {uploadAPIRResponse?.error} + +
+ + {t( + 'content.usermanagement.addMultipleUsers.success.userFailed' + )} + +
+
+
+ + + {t( + 'content.usermanagement.addMultipleUsers.success.errorUsersLabel' + )} + + +
+ {tableErrorData && ( + + )} +
+ ) + } else if (isFileUploaded) { + return ( +
+ + {t( + 'content.usermanagement.addMultipleUsers.uploadedFile.fileHeading' + )} + +
+
+
+ +
+ + {uploadedFile?.name} + +
+
+
+ + {totalRowsInCSV} + +
+ + {t( + 'content.usermanagement.addMultipleUsers.uploadedFile.usersToBeUpload' + )} + +
+
+ + {t( + 'content.usermanagement.addMultipleUsers.uploadedFile.addUserRolesHeading' + )} + + + {t('content.usermanagement.addMultipleUsers.uploadedFile.note')} + +
    +
  • + + {t( + 'content.usermanagement.addMultipleUsers.uploadedFile.note1' + )} + +
  • +
  • + + {t( + 'content.usermanagement.addMultipleUsers.uploadedFile.note2' + )} + +
  • +
  • + + {t( + 'content.usermanagement.addMultipleUsers.uploadedFile.note3' + )} + +
  • +
+ + {t( + 'content.usermanagement.addMultipleUsers.uploadedFile.availableRoles' + )} + + + + {t('content.usermanagement.addMultipleUsers.uploadedFile.roleDesc')} + + {allRoles.length > 0 ? ( +
+ {allRoles.map((role: AppRole) => ( + + handleSelectRole(role.role, e.target.checked) + } + /> + ))} +
+ ) : ( + + {t('content.addUserRight.noRolesFound')} + + )} +
+ ) + } else { + return idps.length === 1 ? ( +
+
+ + 1 + + + {t('content.usermanagement.addMultipleUsers.step1.heading')} + + + + +
+
+ + 2 + + + {t('content.usermanagement.addMultipleUsers.step2.heading')} + + + + {t('content.usermanagement.addMultipleUsers.step2.linkText')} + + + +
+
+ + 3 + + + {t('content.usermanagement.addMultipleUsers.step3.heading')} + + { + file && onChangeFile(file) + }} + errorText={t( + 'content.usermanagement.addMultipleUsers.fileSizeError' + )} + DropStatusHeader={false} + DropArea={renderDropArea} + /> +
+
+ ) : ( + + ) + } + } + + return ( + <> + dispatch(show(OVERLAYS.NONE, '')), + }} + /> + + + {renderContent()} + + + + + {loading ? ( + {}} + sx={{ marginLeft: '10px' }} + /> + ) : ( + + )} + + + ) +} diff --git a/src/components/pages/UserManagement/ActiveUserTable/index.tsx b/src/components/pages/UserManagement/ActiveUserTable/index.tsx index 1034844ef..03d97d236 100644 --- a/src/components/pages/UserManagement/ActiveUserTable/index.tsx +++ b/src/components/pages/UserManagement/ActiveUserTable/index.tsx @@ -48,6 +48,12 @@ export const ActiveUserTable = ({ : '' } addButtonClick={() => dispatch(show(OVERLAYS.ADD_USER))} + addMultipleButtonLabel={ + UserService.hasRole(ROLES.USERMANAGEMENT_ADD) + ? 'content.usermanagement.table.addMultiple' + : '' + } + onMultipleButtonClick={() => dispatch(show(OVERLAYS.ADD_MULTIPLE_USER))} tableLabel={'content.usermanagement.table.title'} fetchHook={useFetchUsersSearchQuery} fetchHookArgs={{ expr, addUserResponse }} diff --git a/src/components/shared/frame/UserList/index.tsx b/src/components/shared/frame/UserList/index.tsx index f2bb7dd99..4e7c916ca 100644 --- a/src/components/shared/frame/UserList/index.tsx +++ b/src/components/shared/frame/UserList/index.tsx @@ -38,6 +38,8 @@ export const UserList = ({ sectionTitle, addButtonLabel, addButtonClick, + addMultipleButtonLabel, + onMultipleButtonClick, tableLabel, onDetailsClick, fetchHook, @@ -49,6 +51,8 @@ export const UserList = ({ sectionTitle: string addButtonLabel: string addButtonClick: () => void + addMultipleButtonLabel?: string + onMultipleButtonClick?: () => void tableLabel: string onDetailsClick: (row: TenantUser) => void fetchHook: (paginArgs: PaginFetchArgs) => any @@ -78,6 +82,8 @@ export const UserList = ({ onButtonClick={addButtonClick} buttonLabel={t(addButtonLabel)} + secondButtonLabel={addMultipleButtonLabel && t(addMultipleButtonLabel)} + onSecondButtonClick={onMultipleButtonClick} toolbarVariant="premium" searchPlaceholder={t('global.table.search')} columnHeadersBackgroundColor={'#FFFFFF'} diff --git a/src/features/appManagement/userManagementApiSlice.ts b/src/features/appManagement/userManagementApiSlice.ts new file mode 100644 index 000000000..a003316e4 --- /dev/null +++ b/src/features/appManagement/userManagementApiSlice.ts @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2021, 2023 BMW Group AG + * Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import { apiBaseQuery } from 'utils/rtkUtil' + +export type MultipleUsersRequest = { + identityProviderId: string + csvFile: File +} + +export type MultipleUsersResponse = { + created: number + error: number + errors: string[] + total: number +} + +export const apiSlice = createApi({ + reducerPath: 'rtk/apps/appManagement', + baseQuery: fetchBaseQuery(apiBaseQuery()), + endpoints: (builder) => ({ + addMutipleUsers: builder.mutation< + MultipleUsersResponse, + MultipleUsersRequest + >({ + query: (data: MultipleUsersRequest) => { + const formData = new FormData() + formData.append('document', data.csvFile) + return { + url: 'api/administration/user/owncompany/usersfile', + method: 'POST', + body: formData, + } + }, + }), + }), +}) + +export const { useAddMutipleUsersMutation } = apiSlice diff --git a/src/features/store.ts b/src/features/store.ts index 1f1444ce6..f6e1df903 100644 --- a/src/features/store.ts +++ b/src/features/store.ts @@ -63,6 +63,7 @@ import { apiSlice as serviceSubscriptionApiSlice } from './serviceSubscription/s import { apiSlice as serviceAdminBoardApiSlice } from './adminBoard/serviceAdminBoardApiSlice' import { apiSlice as companyRoleApiSlice } from './companyRoles/companyRoleApiSlice' import { apiSlice as certificationApiSlice } from './certification/certificationApiSlice' +import { apiSlice as userManagementApiSlice } from './appManagement/userManagementApiSlice' import languageSlice from './language/slice' import { apiSlice as usecaseApiSlice } from './usecase/usecaseApiSlice' @@ -114,6 +115,7 @@ export const reducers = { [serviceAdminBoardApiSlice.reducerPath]: serviceAdminBoardApiSlice.reducer, [companyRoleApiSlice.reducerPath]: companyRoleApiSlice.reducer, [certificationApiSlice.reducerPath]: certificationApiSlice.reducer, + [userManagementApiSlice.reducerPath]: userManagementApiSlice.reducer, [usecaseApiSlice.reducerPath]: usecaseApiSlice.reducer, } @@ -143,6 +145,7 @@ export const store = configureStore({ .concat(serviceAdminBoardApiSlice.middleware) .concat(companyRoleApiSlice.middleware) .concat(certificationApiSlice.middleware) + .concat(userManagementApiSlice.middleware) .concat(usecaseApiSlice.middleware), }) diff --git a/src/services/AccessService.tsx b/src/services/AccessService.tsx index 7954c5c0d..f6439a0e9 100644 --- a/src/services/AccessService.tsx +++ b/src/services/AccessService.tsx @@ -67,6 +67,7 @@ import AppDeclineAdminboard from 'components/overlays/DeclineAdminboard/AppDecli import UpdateCompanyRole from 'components/overlays/UpdateCompanyRole' import EditUsecase from 'components/overlays/EditUsecase' import UpdateCertificate from 'components/overlays/UpdateCertificate' +import AddMultipleUser from 'components/overlays/AddMultipleUser' let pageMap: { [page: string]: IPage } let actionMap: { [action: string]: IAction } @@ -119,6 +120,8 @@ export const getOverlay = (overlay: OverlayState) => { return null case OVERLAYS.ADD_USER: return + case OVERLAYS.ADD_MULTIPLE_USER: + return case OVERLAYS.USER: return case OVERLAYS.TECHUSER: diff --git a/src/types/Config.tsx b/src/types/Config.tsx index 87fdaa9b2..9a5a4815f 100644 --- a/src/types/Config.tsx +++ b/src/types/Config.tsx @@ -500,6 +500,10 @@ export const ALL_OVERLAYS: IOverlay[] = [ name: OVERLAYS.ADD_USER, role: ROLES.USERMANAGEMENT_ADD, }, + { + name: OVERLAYS.ADD_MULTIPLE_USER, + role: ROLES.USERMANAGEMENT_ADD, + }, { name: OVERLAYS.ADD_TECHUSER, role: ROLES.TECHUSER_ADD, diff --git a/src/types/Constants.ts b/src/types/Constants.ts index 684e4ab75..4b945f58f 100644 --- a/src/types/Constants.ts +++ b/src/types/Constants.ts @@ -103,6 +103,7 @@ export enum OVERLAYS { NOT_FOUND = 'notfound', NONE = 'none', ADD_USER = 'add_user', + ADD_MULTIPLE_USER = 'add_multiple_user', ADD_APP_USER_ROLES = 'add_app_user_roles', EDIT_APP_USER_ROLES = 'edit_app_user_roles', USER = 'user', diff --git a/yarn.lock b/yarn.lock index 0dfbabda0..0e2ede2c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1175,10 +1175,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@catena-x/portal-shared-components@^2.0.19": - version "2.0.19" - resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-2.0.19.tgz#c3ba91421181e7b791379e80cdaf082beb46d201" - integrity sha512-+WnZgUBYs487iCILfblZBb6G/bcrU2PqHH4D5MLx864oKKVUuE5pzYJtXuRe9sJYbK1MF6fq8lGmloclMjD5Jg== +"@catena-x/portal-shared-components@^2.0.22": + version "2.0.22" + resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-2.0.22.tgz#acfd9ed4b6c4d13c45d4ada3b8a2ca086fea7052" + integrity sha512-iKOxkfmsDK8yBjmwyKO0DqpRewBm5CVGSzhkC36zRuW9XZB6XBKuPcG49hCPkimVsR0zE91udID0QioAcHvGIA== dependencies: "@mui/base" "^5.0.0-beta.3" "@mui/system" "^5.13.2" @@ -2479,6 +2479,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.36.tgz#0db5d7efc4760d36d0d1d22c85d1a53accd5dc27" integrity sha512-8egDX8dE50XyXWH6C6PRCNkTP106DuUrvdrednFouDSmCi7IOvrqr0frznfZaHifHH/3aq/7a7v9N4wdXMqhBQ== +"@types/papaparse@^5.3.7": + version "5.3.7" + resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.3.7.tgz#8d3bf9e62ac2897df596f49d9ca59a15451aa247" + integrity sha512-f2HKmlnPdCvS0WI33WtCs5GD7X1cxzzS/aduaxSu3I7TbhWlENjSPs6z5TaB9K0J+BH1jbmqTaM+ja5puis4wg== + dependencies: + "@types/node" "*" + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -7445,6 +7452,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +papaparse@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.4.1.tgz#f45c0f871853578bd3a30f92d96fdcfb6ebea127" + integrity sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"