From 2abc066fed28fda847a1d823a723a283d6bdb7c7 Mon Sep 17 00:00:00 2001 From: Saad Bin Rafiq <149799297+saadanzari@users.noreply.github.com> Date: Tue, 27 Aug 2024 09:26:57 +0200 Subject: [PATCH 1/8] fix(business partner invite): updated name pattern to allow 2 characters (#1025) --- CHANGELOG.md | 2 ++ src/types/Patterns.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 473c78708..a84af5b5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - **Company data Management** - integrated /ready api to trigger once the new record is created [#958](https://github.com/eclipse-tractusx/portal-frontend/pull/958) - enabled displaying of sharing state error details in the company overlay details page [#958](https://github.com/eclipse-tractusx/portal-frontend/pull/958) +- **Business partner invite** + - updated first name and last name pattern to allow 2 characters in business partner invite application form [#1025](https://github.com/eclipse-tractusx/portal-frontend/pull/1025) ### Feature diff --git a/src/types/Patterns.ts b/src/types/Patterns.ts index 961b450d1..f6da89de1 100644 --- a/src/types/Patterns.ts +++ b/src/types/Patterns.ts @@ -46,7 +46,7 @@ export const Patterns = { EXTID: /^[a-z0-9]{6,36}$/i, COMPANY_NAME: /^\d*?[a-zÀ-ÿ]\d?([a-z0-9À-ÿ!?@&_£$€¥\-.,:;'()*+#%=]\s?){2,40}$/i, - name: /^([A-Za-zÀ-ÿ-,.'](?!.*[-,.]{2})[A-Za-zÀ-ÿ-,.']{1,40} ?)[^ –]{1,40}$/, + name: /^([A-Za-zÀ-ÿ-,.'](?!.*[-,.]{2})[A-Za-zÀ-ÿ-,.']{0,39} ?)[^ –]{1,40}$/, zipcode: /^[A-Z0-9-]{1,8}$/, streetNumber: /^[0-9A-Za-z- ]{1,20}$/, regionName: /^[0-9A-Za-z- ]{2,20}$/, From d189b17fcec2cc2f1947b9cc75a2351ea6b60882 Mon Sep 17 00:00:00 2001 From: Martin Rohrmeier Date: Tue, 27 Aug 2024 11:49:09 +0200 Subject: [PATCH 2/8] fix: person name pattern (#1040) --- src/types/Patterns.test.ts | 46 +++++++++++++++++++++++++++++++------- src/types/Patterns.ts | 13 ++++++++--- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/types/Patterns.test.ts b/src/types/Patterns.test.ts index 416edda81..602a1b53e 100644 --- a/src/types/Patterns.test.ts +++ b/src/types/Patterns.test.ts @@ -27,8 +27,8 @@ import { isUUID, isCName, isCountryCode, - isFirstName, isClientID, + isPersonName, } from './Patterns' const TESTDATA = { @@ -164,13 +164,43 @@ const TESTDATA = { valid: ['DE'], invalid: ['', 'D', 'de', 'some string'], }, - FIRSTNAME: { - valid: ['Julia Sophie', 'Julia-Sophie', 'Chloé', 'Paŭlo'], + PERSON_NAME: { + valid: [ + 'Franz Mustermann', + 'Li', + 'Bo Li', + 'Julia Sophie', + 'Julia-Sophie', + 'Dr. Müller', + 'Chloé', + 'Paŭlo', + 'Walther von der Vogelweide', + 'Willard Carroll Smith, Jr.', + 'John F. Kennedy', + 'Ursula K. Le Guin', + // prettier-ignore + 'Joseph O\'Neill', + // prettier-ignore + 'd\'Artagnan', + 'Sigríður', + 'Łukasz', + 'Božič', + 'Polývios', + // prettier-ignore + 'Muhammad bin Salmān Āl Su\'ūd', + ], invalid: [ 'Julia Sophie', 'Julia–Sophie', 'Julia Sophie ', ' Julia Sophie ', + 'William H Gates', + 'Prayut Chan-o-cha', + 'Meister. Eder', + '孔夫子', + 'แพทองธาร ชินวัตร', + 'مُحَمَّدْ بْنْ سَلْمَانْ آلْ سُعُودْ', + 'Muḥammad bin Salmān Āl Su‘ūd', ], }, CLIENTID: { @@ -251,12 +281,12 @@ describe('Input Pattern Tests', () => { expect(isCountryCode(expr)).toBe(false) }) }) - it('validates firstName', () => { - TESTDATA.FIRSTNAME.valid.forEach((expr) => { - expect(isFirstName(expr)).toBe(true) + it('validates person name', () => { + TESTDATA.PERSON_NAME.valid.forEach((expr) => { + expect(isPersonName(expr)).toBe(true) }) - TESTDATA.FIRSTNAME.invalid.forEach((expr) => { - expect(isFirstName(expr)).toBe(false) + TESTDATA.PERSON_NAME.invalid.forEach((expr) => { + expect(isPersonName(expr)).toBe(false) }) }) it('validate tech user clientId', () => { diff --git a/src/types/Patterns.ts b/src/types/Patterns.ts index f6da89de1..901796d4b 100644 --- a/src/types/Patterns.ts +++ b/src/types/Patterns.ts @@ -33,6 +33,11 @@ const urlPattern = new RegExp( 'i' ) const prefixUrlPattern = new RegExp(`^${urlProtocol}:`, 'i') +const nameGroup = 'A-Za-z\u00C0-\u017F' +const personNameToken = `([${nameGroup}]\\.|[${nameGroup}']{2,30})` +const personNamePattern = new RegExp( + `^([Dd]r\\.? )?${personNameToken}(( ?- ?| )${personNameToken}){0,16}(,? [JjSs](un|en|n?r)\\.?)?$` +) export const Patterns = { ID: /^[a-z0-9_.@-]{1,80}$/i, @@ -46,7 +51,8 @@ export const Patterns = { EXTID: /^[a-z0-9]{6,36}$/i, COMPANY_NAME: /^\d*?[a-zÀ-ÿ]\d?([a-z0-9À-ÿ!?@&_£$€¥\-.,:;'()*+#%=]\s?){2,40}$/i, - name: /^([A-Za-zÀ-ÿ-,.'](?!.*[-,.]{2})[A-Za-zÀ-ÿ-,.']{0,39} ?)[^ –]{1,40}$/, + personName: personNamePattern, + name: /^([A-Za-z\u00C0-\u017F-,.'](?!.*[-,.]{2})[A-Za-z\u00C0-\u017F-,.']{0,40} ?)[^ –]{1,40}$/, zipcode: /^[A-Z0-9-]{1,8}$/, streetNumber: /^[0-9A-Za-z- ]{1,20}$/, regionName: /^[0-9A-Za-z- ]{2,20}$/, @@ -136,8 +142,9 @@ export const isName = (expr: string) => Patterns.name.test(expr) export const isCityName = isName export const isStreetName = isName export const isRegionName = (expr: string) => Patterns.regionName.test(expr) -export const isFirstName = isName -export const isLastName = isName +export const isPersonName = (expr: string) => Patterns.personName.test(expr) +export const isFirstName = isPersonName +export const isLastName = isPersonName export const isUserName = (expr: string) => isName(expr) || isMail(expr) export const isZipCode = (expr: string) => Patterns.zipcode.test(expr) export const isStreetNumber = (expr: string) => Patterns.streetNumber.test(expr) From 89d2095b1e2d3a9e153d058f34ae4634c873f5d1 Mon Sep 17 00:00:00 2001 From: Manojava Koushik <111366021+manojava-gk@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:36:49 +0530 Subject: [PATCH 3/8] fix(partner network): interface change (#1002) --- CHANGELOG.md | 3 + DEPENDENCIES | 4 +- package.json | 2 +- .../BusinessPartnerDetail.tsx | 33 +++-- src/components/pages/PartnerNetwork/index.tsx | 127 +++++++++++++----- .../partnerNetworkApiSlice.ts | 41 +++--- yarn.lock | 8 +- 7 files changed, 155 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a84af5b5a..8b29fde92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Partner Network + - Change interface of the partner network apis. add /members to the POST api and update the business logic accordingly + ## 2.2.0-RC1 ### Change diff --git a/DEPENDENCIES b/DEPENDENCIES index 583db2173..b0f3c9654 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -601,7 +601,7 @@ npm/npmjs/@babel/template/7.24.0, MIT, approved, clearlydefined npm/npmjs/@babel/traverse/7.24.1, MIT AND (BSD-2-Clause AND ISC AND MIT) AND BSD-2-Clause AND BSD-3-Clause, approved, #13926 npm/npmjs/@babel/types/7.24.0, MIT, approved, clearlydefined npm/npmjs/@bcoe/v8-coverage/0.2.3, ISC AND MIT, approved, clearlydefined -npm/npmjs/@catena-x/portal-shared-components/3.0.29, Apache-2.0 AND CC-BY-4.0 AND OFL-1.1, approved, #14247 +npm/npmjs/@catena-x/portal-shared-components/3.2.0, Apache-2.0 AND CC-BY-4.0 AND OFL-1.1, approved, #16029 npm/npmjs/@cspotcode/source-map-support/0.8.1, MIT, approved, clearlydefined npm/npmjs/@date-io/core/3.0.0, MIT, approved, clearlydefined npm/npmjs/@date-io/date-fns/3.0.0, MIT, approved, #14023 @@ -684,7 +684,7 @@ npm/npmjs/@mui/material/5.15.15, MIT AND CC-BY-3.0, approved, #13175 npm/npmjs/@mui/private-theming/5.15.14, MIT AND CC-BY-3.0, approved, #13174 npm/npmjs/@mui/styled-engine/5.15.14, MIT AND CC-BY-3.0, approved, #13173 npm/npmjs/@mui/system/5.15.15, MIT, approved, #13170 -npm/npmjs/@mui/types/7.2.14, MIT, approved, clearlydefined +npm/npmjs/@mui/types/7.2.14, MIT, approved, #16017 npm/npmjs/@mui/utils/5.15.14, MIT AND OFL-1.1 AND CC-BY-3.0, approved, #13927 npm/npmjs/@mui/x-data-grid/6.19.11, MIT, approved, #14027 npm/npmjs/@mui/x-date-pickers/6.19.9, MIT, approved, #14025 diff --git a/package.json b/package.json index 65b0bdf4a..2bb1cd827 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ ] }, "dependencies": { - "@catena-x/portal-shared-components": "^3.0.29", + "@catena-x/portal-shared-components": "^3.1.3", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@hookform/error-message": "^2.0.1", diff --git a/src/components/pages/PartnerNetwork/components/BusinessPartnerDetailOverlay/BusinessPartnerDetail.tsx b/src/components/pages/PartnerNetwork/components/BusinessPartnerDetailOverlay/BusinessPartnerDetail.tsx index 03ab678f5..f1648f72c 100644 --- a/src/components/pages/PartnerNetwork/components/BusinessPartnerDetailOverlay/BusinessPartnerDetail.tsx +++ b/src/components/pages/PartnerNetwork/components/BusinessPartnerDetailOverlay/BusinessPartnerDetail.tsx @@ -18,21 +18,36 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { useFetchBusinessPartnersQuery } from 'features/newPartnerNetwork/partnerNetworkApiSlice' +import { useFetchBusinessPartnerAddressMutation } from 'features/newPartnerNetwork/partnerNetworkApiSlice' import BusinessPartnerDetailContent from './BusinessPartnerDetailContent' +import { useEffect, useState } from 'react' +import { type BusinessPartner } from 'features/partnerNetwork/types' const BusinessPartnerDetail = ({ id }: { id: string }) => { - const { data } = useFetchBusinessPartnersQuery({ - page: 0, - args: { - expr: id, - }, - }) + const [bpMutation] = useFetchBusinessPartnerAddressMutation() + const [items, setItems] = useState() + const callApi = async () => { + await bpMutation({ + bpnLs: [id], + legalName: '', + }) + .unwrap() + .then((data) => { + setItems(data.content) + }) + .catch(() => { + setItems([]) + }) + } + + useEffect(() => { + callApi() + }, []) return ( <> - {data?.content?.length && ( - + {items?.length && ( + )} ) diff --git a/src/components/pages/PartnerNetwork/index.tsx b/src/components/pages/PartnerNetwork/index.tsx index 3bc0ff62b..9e80d58ca 100644 --- a/src/components/pages/PartnerNetwork/index.tsx +++ b/src/components/pages/PartnerNetwork/index.tsx @@ -21,20 +21,20 @@ import 'components/pages/PartnerNetwork/PartnerNetwork.scss' import { useTranslation } from 'react-i18next' import { - useFetchBusinessPartnersQuery, + useFetchBusinessPartnersMutation, useFetchBusinessPartnerAddressMutation, } from 'features/newPartnerNetwork/partnerNetworkApiSlice' import { PageHeader, - PageLoadingTable, + Table, type PaginResult, } from '@catena-x/portal-shared-components' import { useSelector } from 'react-redux' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { updatePartnerSelector } from 'features/control/updates' import { PartnerNetworksTableColumns } from 'components/pages/PartnerNetwork/partnerNetworkTableColumns' import type { BusinessPartner } from 'features/newPartnerNetwork/types' -import Patterns from 'types/Patterns' +import Patterns, { isBPN } from 'types/Patterns' import { useFetchMemberCompaniesQuery } from 'features/newPartnerNetwork/partnerNetworkPortalApiSlice' import { isContentPresent, @@ -46,42 +46,94 @@ import { type BusinessPartnerAddressResponse } from 'features/partnerNetwork/typ const PartnerNetwork = () => { const { t } = useTranslation() const [expr, setExpr] = useState('') - const [refresh, setRefresh] = useState(0) + const [bpn, setBpn] = useState('') const searchInputData = useSelector(updatePartnerSelector) const columns = PartnerNetworksTableColumns(t) const [mutationRequest] = useFetchBusinessPartnerAddressMutation() const { data } = useFetchMemberCompaniesQuery() + const [fetchMembers] = useFetchBusinessPartnersMutation() const validateSearchText = (text: string): boolean => Patterns.SEARCH.test(text.trim()) + const [page, setPage] = useState(0) + const [allItems, setAllItems] = useState([]) + const [memberData, setMemberData] = useState>() + const [loading, setLoading] = useState(false) + const [fetchArgs, setFetchArgs] = useState({ + args: { + expr, + }, + page, + }) - const [allItems, setAllItems] = useState() + const fetchAllMembers = async () => { + await fetchMembers(fetchArgs) + .unwrap() + .then((payload) => { + setMemberData(payload) + if (isContentPresent(payload) && payload.content.length === 0) { + setAllItems([]) + setLoading(false) + } else { + setCountryAttributes(payload) + } + }) + .catch(() => { + setAllItems([]) + }) + setLoading(false) + } - const fetchAndApply = async (cData: PaginResult) => { - //BPDM response does not has content attribute. Check for it and proceed - if (isContentPresent(cData) && cData.content.length === 0) { - setAllItems([]) - return - } + useEffect(() => { + setLoading(true) + if (data && data.length > 0) fetchAllMembers() + }, [data, fetchArgs]) - const result = cData.content.map((x: BusinessPartner) => x.bpnl) + const setCountryAttributes = (payload: PaginResult) => { + let finalObj = JSON.parse(JSON.stringify(payload?.content)) + finalObj = addCountryAttribute( + finalObj, + payload.content as unknown as BusinessPartnerAddressResponse[] + ) + finalObj = addMemberAttribute(finalObj, data) + setAllItems((i) => (page === 0 ? finalObj : i.concat(finalObj))) + } + + const fetchAndApply = async (result: string[]) => { await mutationRequest({ bpnLs: result, legalName: '' }) .unwrap() - .then((payload: PaginResult) => { - //new country attribute && member attributes based on the response - let finalObj = JSON.parse(JSON.stringify(cData?.content)) - finalObj = addCountryAttribute( - finalObj, - payload.content as unknown as BusinessPartnerAddressResponse[] - ) - finalObj = addMemberAttribute(finalObj, data) - setAllItems(finalObj) + .then((payload) => { + setCountryAttributes(payload) }) .catch(() => { setAllItems([]) }) + + setLoading(false) } + useEffect(() => { + setFetchArgs({ + args: { + expr, + }, + page, + }) + }, [page, expr]) + + useEffect(() => { + if (bpn === '') { + setFetchArgs({ + args: { + expr: '', + }, + page: 0, + }) + } + setLoading(true) + fetchAndApply([bpn]) + }, [bpn]) + return (
{ />
- + { + if (expr === '') { + setBpn('') + setExpr('') + setPage(0) + } if (expr !== '' && !validateSearchText(expr)) return - setRefresh(Date.now()) - setExpr(expr) + setAllItems([]) + if (isBPN(expr)) setBpn(expr) + else setExpr(expr) }} searchDebounce={1000} title={t('content.partnernetwork.tabletitle')} loadLabel={t('global.actions.loadmore')} - fetchHook={useFetchBusinessPartnersQuery} - fetchHookArgs={{ expr }} - fetchHookRefresh={refresh} getRowId={(row: { bpnl: string }) => row.bpnl ?? ''} columns={columns} - callbackToPage={fetchAndApply} - allItems={allItems} + loading={loading} + rows={allItems} + rowsCount={allItems?.length} + noRowsMsg={t('content.companyData.table.noRowsMsg')} + nextPage={() => { + setPage(page + 1) + }} + hasMore={ + memberData?.meta && + memberData.meta.page < memberData.meta.totalPages - 1 + } + hideFooterPagination={true} /> diff --git a/src/features/newPartnerNetwork/partnerNetworkApiSlice.ts b/src/features/newPartnerNetwork/partnerNetworkApiSlice.ts index 5674967ed..06d94769a 100644 --- a/src/features/newPartnerNetwork/partnerNetworkApiSlice.ts +++ b/src/features/newPartnerNetwork/partnerNetworkApiSlice.ts @@ -25,44 +25,53 @@ import type { PaginFetchArgs, } from '@catena-x/portal-shared-components' import type { BusinessPartner } from './types' -import Patterns from 'types/Patterns' -import type { BusinessPartnerAddressResponse } from 'features/partnerNetwork/types' +import { isBPN } from 'types/Patterns' export interface BusinessPartnerRequest { bpnLs: string[] legalName: string } -const checkIfBPNLNumber = (text: string): boolean => - Patterns.BPN.test(text.trim()) - export const apiSlice = createApi({ reducerPath: 'rtk/admin/partnerNetwork', baseQuery: fetchBaseQuery(apiBpdmPoolQuery()), endpoints: (builder) => ({ fetchBusinessPartnerAddress: builder.mutation< - PaginResult, + PaginResult, BusinessPartnerRequest >({ query: (body) => ({ - url: '/legal-entities/search?page=0&size=30', + url: '/members/legal-entities/search?page=0&size=10', method: 'POST', body, }), }), - fetchBusinessPartners: builder.query< + fetchBusinessPartners: builder.mutation< PaginResult, PaginFetchArgs >({ query: (fetchArgs) => { - if (fetchArgs.args.expr && !checkIfBPNLNumber(fetchArgs.args.expr)) { - return `/legal-entities?page=${ - fetchArgs.page - }&size=10&legalName=${fetchArgs.args!.expr}` - } else if (checkIfBPNLNumber(fetchArgs.args.expr)) { - return `/legal-entities/${fetchArgs.args!.expr}` + let url = '' + let body = {} + if (fetchArgs.args.expr && !isBPN(fetchArgs.args.expr)) { + url = `/members/legal-entities/search?page=${fetchArgs.page}&size=10` + body = { + bpnl: [], + legalName: fetchArgs.args.expr, + } + } else if (isBPN(fetchArgs.args.expr)) { + url = `/members/legal-entities/search?page=${fetchArgs.page}&size=10` + body = { + bpnl: [fetchArgs.args.expr], + legalName: '', + } } else { - return `/legal-entities?page=${fetchArgs.page}&size=10` + url = `/members/legal-entities/search?page=${fetchArgs.page}&size=10` + } + return { + url, + method: 'POST', + body, } }, // Add an ESLint exception until there is a solution @@ -96,5 +105,5 @@ export const apiSlice = createApi({ export const { useFetchBusinessPartnerAddressMutation, - useFetchBusinessPartnersQuery, + useFetchBusinessPartnersMutation, } = apiSlice diff --git a/yarn.lock b/yarn.lock index d4f31f46f..fa3011273 100644 --- a/yarn.lock +++ b/yarn.lock @@ -329,10 +329,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@catena-x/portal-shared-components@^3.0.29": - version "3.0.29" - resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-3.0.29.tgz#85f83ce4a3d7b8d19c9719af9cd138a93070269a" - integrity sha512-VFzU+Krmt6doZWPLMn91FvqTXbF/bACKkdm7e+cAyGkiarTs4hL1iv4wjtgwSp/Uq0qZUKBL46N6Ouxyw5p3iQ== +"@catena-x/portal-shared-components@^3.1.3": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-3.2.0.tgz#dcf4b3c4b3c2576801d6c3d9cb5f85ce8a2635d6" + integrity sha512-uy2jgIcGBcbWsg0MBN44b3W0MM6ZXrdmN5YeObGq9PW6D4fiGJviCGbqw12u0Zm20dNgchoCquArkJaSnv1uTw== dependencies: "@date-io/date-fns" "^3.0.0" "@emotion/react" "^11.11.4" From 578365b4cd0e9939ed7cbd498e5d6845af8f3399 Mon Sep 17 00:00:00 2001 From: Manojava Koushik <111366021+manojava-gk@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:41:54 +0530 Subject: [PATCH 4/8] fix(service subscription): update auto setup api response data in the service subscription response overlay (#1020) --- CHANGELOG.md | 2 ++ .../overlays/ActivateServiceSubscription/index.tsx | 11 ++++++----- src/features/serviceManagement/apiSlice.ts | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b29fde92..1eb5ef6b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Service Subscription + - Update auto setup api response data in the service subscription response overlay - Partner Network - Change interface of the partner network apis. add /members to the POST api and update the business logic accordingly diff --git a/src/components/overlays/ActivateServiceSubscription/index.tsx b/src/components/overlays/ActivateServiceSubscription/index.tsx index 25cf7c580..bace44311 100644 --- a/src/components/overlays/ActivateServiceSubscription/index.tsx +++ b/src/components/overlays/ActivateServiceSubscription/index.tsx @@ -96,11 +96,11 @@ export default function ActivateserviceSubscription({ body: [ [ t('serviceSubscription.activation.userId'), - techUserInfo?.technicalUserInfo.technicalClientId ?? '', + techUserInfo?.technicalUserInfo[0]?.technicalClientId ?? '', ], [ t('serviceSubscription.activation.sercret'), - techUserInfo?.technicalUserInfo.technicalUserSecret ?? '', + techUserInfo?.technicalUserInfo[0]?.technicalUserSecret ?? '', ], [ t('serviceSubscription.activation.url'), @@ -108,8 +108,9 @@ export default function ActivateserviceSubscription({ ], [ t('serviceSubscription.activation.technicaluserType'), - techUserInfo?.technicalUserInfo.technicalUserPermissions.join(', ') ?? - '', + techUserInfo?.technicalUserInfo[0]?.technicalUserPermissions?.join( + ', ' + ) ?? '', ], ], edit: [ @@ -127,7 +128,7 @@ export default function ActivateserviceSubscription({ }, { icon: false, - copyValue: techUserInfo?.technicalUserInfo.technicalUserSecret, + copyValue: techUserInfo?.technicalUserInfo[0]?.technicalUserSecret, }, ], ], diff --git a/src/features/serviceManagement/apiSlice.ts b/src/features/serviceManagement/apiSlice.ts index 892ec739e..69a08758e 100644 --- a/src/features/serviceManagement/apiSlice.ts +++ b/src/features/serviceManagement/apiSlice.ts @@ -178,7 +178,7 @@ export type ActivateSubscriptionRequest = { } export type ActivateSubscriptionResponse = { - technicalUserInfo: TechnicalUserInfoType + technicalUserInfo: TechnicalUserInfoType[] clientInfo: ClientInfoType } From 83d193bc6cd278b7b12737745f9126bcc5fc36c3 Mon Sep 17 00:00:00 2001 From: Manojava Koushik <111366021+manojava-gk@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:52:48 +0530 Subject: [PATCH 5/8] fix(company data): crash and disable issue (#1048) --- CHANGELOG.md | 3 + DEPENDENCIES | 3 +- package.json | 4 +- .../pages/CompanyData/components/EditForm.tsx | 1 + .../CompanyData/components/FormFields.tsx | 9 +- .../CompanyData/components/SelectList.tsx | 121 ++++++++++++++++++ yarn.lock | 13 +- 7 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 src/components/pages/CompanyData/components/SelectList.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb5ef6b7..43cc1d0ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Company data + - Disable confirm button if input value is not matching with the regular expression + - Fix infinite loop issue in add New Address Modal - Service Subscription - Update auto setup api response data in the service subscription response overlay - Partner Network diff --git a/DEPENDENCIES b/DEPENDENCIES index b0f3c9654..57b4e27ec 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -601,7 +601,7 @@ npm/npmjs/@babel/template/7.24.0, MIT, approved, clearlydefined npm/npmjs/@babel/traverse/7.24.1, MIT AND (BSD-2-Clause AND ISC AND MIT) AND BSD-2-Clause AND BSD-3-Clause, approved, #13926 npm/npmjs/@babel/types/7.24.0, MIT, approved, clearlydefined npm/npmjs/@bcoe/v8-coverage/0.2.3, ISC AND MIT, approved, clearlydefined -npm/npmjs/@catena-x/portal-shared-components/3.2.0, Apache-2.0 AND CC-BY-4.0 AND OFL-1.1, approved, #16029 +npm/npmjs/@catena-x/portal-shared-components/3.4.0, Apache-2.0 AND CC-BY-4.0 AND OFL-1.1, approved, #16027 npm/npmjs/@cspotcode/source-map-support/0.8.1, MIT, approved, clearlydefined npm/npmjs/@date-io/core/3.0.0, MIT, approved, clearlydefined npm/npmjs/@date-io/date-fns/3.0.0, MIT, approved, #14023 @@ -738,6 +738,7 @@ npm/npmjs/@tsconfig/node12/1.0.11, MIT, approved, clearlydefined npm/npmjs/@tsconfig/node14/1.0.3, MIT, approved, clearlydefined npm/npmjs/@tsconfig/node16/1.0.4, MIT, approved, clearlydefined npm/npmjs/@types/aria-query/5.0.4, MIT, approved, #10958 +npm/npmjs/@types/autosuggest-highlight/3.2.3, MIT, approved, #11672 npm/npmjs/@types/babel__core/7.20.5, MIT, approved, clearlydefined npm/npmjs/@types/babel__generator/7.6.8, MIT, approved, clearlydefined npm/npmjs/@types/babel__template/7.4.4, MIT, approved, clearlydefined diff --git a/package.json b/package.json index 2bb1cd827..12ed647ac 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ ] }, "dependencies": { - "@catena-x/portal-shared-components": "^3.1.3", + "@catena-x/portal-shared-components": "^3.4.0", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@hookform/error-message": "^2.0.1", @@ -43,6 +43,7 @@ "@mui/x-data-grid": "^6.19.11", "@react-hook/cache": "^1.1.1", "@reduxjs/toolkit": "^2.2.6", + "autosuggest-highlight": "^3.3.4", "axios": "^1.6.8", "buffer": "^6.0.3", "dayjs": "^1.11.12", @@ -81,6 +82,7 @@ "@types/react-dom": "^18.2.22", "@types/react-redux": "^7.1.33", "@types/react-slick": "^0.23.13", + "@types/autosuggest-highlight": "^3.2.3", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", "@vitejs/plugin-react": "^4.2.1", diff --git a/src/components/pages/CompanyData/components/EditForm.tsx b/src/components/pages/CompanyData/components/EditForm.tsx index e0cb1046e..8fb21b180 100644 --- a/src/components/pages/CompanyData/components/EditForm.tsx +++ b/src/components/pages/CompanyData/components/EditForm.tsx @@ -147,6 +147,7 @@ export default function EditForm({ }) } catch (e) { setError(true) + setLoading(false) } } diff --git a/src/components/pages/CompanyData/components/FormFields.tsx b/src/components/pages/CompanyData/components/FormFields.tsx index 0a713fabf..dec7a74c8 100644 --- a/src/components/pages/CompanyData/components/FormFields.tsx +++ b/src/components/pages/CompanyData/components/FormFields.tsx @@ -32,7 +32,6 @@ import { import type { IHashMap } from 'types/MainTypes' import { useTranslation } from 'react-i18next' import ValidatingInput from 'components/shared/basic/Input/ValidatingInput' -import { SelectList } from '@catena-x/portal-shared-components' import { type CompanyDataFieldsType } from 'features/companyData/companyDataApiSlice' import { useSelector } from 'react-redux' import { companyDataSelector } from 'features/companyData/slice' @@ -40,6 +39,7 @@ import { type UniqueIdentifier, useFetchUniqueIdentifierQuery, } from 'features/admin/userApiSlice' +import { SelectList } from './SelectList' const responseToForm = (data: CompanyDataFieldsType) => { const form: IHashMap = {} @@ -287,10 +287,13 @@ export const FormFields = ({ setFormData(current) const formValid = current.siteName && + isName(current.siteName) && current.street && + isStreet(current.street) && current.city && - current.postalCode && - current.countryCode + isCity(current.city) + current.postalCode && isPostalCode(current.postalCode) + current.countryCode && isCountry(current.countryCode) onValid( formValid ? { diff --git a/src/components/pages/CompanyData/components/SelectList.tsx b/src/components/pages/CompanyData/components/SelectList.tsx new file mode 100644 index 000000000..4f613ab29 --- /dev/null +++ b/src/components/pages/CompanyData/components/SelectList.tsx @@ -0,0 +1,121 @@ +/******************************************************************************** + * Copyright (c) 2024 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 { type TextFieldProps } from '@mui/material' +import Autocomplete from '@mui/material/Autocomplete' +import parse from 'autosuggest-highlight/parse' +import match from 'autosuggest-highlight/match' +import { SelectInput, SelectOptions } from '@catena-x/portal-shared-components' +import uniqueId from 'lodash/uniqueId' +import isEqual from 'lodash/isEqual' +import { useState } from 'react' + +interface SelectListProps extends Omit { + // eslint-disable-next-line + items: any + label: string + placeholder: string + keyTitle: string + popperHeight?: number + variant?: 'filled' + clearText?: string + noOptionsText?: string + defaultValue?: unknown + disableClearable?: boolean + // eslint-disable-next-line + onChangeItem: (items: any) => void +} + +export const SelectList = ({ + items, + label, + placeholder, + defaultValue = {}, + disableClearable = false, + keyTitle, + variant, + margin, + focused, + helperText, + error = false, + disabled, + popperHeight = 0, + clearText = 'Clear', + noOptionsText = 'No Options', + onChangeItem, +}: SelectListProps) => { + const selectHeight = popperHeight ? `${popperHeight}px` : 'auto' + // Add an ESLint exception until there is a solution + // eslint-disable-next-line + const [selected, setSelected] = useState(defaultValue || {}) + + // eslint-disable-next-line + const handleChange = (newValue: any) => { + if (newValue) { + setSelected(newValue) + onChangeItem(newValue) + } + } + + return ( + item)} + getOptionLabel={(option) => option[keyTitle] || ''} + onChange={(_event, nextValue) => { + handleChange(nextValue) + }} + isOptionEqualToValue={(option, value) => isEqual(option, value)} + renderOption={(props, option, { inputValue }) => ( + + )} + value={selected} + renderInput={(params) => { + return ( + + ) + }} + /> + ) +} diff --git a/yarn.lock b/yarn.lock index fa3011273..376984254 100644 --- a/yarn.lock +++ b/yarn.lock @@ -329,10 +329,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@catena-x/portal-shared-components@^3.1.3": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-3.2.0.tgz#dcf4b3c4b3c2576801d6c3d9cb5f85ce8a2635d6" - integrity sha512-uy2jgIcGBcbWsg0MBN44b3W0MM6ZXrdmN5YeObGq9PW6D4fiGJviCGbqw12u0Zm20dNgchoCquArkJaSnv1uTw== +"@catena-x/portal-shared-components@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-3.4.0.tgz#68f9b2c6d4a567b347cd008a113a3f5f7bf64a89" + integrity sha512-EYhOSvwuXZt1zXPPpCFfSe+6I8VghH8fClH80aud+x8UhMiZbBzOOn6kjAZKnpKH9qoyEX4TqpE4ZUYxnfagMA== dependencies: "@date-io/date-fns" "^3.0.0" "@emotion/react" "^11.11.4" @@ -1366,6 +1366,11 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== +"@types/autosuggest-highlight@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@types/autosuggest-highlight/-/autosuggest-highlight-3.2.3.tgz#966b4f6b2b8fc9df2838481600500db6b3795aaa" + integrity sha512-8Mb21KWtpn6PvRQXjsKhrXIcxbSloGqNH50RntwGeJsGPW4xvNhfml+3kKulaKpO/7pgZfOmzsJz7VbepArlGQ== + "@types/babel__core@^7.1.14", "@types/babel__core@^7.20.5": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" From 26aa9c5e6827620975a742f87f2a3c21ff457097 Mon Sep 17 00:00:00 2001 From: lavanya-bmw <106523828+lavanya-bmw@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:16:53 +0530 Subject: [PATCH 6/8] feat(ospm): create new page (#1052) --- CHANGELOG.md | 4 + src/assets/locales/de/main.json | 34 +- src/assets/locales/en/main.json | 34 +- .../pages/EdcConnector/EdcConnector.scss | 4 - .../pages/IDPManagement/IDPList.tsx | 99 +++- .../OnboardingServiceProvider.scss | 51 +++ .../OnboardingServiceProvider.tsx | 422 ++++++++++++++++++ src/features/admin/idpApiSlice.ts | 56 +++ src/types/Config.tsx | 6 + src/types/Constants.ts | 1 + 10 files changed, 702 insertions(+), 9 deletions(-) create mode 100644 src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.scss create mode 100644 src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 43cc1d0ab..3d6ed7b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Feature + +- **Onboarding Service Provider Management**: + - Create new page for onboarding service provider management [#1052](https://github.com/eclipse-tractusx/portal-frontend/pull/1052) - Company data - Disable confirm button if input value is not matching with the regular expression - Fix infinite loop issue in add New Address Modal diff --git a/src/assets/locales/de/main.json b/src/assets/locales/de/main.json index 0430cd789..4c34a1faf 100644 --- a/src/assets/locales/de/main.json +++ b/src/assets/locales/de/main.json @@ -69,7 +69,8 @@ "companySubscriptions": "Firmenabonnements", "mycompany": "My Company", "mynotifications": "My Notifications", - "companyData": "Company Data" + "companyData": "Company Data", + "ManagementOnboardingServiceProvider": "Onboarding Service Provider" }, "overlays": { "invite": "Neuen Geschäftspartner einladen", @@ -2144,6 +2145,37 @@ "title": "Create {name}", "description": "The changes could not be saved. Please try again" } + }, + "onboardingServiceProvider": { + "headertitle": "Onboarding-Dienstleister-Management", + "desc": "[description] lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", + "subDesc1": "OSP-Callback konfiguriert", + "subDesc2": "https://url.lorem.ipsum.dolor.sit.amet", + "tabletitle1": "OSP-Identitätsanbieter (IDPs)", + "tabletitle2": "Kundenübersicht", + "userList": "Benutzerliste", + "addIdentityProvider": "Identitätsanbieter hinzufügen", + "dialogTitle": "IdP-Metadaten konfigurieren", + "table": { + "customerName": "Kundenname", + "status": "Status", + "idpName": "IDP-Name", + "users": "Anzahl der Benutzer" + }, + "success": "OSP-Rückruf erfolgreich konfiguriert", + "callbackUrlError": "Fehler beim Konfigurieren des OSP-Rückrufs", + "callbackUrl": { + "name": "Rückruf-URL", + "hint": "Geben Sie die Rückruf-URL Ihres IDP ein, die mit „/.well-known/openid-configuration“ endet'" + }, + "clientId": { + "name": "Kunden-ID", + "hint": "Geben Sie die von Ihrem IdP bereitgestellte Client-ID ein" + }, + "clientSecret": { + "name": "Client-Geheimnis", + "hint": "Geben Sie das von Ihrem IdP bereitgestellte Client-Geheimnis ein" + } } }, "navigation": { diff --git a/src/assets/locales/en/main.json b/src/assets/locales/en/main.json index 5170555f0..86952ed00 100644 --- a/src/assets/locales/en/main.json +++ b/src/assets/locales/en/main.json @@ -68,7 +68,8 @@ "companySubscriptions": "Company Subscriptions", "mycompany": "My Company", "mynotifications": "My Notifications", - "companyData": "Company Data" + "companyData": "Company Data", + "ManagementOnboardingServiceProvider": "Onboarding Service Provider" }, "overlays": { "invite": "Invite new Business Partner", @@ -2116,6 +2117,37 @@ "title": "Create {name}", "description": "The changes could not be saved. Please try again" } + }, + "onboardingServiceProvider": { + "headertitle": "Onboarding Service Provider Management", + "desc": "[description] lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", + "subDesc1": "OSP Callback configured", + "subDesc2": "https://url.lorem.ipsum.dolor.sit.amet", + "tabletitle1": "OSP Identity Provider (IDPs)", + "tabletitle2": "Customer Overview", + "userList": "User List", + "addIdentityProvider": "Add Identity Provider", + "dialogTitle": "Configure IdP Metadata", + "table": { + "customerName": "Customer Name", + "status": "Status", + "idpName": "IDP name", + "users": "# of users" + }, + "success": "Successfully configured OSP callback", + "callbackUrlError": "Failed configuring OSP callback", + "callbackUrl": { + "name": "Callback URL", + "hint": "Enter the callback URL from your IDP that ends with '/.well-known/openid-configuration'" + }, + "clientId": { + "name": "Client ID", + "hint": "Enter the client ID provided by your IdP" + }, + "clientSecret": { + "name": "Client Secret", + "hint": "Enter the client secret provided by your IdP" + } } }, "navigation": { diff --git a/src/components/pages/EdcConnector/EdcConnector.scss b/src/components/pages/EdcConnector/EdcConnector.scss index e0cecec46..aad104ecd 100644 --- a/src/components/pages/EdcConnector/EdcConnector.scss +++ b/src/components/pages/EdcConnector/EdcConnector.scss @@ -26,10 +26,6 @@ margin: 0; padding: 0; width: 100%; - - .picture-with-text-section { - margin-top: 50px; - } } .connector-type-selector-container { diff --git a/src/components/pages/IDPManagement/IDPList.tsx b/src/components/pages/IDPManagement/IDPList.tsx index 567b2051a..3f5b54d38 100644 --- a/src/components/pages/IDPManagement/IDPList.tsx +++ b/src/components/pages/IDPManagement/IDPList.tsx @@ -78,7 +78,7 @@ const MenuItemOpenOverlay = ({ ) } -export const IDPList = () => { +export const IDPList = ({ isManagementOSP }: { isManagementOSP?: boolean }) => { const { t } = useTranslation() const ti = useTranslation('idp').t @@ -252,6 +252,92 @@ export const IDPList = () => { ) } + const renderManagementOSPMenu = (idp: IdentityProvider) => { + const isManagedIdp = idp.identityProviderTypeId === IDPCategory.MANAGED + const menuItems = { + edit: ( + + ), + delete: isManagedIdp ? ( + + ) : ( + ) => + !deleteLoading && doDelete(e, idp) + } + disabled={idp.enabled} + > + {ti('action.delete')} + {deleteLoading && ( + + )} + + ), + enableToggle: + isManagedIdp && idp.enabled ? ( + + ) : ( + idp.enabled).length < 2 + } + onClick={(e: React.MouseEvent) => + !disableLoading && doEnableDisableToggle(e, idp) + } + > + {idp.enabled ? ti('action.disable') : ti('action.enable')} + {disableLoading && ( + + )} + + ), + } + + return ( +
+ + {menuItems.edit} + {menuItems.enableToggle} + {menuItems.delete} + +
+ ) + } + return (
{ headerName: t('global.field.action'), flex: 2, sortable: false, - renderCell: ({ row }: { row: IdentityProvider }) => renderMenu(row), + renderCell: ({ row }: { row: IdentityProvider }) => + isManagementOSP ? renderManagementOSPMenu(row) : renderMenu(row), }, ]} - rows={idpsData ?? []} + rows={ + (isManagementOSP + ? idpsData?.filter( + (a) => a.identityProviderTypeId === IDPCategory.MANAGED + ) + : idpsData) ?? [] + } getRowId={(row: { [key: string]: string }) => row.identityProviderId} hasBorder={false} /> diff --git a/src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.scss b/src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.scss new file mode 100644 index 000000000..fd5ecb96e --- /dev/null +++ b/src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.scss @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (c) 2024 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 + ********************************************************************************/ + +.onboarding-service-page-container { + margin: 0; + padding: 0; + width: 100%; +} + +.onboarding-service-header { + text-align: center; + width: 100%; + margin: 0 auto; + + .onboarding-service-title { + text-align: center; + padding-bottom: 20px; + position: relative; + } + + .onboarding-service-title::after { + content: ''; + width: 70px; + border-bottom: 2px solid; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + } + + .onboarding-service-desc { + width: 74%; + margin: 50px auto; + } +} diff --git a/src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.tsx b/src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.tsx new file mode 100644 index 000000000..507fb1014 --- /dev/null +++ b/src/components/pages/OnboardingServiceProvider/OnboardingServiceProvider.tsx @@ -0,0 +1,422 @@ +/******************************************************************************** + * Copyright (c) 2024 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 { type SyntheticEvent, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { + Typography, + IconButton, + Tabs, + Tab, + TabPanel, + PageLoadingTable, + DialogActions, + Button, + DialogHeader, + DialogContent, + Dialog, + LoadingButton, +} from '@catena-x/portal-shared-components' +import ArrowForwardIcon from '@mui/icons-material/ArrowForward' +import { Box } from '@mui/material' +import './OnboardingServiceProvider.scss' +import { IDPList } from '../IDPManagement/IDPList' +import { + type networkCompany, + type OIDCSignatureAlgorithm, + type RegistartionStatusCallbackType, + useFetchCompaniesListQuery, + useFetchRegistartionStatusCallbackQuery, + useUpdateRegistartionStatusCallbackMutation, +} from 'features/admin/idpApiSlice' +import ValidatingInput from 'components/shared/basic/Input/ValidatingInput' +import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline' +import { useDispatch } from 'react-redux' +import { show } from 'features/control/overlay' +import { OVERLAYS } from 'types/Constants' +import { isIDPClientID, isIDPClientSecret, isURL } from 'types/Patterns' +import { InputType } from 'components/shared/basic/Input/BasicInput' +import { type IHashMap } from 'types/MainTypes' +import { success } from 'services/NotifyService' +import WarningAmberIcon from '@mui/icons-material/WarningAmber' + +const OnboardingServiceProvider = () => { + const { t } = useTranslation() + const [activeTab, setActiveTab] = useState(0) + const [overlayOpen, setOverlayOpen] = useState(false) + const dispatch = useDispatch() + const { data } = useFetchRegistartionStatusCallbackQuery() + const [loading, setLoading] = useState(false) + const [updateRegistartionStatusCallback] = + useUpdateRegistartionStatusCallbackMutation() + const [formData, setFormData] = useState>({}) + const [showError, setShowError] = useState(false) + const [callbackData, setCallbackData] = useState< + RegistartionStatusCallbackType | undefined + >(undefined) + + const handleTabChange = ( + _e: SyntheticEvent, + value: number + ) => { + setActiveTab(value) + } + + const getTabsIcon = (step: number) => { + return ( + + {step} + + ) + } + + const isWellknownMetadata = (expr: string) => + isURL(expr) && expr.endsWith('.well-known/openid-configuration') + + const updateCallbackIDP = async () => { + if (!(data && callbackData)) return + setLoading(true) + try { + await updateRegistartionStatusCallback(callbackData).unwrap() + success(t('content.onboardingServiceProvider.success')) + setOverlayOpen(false) + } catch (err) { + setShowError(true) + } + setLoading(false) + } + + const checkValidData = (key: string, value: string | undefined): boolean => { + const current: IHashMap = { ...formData } + current[key] = value as OIDCSignatureAlgorithm + setFormData(current) + const formValid = + current.callbackUrl && current.clientId && current.clientSecret + setCallbackData( + formValid + ? { + callbackUrl: current.callbackUrl, + clientId: current.clientId, + clientSecret: current.clientSecret, + authUrl: '', + } + : undefined + ) + return true + } + + return ( +
+
+ + + } + closeWithIcon={true} + onCloseWithIcon={() => { + setOverlayOpen(false) + }} + /> + + <> +
+ isWellknownMetadata(expr)} + hint={t('content.onboardingServiceProvider.callbackUrl.hint')} + debounceTime={0} + onValid={checkValidData} + /> +
+
+ +
+
+ +
+ {showError && ( + + + {t('content.onboardingServiceProvider.callbackUrlError')} + + )} + +
+ + + + {loading ? ( + { + // do nothing + }} + sx={{ marginLeft: '10px' }} + /> + ) : ( + + )} + +
+ +
+ + {t('content.onboardingServiceProvider.headertitle')} + + + {t('content.onboardingServiceProvider.desc')} + + + + + {t('content.onboardingServiceProvider.subDesc1')} + + + {t('content.onboardingServiceProvider.subDesc2')} + + + { + setOverlayOpen(true) + }} + sx={{ + right: '0', + position: 'absolute', + top: '50%', + msTransform: 'translateY(-50%)', + transform: 'translateY(-50%)', + }} + > + + + +
+
+ + + + + + + +
+ + + {t('content.onboardingServiceProvider.userList')} + + + + +
+
+ +
+ + toolbarVariant="premium" + title={t('content.onboardingServiceProvider.tabletitle2')} + loadLabel={t('global.actions.more')} + fetchHook={useFetchCompaniesListQuery} + getRowId={(row: { [key: string]: string }) => + row.applicationStatus + } + columns={[ + { + field: 'companyName', + headerName: t( + 'content.onboardingServiceProvider.table.customerName' + ), + flex: 1, + sortable: false, + }, + { + field: 'applicationStatus', + headerName: t( + 'content.onboardingServiceProvider.table.status' + ), + flex: 1, + sortable: false, + }, + { + field: 'identityProvider', + headerName: t( + 'content.onboardingServiceProvider.table.idpName' + ), + flex: 1, + sortable: false, + renderCell: ({ row }: { row: networkCompany }) => + row?.identityProvider?.[0].alias, + }, + { + field: 'activeUsers', + headerName: t( + 'content.onboardingServiceProvider.table.users' + ), + flex: 1, + sortable: false, + }, + ]} + /> +
+
+
+
+ ) +} + +export default OnboardingServiceProvider diff --git a/src/features/admin/idpApiSlice.ts b/src/features/admin/idpApiSlice.ts index e86514ea9..611e7c9e2 100644 --- a/src/features/admin/idpApiSlice.ts +++ b/src/features/admin/idpApiSlice.ts @@ -18,6 +18,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +import { + type PaginFetchArgs, + type PaginResult, +} from '@catena-x/portal-shared-components' import { type PayloadAction, createSlice } from '@reduxjs/toolkit' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { type RootState } from 'features/store' @@ -177,6 +181,32 @@ export interface ManagedIDPNetworkType { | null } +export type networkCompany = { + companyId: string + externalId: string + applicationId: string + applicationStatus: string + applicationDateCreated: string + dateCreated: string + lastChangedDate: string + companyName: string + companyRoles?: string[] | null + identityProvider?: IdentityProviderEntity[] | null + bpn: string + activeUsers: number +} +export interface IdentityProviderEntity { + identityProviderId: string + alias: string +} + +export interface RegistartionStatusCallbackType { + callbackUrl: string + authUrl?: string + clientId: string + clientSecret: string +} + enum TAGS { IDP = 'idp', } @@ -280,6 +310,29 @@ export const apiSlice = createApi({ query: (id: string) => `/api/administration/identityprovider/network/identityproviders/managed/${id}`, }), + fetchCompaniesList: builder.query< + PaginResult, + PaginFetchArgs + >({ + query: (filters) => + `/api/administration/registration/network/companies?page=${filters.page}&size=10`, + }), + fetchRegistartionStatusCallback: builder.query< + RegistartionStatusCallbackType, + void + >({ + query: () => '/api/administration/RegistrationStatus/callback', + }), + updateRegistartionStatusCallback: builder.mutation< + void, + RegistartionStatusCallbackType + >({ + query: (data: RegistartionStatusCallbackType) => ({ + url: '/api/administration/RegistrationStatus/callback', + method: 'POST', + body: data, + }), + }), }), }) @@ -296,6 +349,9 @@ export const { useEnableIDPMutation, useUpdateUserIDPMutation, useFetchManagedIDPNetworkQuery, + useFetchCompaniesListQuery, + useFetchRegistartionStatusCallbackQuery, + useUpdateRegistartionStatusCallbackMutation, } = apiSlice export default slice diff --git a/src/types/Config.tsx b/src/types/Config.tsx index 387b73f24..02a48c5bf 100644 --- a/src/types/Config.tsx +++ b/src/types/Config.tsx @@ -87,6 +87,7 @@ import { OSPConsent } from 'components/pages/OSPConsent' import CompanySubscriptions from 'components/pages/CompanySubscriptions' import CompanySubscriptionDetail from 'components/pages/CompanySubscriptions/CompanySubscriptionDetail' import CompanyData from 'components/pages/CompanyData' +import OnboardingServiceProvider from 'components/pages/OnboardingServiceProvider/OnboardingServiceProvider' /** * ALL_PAGES @@ -579,6 +580,10 @@ export const ALL_PAGES: IPage[] = [ role: ROLES.MY_ORGANIZATION_VIEW, element: , }, + { + name: PAGES.MANAGEMENT_ONBOARDING_SERVICE_PROVIDER, + element: , + }, ] export const ALL_OVERLAYS: IOverlay[] = [ @@ -812,6 +817,7 @@ export const userMenuFull = [ PAGES.COMPANY_CERTIFICATE, PAGES.COMPANY_WALLET, PAGES.COMPANY_DATA, + PAGES.MANAGEMENT_ONBOARDING_SERVICE_PROVIDER, PAGES.LOGOUT, ] diff --git a/src/types/Constants.ts b/src/types/Constants.ts index 6b54a64aa..6fb5b4605 100644 --- a/src/types/Constants.ts +++ b/src/types/Constants.ts @@ -109,6 +109,7 @@ export enum PAGES { COMPANY_SUBSCRIPTIONS = 'companySubscriptions', COMPANY_SUBSCRIPTIONS_DETAIL = 'companySubscriptionsDetail', COMPANY_DATA = 'companyData', + MANAGEMENT_ONBOARDING_SERVICE_PROVIDER = 'ManagementOnboardingServiceProvider', } export enum OVERLAYS { From d1f02da946823fc3f7aa44744baa8b6542edffad Mon Sep 17 00:00:00 2001 From: Manojava Koushik <111366021+manojava-gk@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:27:41 +0530 Subject: [PATCH 7/8] feat(company data): upload csv (#1056) --- src/assets/locales/de/main.json | 17 +- src/assets/locales/en/main.json | 17 +- .../overlays/CSVUploadOverlay/index.tsx | 291 ++++++++++++++++++ .../components/CompanyAddressList.tsx | 62 ++-- src/components/pages/CompanyData/index.tsx | 24 +- .../companyData/companyDataApiSlice.tsx | 41 ++- src/features/companyData/slice.ts | 10 + src/services/AccessService.tsx | 3 + src/types/Config.tsx | 3 + src/types/Constants.ts | 1 + 10 files changed, 434 insertions(+), 35 deletions(-) create mode 100644 src/components/overlays/CSVUploadOverlay/index.tsx diff --git a/src/assets/locales/de/main.json b/src/assets/locales/de/main.json index 4c34a1faf..b566cf13b 100644 --- a/src/assets/locales/de/main.json +++ b/src/assets/locales/de/main.json @@ -2046,9 +2046,22 @@ }, "companyData": { "label": "Company Data", + "csvUploadBtn": "Upload CSV", "statusInfo": { "title": "Status Information" }, + "upload": { + "title": "Upload CSV", + "fileSizeError": "Uploaded file is too big. Maximum 1MB is allowed", + "successDescription": "CSV Upload is success. Please close the modal to see updated list", + "note": "Note: Please upload only CSV files with maximum 1 MB.", + "errorDescription": "CSV Upload failed with the following erros", + "templateBtn": "Download CSV Template", + "emptyError": "Uploaded CSV is empty. Kindly check and upload again", + "copy": "Copy Error to clipboard", + "copySuccess": "Error messages copied successfully", + "downloadSuccess": "CSV template download successfully" + }, "companyInfo": { "title": "Company Name", "legalEntityName": "Legal Entity Name", @@ -2066,7 +2079,9 @@ "location": "Location", "search": "search address / site", "noRowsMsg": "No Data yet", - "type": "Type" + "type": "Type", + "status": "Status", + "details": "Details" }, "site": { "title": "Create new site", diff --git a/src/assets/locales/en/main.json b/src/assets/locales/en/main.json index 86952ed00..aef08c23b 100644 --- a/src/assets/locales/en/main.json +++ b/src/assets/locales/en/main.json @@ -2013,9 +2013,22 @@ }, "companyData": { "label": "Company Data", + "csvUploadBtn": "Upload CSV", "statusInfo": { "title": "Status Information" }, + "upload": { + "title": "Upload CSV", + "fileSizeError": "Uploaded file is too big. Maximum 1MB is allowed", + "successDescription": "CSV Upload is success. Please close the modal to see updated list", + "note": "Note: Please upload only CSV files with maximum 1 MB.", + "errorDescription": "CSV Upload failed with the following erros", + "templateBtn": "Download CSV Template", + "emptyError": "Uploaded CSV is empty. Kindly check and upload again", + "copy": "Copy Error to clipboard", + "copySuccess": "Error messages copied successfully", + "downloadSuccess": "CSV template download successfully" + }, "companyInfo": { "title": "Company Name", "legalEntityName": "Legal Entity Name", @@ -2033,7 +2046,9 @@ "location": "Location", "search": "search address / site", "noRowsMsg": "No Data yet", - "type": "Type" + "type": "Type", + "status": "Status", + "details": "Details" }, "site": { "title": "Create new site", diff --git a/src/components/overlays/CSVUploadOverlay/index.tsx b/src/components/overlays/CSVUploadOverlay/index.tsx new file mode 100644 index 000000000..d2a514718 --- /dev/null +++ b/src/components/overlays/CSVUploadOverlay/index.tsx @@ -0,0 +1,291 @@ +/******************************************************************************** + * Copyright (c) 2024 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 { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { + Dialog, + Button, + DialogActions, + DialogContent, + DialogHeader, + DropArea, + type DropAreaProps, + LoadingButton, + Typography, +} from '@catena-x/portal-shared-components' +import { useUploadCSVMutation } from 'features/companyData/companyDataApiSlice' +import { Dropzone } from 'components/shared/basic/Dropzone' +import { useDispatch } from 'react-redux' +import { closeOverlay } from 'features/control/overlay' +import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined' +import UserService from 'services/UserService' +import { getBpdmGateApiBase } from 'services/EnvironmentService' +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline' +import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined' +import WarningAmberOutlinedIcon from '@mui/icons-material/WarningAmberOutlined' +import { success as CopySuccess } from 'services/NotifyService' + +export default function CSVUploadOverlay(): JSX.Element { + const { t } = useTranslation() + const dispatch = useDispatch() + const [uploadedFile, setUploadedFile] = useState() + const [loading, setLoading] = useState(false) + const [uploadCSV] = useUploadCSVMutation() + const [success, setSuccess] = useState(false) + const [error, setError] = useState(false) + const [errorResponse, setErrorResponse] = useState([]) + + const renderDropArea = (props: DropAreaProps): JSX.Element => { + return + } + + const handleSubmit = async (): Promise => { + setLoading(true) + try { + if (uploadedFile != null) { + await uploadCSV(uploadedFile) + .unwrap() + .then((response) => { + if (response.length > 0) { + setSuccess(true) + } else { + setError(true) + setErrorResponse([]) + } + }) + setLoading(false) + } + // Add an ESLint exception until there is a solution + // eslint-disable-next-line + } catch (err: any) { + setLoading(false) + setErrorResponse(err?.data?.error ?? []) + setError(true) + } + } + + const handleClose = () => { + dispatch(closeOverlay()) + } + + const downloadTemplate = () => { + const url = `${getBpdmGateApiBase()}/input/partner-upload-template` + return fetch(url, { + method: 'GET', + headers: { + authorization: `Bearer ${UserService.getToken()}`, + }, + }) + .then((res) => res.blob()) + .then((blob) => { + const url = window.URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = 'upload_template.csv' + document.body.appendChild(a) + a.click() + a.remove() + CopySuccess(t('content.companyData.upload.downloadSuccess')) + }) + } + + const getIcon = () => { + if (success) + return + else if (error) + return + return <> + } + + return ( + + { + handleClose() + }, + iconComponent: getIcon(), + }} + /> + {success && ( + +
+ + {t('content.companyData.upload.successDescription')} + +
+
+ )} + {error && ( + +
+ + {t('content.companyData.upload.errorDescription')} + +
+ {errorResponse?.length > 0 ? ( + <> + {errorResponse.map((text, i) => ( + + {text} + + ))} + + ) : ( + + {t('content.companyData.upload.emptyError')} + + )} +
+
+ +
+
+
+ )} + {!success && !error && ( + +
+ +
+ { + setUploadedFile(file) + }} + errorText={t('content.companyData.upload.fileSizeError')} + DropStatusHeader={false} + DropArea={renderDropArea} + /> +
+ + {t('content.companyData.upload.note')} + +
+
+ )} + {success && ( + + + + )} + {!success && !error && ( + + + + {loading ? ( + { + // do nothing + }} + sx={{ marginLeft: '10px' }} + /> + ) : ( + + )} + + )} +
+ ) +} diff --git a/src/components/pages/CompanyData/components/CompanyAddressList.tsx b/src/components/pages/CompanyData/components/CompanyAddressList.tsx index a2e4ec14f..816ae07fe 100644 --- a/src/components/pages/CompanyData/components/CompanyAddressList.tsx +++ b/src/components/pages/CompanyData/components/CompanyAddressList.tsx @@ -19,7 +19,7 @@ import { Chip, IconButton, Table } from '@catena-x/portal-shared-components' import { useEffect, useState } from 'react' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { Box } from '@mui/material' import { useTranslation } from 'react-i18next' import { @@ -29,6 +29,7 @@ import { useFetchOutputCompanyBusinessPartnersMutation, useFetchSharingStateQuery, AddressType, + type SharingStateType, } from 'features/companyData/companyDataApiSlice' import HourglassBottomIcon from '@mui/icons-material/HourglassBottom' import WarningAmberIcon from '@mui/icons-material/WarningAmber' @@ -37,32 +38,34 @@ import ArrowForwardIcon from '@mui/icons-material/ArrowForward' import { type GridCellParams } from '@mui/x-data-grid' import DetailsOverlay from './DetailsOverlay' import { + companyRefetch, + setCompanyPageRefetch, setSelectedCompanyData, setSelectedCompanyStatus, setSharingStateInfo, } from 'features/companyData/slice' -import LoadingProgress from 'components/shared/basic/LoadingProgress' import { statusColorMap } from 'utils/dataMapper' export const CompanyAddressList = ({ handleButtonClick, handleSecondButtonClick, - refetch = false, handleConfirm, }: { handleButtonClick: () => void handleSecondButtonClick: () => void - refetch: boolean handleConfirm: () => void }) => { const { t } = useTranslation() + const [page, setPage] = useState(0) const { data, - refetch: refreshSharingData, isFetching, error: sharingStateError, - } = useFetchSharingStateQuery() - const sharingStates = data?.content + refetch: refetchSharingState, + } = useFetchSharingStateQuery({ + page, + }) + const [sharingStates, setSharingStates] = useState([]) const [outputRequest, { isLoading: isOutputLoading, error: outputError }] = useFetchOutputCompanyBusinessPartnersMutation() const [inputRequest, { isLoading: isInputLoading, error: inputError }] = @@ -71,6 +74,7 @@ export const CompanyAddressList = ({ const [inputs, setInputs] = useState([]) const [details, setDetails] = useState(false) const dispatch = useDispatch() + const refetch = useSelector(companyRefetch) const getInputItems = async () => { const params = sharingStates @@ -113,14 +117,24 @@ export const CompanyAddressList = ({ useEffect(() => { if (refetch) { - refreshSharingData() setInputs([]) setOutputs([]) + setPage(0) + refetchSharingState() + dispatch(setCompanyPageRefetch(false)) } getInputItems() getOutputItems() }, [sharingStates, refetch]) + useEffect(() => { + if (data) { + setSharingStates((i) => + page === 0 ? data.content : i.concat(data.content) + ) + } + }, [data]) + const getStatus = (id: string) => sharingStates?.filter((state) => id === state.externalId)[0] .sharingStateType @@ -164,8 +178,14 @@ export const CompanyAddressList = ({ return ( <> - {!isFetching && !isOutputLoading && !isInputLoading ? ( + {sharingStates.length > 0 && (
page + 1} + nextPage={() => { + setPage((i) => i + 1) + }} + hideFooterPagination={true} autoFocus={false} onButtonClick={handleButtonClick} rowsCount={inputs.length + outputs.length} @@ -181,7 +201,7 @@ export const CompanyAddressList = ({ getRowId={(row: { [key: string]: string }) => row.createdAt} rows={inputs.concat(outputs)} onCellClick={onRowClick} - error={errorObj} + error={errorObj.status === 0 ? null : errorObj} columns={[ { field: 'site', @@ -197,7 +217,7 @@ export const CompanyAddressList = ({ headerAlign: 'left', align: 'left', headerName: t('content.companyData.table.location'), - flex: 2.5, + flex: 2, valueGetter: ({ row }: { row: CompanyDataType }) => row.address ? `${row.address.name ?? ''} ${row.address.physicalPostalAddress.street?.name ?? ''} ${row.address.physicalPostalAddress.street?.houseNumber ?? ''} ${row.address.physicalPostalAddress.city ?? ''} ${row.address.physicalPostalAddress.postalCode ?? ''} ${row.address.physicalPostalAddress.country ?? ''}` @@ -216,7 +236,7 @@ export const CompanyAddressList = ({ }, { field: 'status', - headerName: '', + headerName: t('content.companyData.table.status'), align: 'left', flex: 1, renderCell: ({ row }: { row: CompanyDataType }) => { @@ -252,9 +272,9 @@ export const CompanyAddressList = ({ }, { field: 'details', - headerName: '', + headerName: t('content.companyData.table.details'), align: 'left', - flex: 0.5, + flex: 1, renderCell: () => { return ( - ) : ( - - - )} {details && ( { + setSharingStates([]) + handleConfirm() + }} /> )} diff --git a/src/components/pages/CompanyData/index.tsx b/src/components/pages/CompanyData/index.tsx index 8088f3180..167888a1d 100644 --- a/src/components/pages/CompanyData/index.tsx +++ b/src/components/pages/CompanyData/index.tsx @@ -21,13 +21,20 @@ import { CompanyAddressList } from './components/CompanyAddressList' import { useState } from 'react' import MyCompanyInfoComponent from '../Organization/MyCompanyInfoComponent' import EditForm from './components/EditForm' +import { useDispatch } from 'react-redux' +import { setCompanyPageRefetch } from 'features/companyData/slice' +import { Button } from '@catena-x/portal-shared-components' +import { useTranslation } from 'react-i18next' +import { show } from 'features/control/overlay' +import { OVERLAYS } from 'types/Constants' export default function CompanyData() { + const { t } = useTranslation() const [showOverlay, setShowOverlay] = useState({ address: false, site: false, }) - const [refetch, setRefetch] = useState(0) + const dispatch = useDispatch() const updateOverlay = () => { setShowOverlay((old) => { @@ -41,6 +48,16 @@ export default function CompanyData() {
+
+ +
{ @@ -55,9 +72,8 @@ export default function CompanyData() { return { ...old } }) }} - refetch={refetch !== 0} handleConfirm={() => { - setRefetch(Date.now()) + dispatch(setCompanyPageRefetch(true)) }} /> { - setRefetch(Date.now()) + dispatch(setCompanyPageRefetch(true)) }} />
diff --git a/src/features/companyData/companyDataApiSlice.tsx b/src/features/companyData/companyDataApiSlice.tsx index 6dfe4cc7a..a3efef5c0 100644 --- a/src/features/companyData/companyDataApiSlice.tsx +++ b/src/features/companyData/companyDataApiSlice.tsx @@ -215,15 +215,28 @@ export interface ReadyStateRequestBody { externalIds: string[] } +interface SharingStateRequest { + page: number +} + +enum TAGS { + SHARING = 'sharing', +} + export const apiSlice = createApi({ reducerPath: 'rtk/companyData', baseQuery: fetchBaseQuery(apiBpdmGateQuery()), + tagTypes: [TAGS.SHARING], endpoints: (builder) => ({ - fetchSharingState: builder.query({ - query: () => ({ - url: '/sharing-state?page=0&size=100', - }), - }), + fetchSharingState: builder.query( + { + query: (obj) => ({ + url: `/sharing-state?page=${obj.page}&size=100`, + }), + keepUnusedDataFor: 5, + providesTags: [TAGS.SHARING], + } + ), fetchInputCompanyBusinessPartners: builder.mutation< CompanyDataResponse, string[] | void @@ -264,6 +277,22 @@ export const apiSlice = createApi({ body: data, }), }), + uploadCSV: builder.mutation({ + query: (body) => { + const formData = new FormData() + formData.append('file', body) + formData.append('type', body.type) + return { + url: '/input/partner-upload-process', + method: 'POST', + body: formData, + } + }, + invalidatesTags: [TAGS.SHARING], + }), + downloadCsv: builder.query({ + query: () => '/input/partner-upload-template', + }), }), }) @@ -273,4 +302,6 @@ export const { useFetchOutputCompanyBusinessPartnersMutation, useUpdateCompanySiteAndAddressMutation, useUpdateCompanyStatusToReadyMutation, + useUploadCSVMutation, + useDownloadCsvQuery, } = apiSlice diff --git a/src/features/companyData/slice.ts b/src/features/companyData/slice.ts index 6e1d507a2..d50ebf0f1 100644 --- a/src/features/companyData/slice.ts +++ b/src/features/companyData/slice.ts @@ -31,6 +31,7 @@ export interface CompanyDataState { row: CompanyDataType status: string sharingStateInfo: SharingStateType + refetchState: boolean } export const companyDataInitialData: CompanyDataType = { @@ -158,6 +159,7 @@ export const initialState: CompanyDataState = { sharingProcessStarted: '', taskId: '', }, + refetchState: false, } const companyDataSlice = createSlice({ @@ -176,6 +178,10 @@ const companyDataSlice = createSlice({ ...state, sharingStateInfo: actions.payload, }), + setCompanyPageRefetch: (state, actions) => ({ + ...state, + refetchState: actions.payload, + }), }, }) @@ -188,10 +194,14 @@ export const statusSelector = (state: RootState): string => export const sharingStateInfoSelector = (state: RootState): SharingStateType => state.companyData.sharingStateInfo +export const companyRefetch = (state: RootState): boolean => + state.companyData.refetchState + export const { setSelectedCompanyData, setSelectedCompanyStatus, setSharingStateInfo, + setCompanyPageRefetch, } = companyDataSlice.actions export default companyDataSlice diff --git a/src/services/AccessService.tsx b/src/services/AccessService.tsx index 50bbd0967..69e5549cd 100644 --- a/src/services/AccessService.tsx +++ b/src/services/AccessService.tsx @@ -70,6 +70,7 @@ import CompanyCertificateDetails from 'components/overlays/CompanyCertificateDet import DeleteCompanyCertificateConfirmationOverlay from 'components/overlays/CompanyCertificateDetails/DeleteCompanyCertificateConfirmationOverlay' import { DisableManagedIDP } from 'components/overlays/EnableIDP/DisableManagedIdp' import { DeleteManagedIDP } from 'components/overlays/IDPDelete/DeleteManagedIdp' +import CSVUploadOverlay from 'components/overlays/CSVUploadOverlay' let pageMap: { [page: string]: IPage } let actionMap: { [action: string]: IAction } @@ -209,6 +210,8 @@ export const getOverlay = (overlay: OverlayState) => { title={overlay.title ?? ''} /> ) + case OVERLAYS.CSV_UPLOAD_OVERLAY: + return default: return } diff --git a/src/types/Config.tsx b/src/types/Config.tsx index 02a48c5bf..041a1f142 100644 --- a/src/types/Config.tsx +++ b/src/types/Config.tsx @@ -726,6 +726,9 @@ export const ALL_OVERLAYS: IOverlay[] = [ { name: OVERLAYS.COMPANY_CERTIFICATE_CONFIRM_DELETE, }, + { + name: OVERLAYS.CSV_UPLOAD_OVERLAY, + }, ] export const ALL_ACTIONS: IAction[] = [ diff --git a/src/types/Constants.ts b/src/types/Constants.ts index 6fb5b4605..4e1b491c6 100644 --- a/src/types/Constants.ts +++ b/src/types/Constants.ts @@ -156,6 +156,7 @@ export enum OVERLAYS { UPDATE_CERTIFICATE = 'updateCertificate', COMPANY_CERTIFICATE_DETAILS = 'companyCertificateDetails', COMPANY_CERTIFICATE_CONFIRM_DELETE = 'companyCertificateConfirmDelete', + CSV_UPLOAD_OVERLAY = 'csvUploadOverlay', } export enum ACTIONS { From a8dcdf2ef083738fc346ad581482eaed3380f110 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Mon, 2 Sep 2024 09:39:23 +0200 Subject: [PATCH 8/8] build(release): bump version for v2.2.0-RC2 (#1060) * build: update changelog * build(release): bump version for v2.2.0-RC2 ---------------- Reviewed-By: Martin Rohrmeier --- CHANGELOG.md | 18 ++++++++++++------ package.json | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d6ed7b74..9193313a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,24 @@ # Changelog -## Unreleased +## 2.2.0-RC2 ### Feature - **Onboarding Service Provider Management**: - Create new page for onboarding service provider management [#1052](https://github.com/eclipse-tractusx/portal-frontend/pull/1052) -- Company data - - Disable confirm button if input value is not matching with the regular expression +- **Company data** + - Disable confirm button if input value is not matching with the regular expression [#1056](https://github.com/eclipse-tractusx/portal-frontend/pull/1056) - Fix infinite loop issue in add New Address Modal -- Service Subscription + - Add new csv bulk upload modal [#1056](https://github.com/eclipse-tractusx/portal-frontend/pull/1056) +- **Service Subscription** - Update auto setup api response data in the service subscription response overlay -- Partner Network - - Change interface of the partner network apis. add /members to the POST api and update the business logic accordingly + +### Bugfixes + +- **Partner Network** + - Change interface of the partner network apis. add /members to the POST api and update the business logic accordingly [#1002](https://github.com/eclipse-tractusx/portal-frontend/pull/1002) +- **Business Partner invite** + - fix person name regex patter [#1040](https://github.com/eclipse-tractusx/portal-frontend/pull/1040) ## 2.2.0-RC1 diff --git a/package.json b/package.json index 12ed647ac..e4701b741 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@catena-x/portal-frontend", - "version": "v2.2.0-RC1", + "version": "v2.2.0-RC2", "description": "Catena-X Portal Frontend", "author": "Catena-X Contributors", "license": "Apache-2.0", @@ -71,6 +71,7 @@ "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^14.2.2", "@testing-library/user-event": "^14.5.2", + "@types/autosuggest-highlight": "^3.2.3", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.7", "@types/lodash.debounce": "^4.0.9", @@ -82,7 +83,6 @@ "@types/react-dom": "^18.2.22", "@types/react-redux": "^7.1.33", "@types/react-slick": "^0.23.13", - "@types/autosuggest-highlight": "^3.2.3", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", "@vitejs/plugin-react": "^4.2.1",