diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml new file mode 100644 index 000000000..1c9cfd7f6 --- /dev/null +++ b/.github/workflows/trufflehog.yml @@ -0,0 +1,60 @@ +############################################################### +# 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 +############################################################### + +name: 'TruffleHog' + +on: + push: + branches: ['main'] + pull_request: + # The branches below must be a subset of the branches above + branches: ['main'] + schedule: + - cron: '0 0 * * *' # Once a day + workflow_dispatch: + +permissions: + actions: read + contents: read + security-events: write + id-token: write + issues: write + +jobs: + ScanSecrets: + name: Scan secrets + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 # Ensure full clone for pull request workflows + + - name: TruffleHog OSS + id: trufflehog + uses: trufflesecurity/trufflehog@7e78ca385fb82c19568c7a4b341c97d57d9aa5e1 #v3.82.2 + continue-on-error: true + with: + path: ./ # Scan the entire repository + base: '${{ github.event.repository.default_branch }}' # Set base branch for comparison (pull requests) + extra_args: --filter-entropy=4 --results=verified,unknown --debug + + - name: Scan Results Status + if: steps.trufflehog.outcome == 'failure' + run: exit 1 # Set workflow run to failure if TruffleHog finds secrets diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa1391c0..22da9cd5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,25 @@ # Changelog +## (unreleased) 2.2.0-RC3 + ## Unreleased +### Bugfixes + +- **Service Release Process** + - Updated description regex & validation error message to prevent only spaces in short & long description +- **OSP Consent form** + - Fixed missing street number in form [#1081](https://github.com/eclipse-tractusx/portal-frontend/issues/1081) +- **App and Service Admin board** + - Fix data disappearing issue on re-clicking active tab/filter in app/service admin board [#1100](https://github.com/eclipse-tractusx/portal-frontend/pull/1100) +- **App & Service Subcription Managment** + - Search place holder text updated + - Show only available offer names at the top +- **Admin Service Management** + - Resolved service title overlap and improved responsiveness on the admin service detail page [#1112](https://github.com/eclipse-tractusx/portal-frontend/pull/1112) + +## Unreleased 2.2.0-RC3 + ### Feature - **Roles and Permissions** @@ -15,6 +33,7 @@ - **Company Subscriptions** - Update and bind API with filter options [#1062](https://github.com/eclipse-tractusx/portal-frontend/pull/1062) + - Add a tabular section to show up both app and service subscription data [#1101](https://github.com/eclipse-tractusx/portal-frontend/pull/1101) ### Bugfixes @@ -22,6 +41,7 @@ - fixed 400 Bad Request error in App Access Management -> Add Role search filter [#1057](https://github.com/eclipse-tractusx/portal-frontend/issues/1057) - **Connector Management** - fixed technical user selection + - updated inconsistent wording during connector deletion [#1098](https://github.com/eclipse-tractusx/portal-frontend/pull/1098) - **IDP management** - Fixed IDP management page title & description - **Technical User** @@ -37,10 +57,15 @@ - Fixed 'activeTab' conditions to load data for Tab-2(Registration Process) [#1050](https://github.com/eclipse-tractusx/portal-frontend/pull/1050) - **App Release Process**: - Fixed role upload does not work using Firefox [#1003](https://github.com/eclipse-tractusx/portal-frontend/pull/1003) + - Fixed "None" selection issue in Technical Integration -> App Release Process [#1036](https://github.com/eclipse-tractusx/portal-frontend/issues/1036) - **Technical User Management** - Display technicalUserManagement button based on role validation [#1073](https://github.com/eclipse-tractusx/portal-frontend/pull/1073) - **OSP Consent form** - Display invited company name in OSP consent form (Previously hard coded with 'BMW') [#1083](https://github.com/eclipse-tractusx/portal-frontend/pull/1083) + - Filter out roles not defined by OSP [#1114](https://github.com/eclipse-tractusx/portal-frontend/pull/1114) + - Fix OSP consent form 400 submission error [#1102](https://github.com/eclipse-tractusx/portal-frontend/pull/1102/files) +- **Use Case participation** + - Removes use cases without verified credentials from the "Use Case Participation" list [#1088](https://github.com/eclipse-tractusx/portal-frontend/pull/1088) - **User Management** - Fixed special characters in user management email filters [#1128](https://github.com/eclipse-tractusx/portal-frontend/issues/1128) diff --git a/DEPENDENCIES b/DEPENDENCIES index c17562ff9..c3d4f373d 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.5.1, Apache-2.0 AND CC-BY-4.0 AND OFL-1.1, approved, #16079 +npm/npmjs/@catena-x/portal-shared-components/3.6.1, Apache-2.0 AND CC-BY-4.0 AND OFL-1.1, approved, #16144 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 @@ -749,7 +749,7 @@ npm/npmjs/@types/hoist-non-react-statics/3.3.5, MIT, approved, clearlydefined npm/npmjs/@types/istanbul-lib-coverage/2.0.6, MIT, approved, clearlydefined npm/npmjs/@types/istanbul-lib-report/3.0.3, MIT, approved, clearlydefined npm/npmjs/@types/istanbul-reports/3.0.4, MIT, approved, clearlydefined -npm/npmjs/@types/jest/29.5.12, MIT, approved, #11951 +npm/npmjs/@types/jest/29.5.13, MIT, approved, #11951 npm/npmjs/@types/jsdom/20.0.1, MIT, approved, clearlydefined npm/npmjs/@types/json-schema/7.0.15, MIT, approved, clearlydefined npm/npmjs/@types/json5/0.0.29, MIT, approved, clearlydefined diff --git a/package.json b/package.json index 5c8b81ff7..e314f5e9a 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ ] }, "dependencies": { - "@catena-x/portal-shared-components": "^3.5.1", + "@catena-x/portal-shared-components": "^3.6.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@hookform/error-message": "^2.0.1", @@ -72,7 +72,7 @@ "@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/jest": "^29.5.13", "@types/lodash": "^4.17.7", "@types/lodash.debounce": "^4.0.9", "@types/lodash.uniq": "^4.5.9", diff --git a/src/assets/locales/de/main.json b/src/assets/locales/de/main.json index 519096281..9517608c6 100644 --- a/src/assets/locales/de/main.json +++ b/src/assets/locales/de/main.json @@ -453,8 +453,8 @@ "technicalUser": "Technischer Benutzer", "unsubscribe": "Abbestellen", "table": { - "appIcon": "App-Symbol", - "appTitle": "App-Titel", + "appIcon": "Symbol", + "appTitle": "Titel", "status": "Status", "details": "Einzelheiten", "action": "Aktion" @@ -464,7 +464,8 @@ "active": "aktiv", "inactive": "inaktiv", "showAll": "Alles anzeigen" - } + }, + "searchName": "..search by company name here" }, "companySubscriptionsDetail": { "language": "Verfügbare App Sprachen", @@ -676,7 +677,7 @@ "title": "EDC-Connector Löschen", "description": "Möchten Sie den ausgewählten Connector wirklich löschen? Die Löschung kann nicht rückgängig gemacht werden.", "techUserdescription": "Möchten Sie den Connector wirklich löschen? Mit der Löschung wird der Connector automatisch vom Endpunkt „Connector Discovery“ gelöscht und das Selbstbeschreibungsdokument auf inaktiv gesetzt.\n Bitte beachten Sie, dass mit dem Connector ein technischer Benutzer verbunden ist. Der technische Benutzer kann gelöscht werden, wenn Sie das Kontrollkästchen unten aktivieren. Andernfalls wird nur die Zuordnung zwischen Connector und technischem Benutzer gelöscht.", - "techUserCheckBoxLabel": "Ja, ich möchte den Connector deaktivieren, wodurch automatisch auch der erwähnte technische Benutzer gelöscht wird.", + "techUserCheckBoxLabel": "Ja, ich möchte den Connector löschen, wodurch automatisch auch der erwähnte technische Benutzer gelöscht wird.", "techDetails": { "title": "Technical Details", "name": "Name", @@ -954,7 +955,10 @@ "spoc": "SPOC", "permission": "Berechtigungen", "companyServiceAccountTypeID": "Firmenservicekontotyp", - "authenticationType": "Authentifizierungstyp" + "authenticationType": "Authentifizierungstyp", + "userType": "Benutzertyp", + "serviceEndpoint": "Dienstendpunkt", + "missingInfoHint": "Diese Informationen stehen älteren Benutzern möglicherweise nicht zur Verfügung. Erstellen Sie den Benutzer neu, um die vollständigen Details zu erhalten." }, "status": { "ACTIVE": "AKTIV", @@ -1471,7 +1475,7 @@ "description": "Do you wan't to rather use the autosetup; connect your service to automate marketplace subscription build", "readMore": "Read More", "registerURL": "RegisterURL", - "search": "...search by entering the app name here.", + "search": "...search by entering the company name here.", "noDataMessage": "No data available", "subscriptionHeading": ">> there are already active subscriptions for the service {serviceName}", "subscribeSuccessMsg": "Subscribed Successfully", @@ -2327,7 +2331,9 @@ "alias": "Alias", "authMethod": "Auth Method", "progress": "Progress", - "action": "Action" + "action": "Action", + "ownership": "Ownership", + "userType": "Usertype" }, "actions": { "cancel": "Abbrechen", diff --git a/src/assets/locales/de/servicerelease.json b/src/assets/locales/de/servicerelease.json index e7810fd66..463e9f045 100644 --- a/src/assets/locales/de/servicerelease.json +++ b/src/assets/locales/de/servicerelease.json @@ -90,6 +90,7 @@ "maximum": "Maximum", "charactersAllowed": " characters allowed", "validCharactersIncludes": "Valid characters includes: ", + "spaceAloneNotAllowed": " and spaces alone are not allowed", "isMandatory": " is mandatory", "fileUploadIsMandatory": "File upload is mandatory", "error": { @@ -152,7 +153,7 @@ "description": "Do you wan't to rather use the autosetup; connect your service to automate marketplace subscription build", "readMore": "Read More", "registerURL": "RegisterURL", - "search": "...search by entering the service name here.", + "search": "...search by entering the company name here.", "noDataMessage": "No data available", "subscriptionHeading": ">> there are already active subscriptions for the service {serviceName}", "subscribeSuccessMsg": "Subscribed Successfully", diff --git a/src/assets/locales/en/main.json b/src/assets/locales/en/main.json index d259d062b..06080b3b2 100644 --- a/src/assets/locales/en/main.json +++ b/src/assets/locales/en/main.json @@ -449,8 +449,8 @@ "technicalUser": "Technical User", "unsubscribe": "Unsubscribe", "table": { - "appIcon": "App Icon", - "appTitle": "App title", + "appIcon": "Icon", + "appTitle": "Title", "status": "Status", "details": "Details", "action": "Action" @@ -460,7 +460,8 @@ "active": "active", "inactive": "inactive", "showAll": "show all" - } + }, + "searchName": "..search by company name here" }, "companySubscriptionsDetail": { "language": "Language", @@ -681,7 +682,7 @@ "title": "Delete Connector", "description": "Do you really want to delete the registered connector? Please note that the deletion is not reversable.", "techUserdescription": "Do you really want to delete the connector? With the deletion the connector will be automatically deleted from the „connector discovery“ endpoint as well as the self-description document will be set to inactive.\n\n Please note, the connector has a technical user connected - the technical user can be deleted if you select the checkbox below otherwise only the mapping between connector and technical user will be deleted.", - "techUserCheckBoxLabel": "Yes, I want to deactivate the connector which will automatically also delete the mentioned technical user.", + "techUserCheckBoxLabel": "Yes, I want to delete the connector which will automatically also delete the mentioned technical user.", "techDetails": { "title": "Technical Details", "name": "Name", @@ -959,7 +960,10 @@ "spoc": "SPOC", "permission": "Permission", "companyServiceAccountTypeID": "Company Service Account Type", - "authenticationType": "Authentication Type" + "authenticationType": "Authentication Type", + "userType": "User Type", + "serviceEndpoint": "Service Endpoint", + "missingInfoHint": "This information may not be available to older users. Recreate the user to get the complete details." }, "status": { "ACTIVE": "ACTIVE", @@ -1439,7 +1443,7 @@ "description": "Do you wan't to rather use the autosetup; connect your service to automate marketplace subscription build", "readMore": "Read More", "registerURL": "RegisterURL", - "search": "...search by entering the app name here.", + "search": "...search by entering the company name here.", "noDataMessage": "No data available", "subscriptionHeading": ">> there are already active subscriptions for the service {serviceName}", "subscribeSuccessMsg": "Subscribed Successfully", @@ -2302,7 +2306,9 @@ "alias": "Alias", "authMethod": "Auth Method", "progress": "Progress", - "action": "Action" + "action": "Action", + "ownership": "Ownership", + "userType": "Usertype" }, "actions": { "cancel": "Cancel", diff --git a/src/assets/locales/en/servicerelease.json b/src/assets/locales/en/servicerelease.json index cbd9b1577..2ff796b0b 100644 --- a/src/assets/locales/en/servicerelease.json +++ b/src/assets/locales/en/servicerelease.json @@ -90,6 +90,7 @@ "maximum": "Maximum", "charactersAllowed": " characters allowed", "validCharactersIncludes": "Valid characters includes: ", + "spaceAloneNotAllowed": " and spaces alone are not allowed", "isMandatory": " is mandatory", "fileUploadIsMandatory": "File upload is mandatory", "error": { @@ -152,7 +153,7 @@ "description": "Do you wan't to rather use the autosetup; connect your service to automate marketplace subscription build", "readMore": "Read More", "registerURL": "RegisterURL", - "search": "...search by entering the service name here.", + "search": "...search by entering the company name here.", "noDataMessage": "No data available", "subscriptionHeading": ">> there are already active subscriptions for the service {serviceName}", "subscribeSuccessMsg": "Subscribed Successfully", diff --git a/src/components/overlays/OSPRegister/OSPRegisterContent.tsx b/src/components/overlays/OSPRegister/OSPRegisterContent.tsx index 777ff067b..d93f0f1b3 100644 --- a/src/components/overlays/OSPRegister/OSPRegisterContent.tsx +++ b/src/components/overlays/OSPRegister/OSPRegisterContent.tsx @@ -338,7 +338,7 @@ const OSPRegisterForm = ({ variant: 'filled', clearText: 'clear', keyTitle: 'id', - onChangeItem: (value: ItemType): void => { + onChangeItem: (value): void => { updateData({ ...data, uniqueIds: [ diff --git a/src/components/overlays/UpdateCertificate/index.tsx b/src/components/overlays/UpdateCertificate/index.tsx index ee9684532..f876d08f1 100644 --- a/src/components/overlays/UpdateCertificate/index.tsx +++ b/src/components/overlays/UpdateCertificate/index.tsx @@ -259,7 +259,7 @@ export default function UpdateCertificate() { ) } onChangeItem={(e) => { - setSelectedCertificate(e.title) + setSelectedCertificate(e.title as string) }} keyTitle={'title'} disabled={certificatetypesArr.length === 1} diff --git a/src/components/pages/AdminBoardDetail/AdminBoardDetail.scss b/src/components/pages/AdminBoardDetail/AdminBoardDetail.scss index f1d394549..4e2bbce3f 100644 --- a/src/components/pages/AdminBoardDetail/AdminBoardDetail.scss +++ b/src/components/pages/AdminBoardDetail/AdminBoardDetail.scss @@ -17,34 +17,56 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ + +@import 'src/components/styles/_breakpoints'; +@import 'src/components/styles/_colors'; + .service-admin-board-detail { - min-width: 1440px; + max-width: 1000px; + margin: 60px auto 0; + width: calc(100% - 40px); + + @include media-breakpoint-up(xxl-sm) { + margin-top: 0; + width: 100%; + } + .service-back { height: 140px; - padding: 48px 256px 48px 160px; + padding: 48px 0px; + @include media-breakpoint-up(xxl-sm) { + margin-left: -75px; + } } .service-content { - padding: 0px 256px 0px 256px; .divider-height { height: 39px; } .service-board-header { - width: 928px; - height: 288px; display: flex; flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 48px; + + @include media-breakpoint-up(lg) { + min-width: 928px; + height: 288px; + gap: 56px; + } + .lead-image > img { border-radius: 16px; + max-width: 100%; width: 412px; - height: 288px; } .service-app-content { - padding: 68px 0 68px 56px; + flex: 1; } } .service-documents { padding: 10px 18px; - color: #0f71cb !important; + color: color('primary') !important; display: flex; } .document-button-link { @@ -54,7 +76,7 @@ cursor: pointer; line-height: 20px; padding: 0; - color: #0f71cb !important; + color: color('primary') !important; } } } diff --git a/src/components/pages/AppDetail/components/AppDetailHeader/index.tsx b/src/components/pages/AppDetail/components/AppDetailHeader/index.tsx index e148b7b9b..b596d03a0 100644 --- a/src/components/pages/AppDetail/components/AppDetailHeader/index.tsx +++ b/src/components/pages/AppDetail/components/AppDetailHeader/index.tsx @@ -37,7 +37,7 @@ import CommonService from 'services/CommonService' import type { UseCaseType } from 'features/appManagement/types' import { userHasPortalRole } from 'services/AccessService' import type { RootState } from 'features/store' - +import { resetDialog } from 'features/overlay/slice' export interface AppDetailHeaderProps { item: AppDetails } @@ -73,6 +73,12 @@ export default function AppDetailHeader({ item }: AppDetailHeaderProps) { } } + useEffect(() => { + return () => { + dispatch(resetDialog()) + } + }, [dispatch]) + useEffect(() => { if (isDialogConfirmed) { setButtonLabel(t('content.appdetail.requested')) diff --git a/src/components/pages/CompanyCertificates/UploadCompanyCerificate.tsx b/src/components/pages/CompanyCertificates/UploadCompanyCerificate.tsx index 10fb49b0d..9b274e966 100644 --- a/src/components/pages/CompanyCertificates/UploadCompanyCerificate.tsx +++ b/src/components/pages/CompanyCertificates/UploadCompanyCerificate.tsx @@ -155,8 +155,8 @@ export default function UploadCompanyCertificate({ 'content.companyCertificate.upload.certificateTypePlaceholder' ) } - onChangeItem={(e: CertificateTypes) => { - setSelectedCertificateType(e) + onChangeItem={(e) => { + setSelectedCertificateType(e as CertificateTypes) }} keyTitle={'certificateType'} disabled={certificateTypes?.length === 1} diff --git a/src/components/pages/CompanySubscriptions/CompanySubscriptionDetail.tsx b/src/components/pages/CompanySubscriptions/CompanySubscriptionDetail.tsx index 359b9b06a..e1c59960e 100644 --- a/src/components/pages/CompanySubscriptions/CompanySubscriptionDetail.tsx +++ b/src/components/pages/CompanySubscriptions/CompanySubscriptionDetail.tsx @@ -17,37 +17,75 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { BackButton } from '@catena-x/portal-shared-components' +import { BackButton, LogoGrayData } from '@catena-x/portal-shared-components' import { useLocation, useNavigate } from 'react-router-dom' import { useTranslation } from 'react-i18next' -import { Box } from '@mui/material' +import { Box, Typography } from '@mui/material' import { useFetchAppDetailsQuery, useFetchSubscriptionAppQuery, } from 'features/apps/apiSlice' import { PAGES } from 'types/Constants' +import './CompanySubscriptions.scss' +import { + useFetchServiceDetailsQuery, + useFetchSubscriptionServiceQuery, +} from 'features/serviceSubscription/serviceSubscriptionApiSlice' +import CommonService from 'services/CommonService' +import { useState, useEffect } from 'react' +import { getApiBase } from 'services/EnvironmentService' import CompanySubscriptionTechnical from './components/CompanySubscriptionTechnical' -import CompanySubscriptionContent from './components/CompanySubscriptionContent' import CompanySubscriptionHeader from './components/CompanySubscriptionHeader' -import CompanySubscriptionDocument from './components/CompanySubscriptionDocument' +import CompanySubscriptionContent from './components/CompanySubscriptionContent' import CompanySubscriptionPrivacy from './components/CompanySubscriptionPrivacyContent' -import './CompanySubscriptions.scss' +import CompanySubscriptionDocument from './components/CompanySubscriptionDocument' export default function CompanySubscriptionDetail() { const navigate = useNavigate() const { state: items } = useLocation() const { t } = useTranslation() + const id = items.row.offerId ?? ('' as string) + const subscriptionId = items.row.subscriptionId ?? ('' as string) + const { data: appData, error: appError } = useFetchSubscriptionAppQuery( + { appId: id, subscriptionId }, + { skip: items.service } + ) + const { data: serviceData, error: serviceError } = + useFetchSubscriptionServiceQuery( + { serviceId: id, subscriptionId }, + { skip: items.app } + ) + const { data: fetchAppsData } = useFetchAppDetailsQuery(id, { + skip: items.service, + }) + const { data: fetchServicessData } = useFetchServiceDetailsQuery(id, { + skip: items.app, + }) + const [docId, setDocId] = useState('') + + const data = items.app ? appData : serviceData + const fetchData = items.app ? fetchAppsData : fetchServicessData - const appId = items ? items.offerId : '' - const subscriptionId = items ? items.subscriptionId : '' + // To-Do fix the type issue with status and data from FetchBaseQueryError + // eslint-disable-next-line + const error: any = items.app ? appError : serviceError - // Prevent API call when appId does not exist - const { data } = appId - ? useFetchSubscriptionAppQuery({ appId, subscriptionId }) - : { data: undefined } - const { data: fetchAppsData } = appId - ? useFetchAppDetailsQuery(appId) - : { data: undefined } + useEffect(() => { + if (fetchData?.leadPictureId) { + const id = CommonService.isValidPictureId(fetchData?.leadPictureId) + setDocId(id) + } + }, [fetchData]) + + const getSrc = () => { + if (fetchData?.id && items.app && docId) + return `${getApiBase()}/api/apps/${fetchData.id}/appDocuments/${docId}` + if (fetchData?.id && items.service && docId) + return `${getApiBase()}/api/services/${ + fetchData.id + }/serviceDocuments/${docId}` + return LogoGrayData + } return (
@@ -61,12 +99,14 @@ export default function CompanySubscriptionDetail() { }} /> - {data && fetchAppsData && ( + {error && {error?.data?.title}} + + {data && fetchData && ( <> - - - - + + + + )} diff --git a/src/components/pages/CompanySubscriptions/CompanySubscriptionsTableColumns.tsx b/src/components/pages/CompanySubscriptions/CompanySubscriptionsTableColumns.tsx index 87029a91f..58b0008e1 100644 --- a/src/components/pages/CompanySubscriptions/CompanySubscriptionsTableColumns.tsx +++ b/src/components/pages/CompanySubscriptions/CompanySubscriptionsTableColumns.tsx @@ -43,7 +43,8 @@ import './CompanySubscriptions.scss' export const CompanySubscriptionsTableColumns = ( t: typeof i18next.t, - handleOverlay?: (row: SubscribedActiveApps, enable: boolean) => void + handleOverlay?: (row: SubscribedActiveApps, enable: boolean) => void, + currentActive?: number ): Array => { const navigate = useNavigate() @@ -86,6 +87,19 @@ export const CompanySubscriptionsTableColumns = ( ) } + const canShowButton = (row: SubscribedActiveApps) => + row.status === SubscriptionStatus.ACTIVE + + const getSource = (row: SubscribedActiveApps) => { + if (row.image && currentActive === 0) + return `${getApiBase()}/api/apps/${row.offerId}/appDocuments/${row.image}` + if (row.image && currentActive === 1) + return `${getApiBase()}/api/services/${row.offerId}/serviceDocuments/${ + row.image + }` + return LogoGrayData + } + return [ { field: 'image', @@ -96,13 +110,7 @@ export const CompanySubscriptionsTableColumns = ( disableColumnMenu: true, renderCell: ({ row }: { row: SubscribedActiveApps }) => ( { navigate( `/${PAGES.COMPANY_SUBSCRIPTIONS_DETAIL}/${row.offerId}`, - { state: row } + { + state: { + row, + app: currentActive === 0, + service: currentActive === 1, + }, + } ) }} > @@ -161,21 +175,20 @@ export const CompanySubscriptionsTableColumns = ( flex: 2, disableColumnMenu: true, sortable: false, - renderCell: ({ row }: { row: SubscribedActiveApps }) => - row.status === SubscriptionStatus.ACTIVE && ( - - ), + renderCell: ({ row }: { row: SubscribedActiveApps }) => ( + + ), }, ] } diff --git a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionContent/index.tsx b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionContent/index.tsx index d984d8c3a..624e13e84 100644 --- a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionContent/index.tsx +++ b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionContent/index.tsx @@ -22,11 +22,12 @@ import { Box, useMediaQuery, useTheme } from '@mui/material' import { ImageGallery, Typography } from '@catena-x/portal-shared-components' import { type AppDetails } from 'features/apps/types' import CommonService from 'services/CommonService' +import { type ServiceDetailsResponse } from 'features/serviceSubscription/serviceSubscriptionApiSlice' export default function CompanySubscriptionContent({ detail, }: Readonly<{ - detail: AppDetails + detail: AppDetails | ServiceDetailsResponse }>) { const { t } = useTranslation() const theme = useTheme() @@ -44,14 +45,16 @@ export default function CompanySubscriptionContent({ - + {detail?.images && ( + + )} ) diff --git a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionDocument/index.tsx b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionDocument/index.tsx index c996573c0..514496568 100644 --- a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionDocument/index.tsx +++ b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionDocument/index.tsx @@ -26,11 +26,12 @@ import { useFetchDocumentByIdMutation } from 'features/apps/apiSlice' import { type AppDetails, type Documents } from 'features/apps/types' import { download } from 'utils/downloadUtils' import 'components/styles/document.scss' +import { type ServiceDetailsResponse } from 'features/serviceSubscription/serviceSubscriptionApiSlice' export default function CompanySubscriptionDocument({ detail, }: Readonly<{ - detail: AppDetails + detail: AppDetails | ServiceDetailsResponse }>) { const { t } = useTranslation() diff --git a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionHeader/index.tsx b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionHeader/index.tsx index 57ed54406..3523d206e 100644 --- a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionHeader/index.tsx +++ b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionHeader/index.tsx @@ -28,26 +28,18 @@ import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline' import UnpublishedIcon from '@mui/icons-material/Unpublished' import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty' import { useTranslation } from 'react-i18next' -import { useEffect, useState } from 'react' import { type AppDetails, SubscriptionStatus } from 'features/apps/types' -import CommonService from 'services/CommonService' import { fetchImageWithToken } from 'services/ImageService' -import { getApiBase } from 'services/EnvironmentService' +import { type ServiceDetailsResponse } from 'features/serviceSubscription/serviceSubscriptionApiSlice' export default function CompanySubscriptionHeader({ detail, + src, }: Readonly<{ - detail: AppDetails + detail: AppDetails | ServiceDetailsResponse + src: string }>) { const { t } = useTranslation() - const [docId, setDocId] = useState('') - - useEffect(() => { - if (detail.leadPictureId) { - const id = CommonService.isValidPictureId(detail.leadPictureId) - setDocId(id) - } - }, [detail]) const renderStatusButton = (status: string) => { if (status === SubscriptionStatus.ACTIVE) @@ -102,11 +94,7 @@ export default function CompanySubscriptionHeader({
{detail.title} @@ -119,7 +107,7 @@ export default function CompanySubscriptionHeader({ {t('content.companySubscriptionsDetail.language')}: - {detail.languages.length + {detail?.languages?.length ? detail.languages.map((lang, index) => ( {` ${index ? ', ' : ''}${lang.toUpperCase()} `} diff --git a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionPrivacyContent/index.tsx b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionPrivacyContent/index.tsx index f57347048..265d7f2d2 100644 --- a/src/components/pages/CompanySubscriptions/components/CompanySubscriptionPrivacyContent/index.tsx +++ b/src/components/pages/CompanySubscriptions/components/CompanySubscriptionPrivacyContent/index.tsx @@ -24,6 +24,7 @@ import { Typography } from '@catena-x/portal-shared-components' import { PrivacyPolicyType } from 'features/adminBoard/adminBoardApiSlice' import { type AppDetails } from 'features/apps/types' import './CompanySubscriptionPrivacyContent.scss' +import { type ServiceDetailsResponse } from 'features/serviceSubscription/serviceSubscriptionApiSlice' const policyIcons = { [PrivacyPolicyType.COMPANY_DATA]: Apartment, @@ -36,7 +37,7 @@ const policyIcons = { export default function CompanySubscriptionPrivacy({ detail, }: Readonly<{ - detail: AppDetails + detail: AppDetails | ServiceDetailsResponse }>) { const { t } = useTranslation('', { keyPrefix: 'content.companySubscriptionsDetail.privacy', @@ -58,7 +59,7 @@ export default function CompanySubscriptionPrivacy({
{detail?.privacyPolicies?.length ? (
- {detail.privacyPolicies.map((policy: PrivacyPolicyType) => ( + {detail?.privacyPolicies?.map((policy: PrivacyPolicyType) => ( ) { const { t } = useTranslation() diff --git a/src/components/pages/CompanySubscriptions/index.tsx b/src/components/pages/CompanySubscriptions/index.tsx index 9c609877f..e41e53bc5 100644 --- a/src/components/pages/CompanySubscriptions/index.tsx +++ b/src/components/pages/CompanySubscriptions/index.tsx @@ -19,6 +19,9 @@ import { PageLoadingTable, + Tab, + TabPanel, + Tabs, Typography, } from '@catena-x/portal-shared-components' import { useTranslation } from 'react-i18next' @@ -26,7 +29,7 @@ import { useFetchSubscribedActiveAppsStatusQuery, useUnsubscribeAppMutation, } from 'features/apps/apiSlice' -import { useEffect, useState } from 'react' +import { type SyntheticEvent, useEffect, useState } from 'react' import { isName } from 'types/Patterns' import { useDispatch, useSelector } from 'react-redux' import { setSearchInput } from 'features/appManagement/actions' @@ -38,6 +41,11 @@ import { type SubscribedActiveApps, } from 'features/apps/types' import { CompanySubscriptionsTableColumns } from './CompanySubscriptionsTableColumns' +import { + useFetchCompanyServiceSubscriptionsQuery, + useUnsubscribeServiceMutation, +} from 'features/serviceSubscription/serviceSubscriptionApiSlice' +import { Box } from '@mui/material' interface FetchHookArgsType { statusId: string @@ -62,8 +70,10 @@ export default function CompanySubscriptions() { const [loading, setLoading] = useState(false) const [appId, setAppId] = useState('') const [subscriptionId, setSubscriptionId] = useState('') - const [unsubscribeMutation] = useUnsubscribeAppMutation() + const [unsubscribeAppMutation] = useUnsubscribeAppMutation() + const [unsubscribeServiceMutation] = useUnsubscribeServiceMutation() const [enableErrorMessage, setEnableErrorMessage] = useState(false) + const [currentActive, setCurrentActive] = useState(0) const setView = (e: React.MouseEvent) => { const viewValue = e.currentTarget.value @@ -72,6 +82,13 @@ export default function CompanySubscriptions() { setRefresh(Date.now()) } + const handleTabChange = ( + _e: SyntheticEvent, + value: number + ) => { + setCurrentActive(value) + } + const filterView = [ { buttonText: t('content.companySubscriptions.filter.pending'), @@ -108,16 +125,24 @@ export default function CompanySubscriptions() { return validateExpr } + const callSuccess = () => { + success(t('content.organization.unsubscribe.unsubscribeSuccess')) + setLoading(false) + setShowUnsubscribeOverlay(false) + setEnableErrorMessage(false) + setRefresh(Date.now()) + } + const onUnsubscribeSubmit = async () => { setLoading(true) - await unsubscribeMutation(subscriptionId) + await ( + currentActive === 0 + ? unsubscribeAppMutation(subscriptionId) + : unsubscribeServiceMutation(subscriptionId) + ) .unwrap() .then(() => { - success(t('content.organization.unsubscribe.unsubscribeSuccess')) - setLoading(false) - setShowUnsubscribeOverlay(false) - setEnableErrorMessage(false) - setRefresh(Date.now()) + callSuccess() }) .catch(() => { setLoading(false) @@ -133,7 +158,69 @@ export default function CompanySubscriptions() { const companySubscriptionsCols = CompanySubscriptionsTableColumns( t, - handleOverlay + handleOverlay, + currentActive + ) + + const getIcon = (num: number) => { + return ( + + {num} + + ) + } + + // Add an ESLint exception until there is a solution + // eslint-disable-next-line + const renderTable = (query: any) => ( +
+ + sx={{ + '.MuiDataGrid-cell': { + alignContent: 'center !important', + }, + }} + autoFocus={false} + searchExpr={searchExpr} + alignCell="start" + defaultFilter={group} + filterViews={filterView} + toolbarVariant={'searchAndFilter'} + hasBorder={false} + columnHeadersBackgroundColor={'transparent'} + searchPlaceholder={t('content.companySubscriptions.searchName')} + searchInputData={searchInputData} + onSearch={(expr: string) => { + if (expr !== '' && !onValidate(expr)) return + setRefresh(Date.now()) + setSearchExpr(expr) + }} + searchDebounce={1000} + title={''} + loadLabel={t('global.actions.more')} + fetchHook={query} + fetchHookArgs={fetchHookArgs} + fetchHookRefresh={refresh} + getRowId={(row: { [key: string]: string }) => row.offerId} + columns={companySubscriptionsCols} + /> +
) return ( @@ -151,6 +238,7 @@ export default function CompanySubscriptions() { appId={appId} subscriptionId={subscriptionId} enableErrorMessage={enableErrorMessage} + activeTab={currentActive} /> )} {t('content.companySubscriptions.headertitle')} - -
- - sx={{ - '.MuiDataGrid-cell': { - alignContent: 'center !important', - }, - }} - autoFocus={false} - searchExpr={searchExpr} - alignCell="start" - defaultFilter={group} - filterViews={filterView} - toolbarVariant={'searchAndFilter'} - hasBorder={false} - columnHeadersBackgroundColor={'transparent'} - searchPlaceholder={t('global.table.searchName')} - searchInputData={searchInputData} - onSearch={(expr: string) => { - if (!onValidate(expr)) return - setRefresh(Date.now()) - setSearchExpr(expr) - }} - searchDebounce={1000} - title={''} - loadLabel={t('global.actions.more')} - fetchHook={useFetchSubscribedActiveAppsStatusQuery} - fetchHookArgs={fetchHookArgs} - fetchHookRefresh={refresh} - getRowId={(row: { [key: string]: string }) => row.subscriptionId} - columns={companySubscriptionsCols} - /> -
+ + + + + + + {renderTable(useFetchSubscribedActiveAppsStatusQuery)} + + + {renderTable(useFetchCompanyServiceSubscriptionsQuery)} + +
) } diff --git a/src/components/pages/OSPConsent/CompanyDetails.tsx b/src/components/pages/OSPConsent/CompanyDetails.tsx index 4987cd026..b96000ed9 100644 --- a/src/components/pages/OSPConsent/CompanyDetails.tsx +++ b/src/components/pages/OSPConsent/CompanyDetails.tsx @@ -35,6 +35,7 @@ import { type companyRole, useFetchAgreementConsentsQuery, type AgreementData, + type SubmitData, } from 'features/registration/registrationApiSlice' import { getApiBase } from 'services/EnvironmentService' import UserService from 'services/UserService' @@ -46,6 +47,7 @@ interface CompanyDetailsProps { loading: boolean handleSubmit: () => void submitError: boolean + updateConsents: (data: SubmitData) => void } export const CompanyDetails = ({ @@ -53,6 +55,7 @@ export const CompanyDetails = ({ loading, handleSubmit, submitError, + updateConsents, }: CompanyDetailsProps) => { const tm = useTranslation().t const { t } = useTranslation('registration') @@ -76,6 +79,13 @@ export const CompanyDetails = ({ applicationId ?? '' ) + useEffect(() => { + updateConsents({ + roles: companyRoleChecked, + consents: agreementChecked, + }) + }, [companyRoleChecked, agreementChecked]) + useEffect(() => { updateSelectedRolesAndAgreement() }, [consentData]) @@ -209,7 +219,10 @@ export const CompanyDetails = ({ const tableData: TableType = { head: [t('osp.companyName'), companyDetails?.name ?? ''], body: [ - [t('osp.street'), companyDetails?.streetName ?? ''], + [ + t('osp.street'), + `${companyDetails?.streetName ?? ''} ${companyDetails?.streetNumber ?? ''}`, + ], [t('osp.zip'), companyDetails?.zipCode ?? ''], [t('osp.city'), companyDetails?.city ?? ''], [t('osp.region'), companyDetails?.region ?? ''], @@ -237,37 +250,45 @@ export const CompanyDetails = ({ {t('osp.companyRole.subTitle')}
- {allConsentData?.companyRoles.map((role) => ( -
-
-
- { - handleCompanyRoleCheck(role.companyRole) - }} - checked={companyRoleChecked?.[role.companyRole]} - /> -
-
- - {t(`osp.companyRole.${role.companyRole}`)} - - - {role.descriptions['en' as keyof typeof role.descriptions]} - - {companyRoleChecked?.[role.companyRole] && - renderTermsSection(role)} + {allConsentData?.companyRoles + .filter((role) => + consentData?.companyRoles.includes(role.companyRole) + ) + .map((role) => ( +
+
+
+ { + handleCompanyRoleCheck(role.companyRole) + }} + checked={companyRoleChecked?.[role.companyRole]} + /> +
+
+ + {t(`osp.companyRole.${role.companyRole}`)} + + + { + role.descriptions[ + 'en' as keyof typeof role.descriptions + ] + } + + {companyRoleChecked?.[role.companyRole] && + renderTermsSection(role)} +
-
- ))} + ))}
{submitError && ( diff --git a/src/components/pages/OSPConsent/index.tsx b/src/components/pages/OSPConsent/index.tsx index 5a760479b..d8f31ba9c 100644 --- a/src/components/pages/OSPConsent/index.tsx +++ b/src/components/pages/OSPConsent/index.tsx @@ -26,6 +26,7 @@ import { useFetchAgreementConsentsQuery, useUpdateAgreementConsentsMutation, CONSENT_STATUS, + type SubmitData, } from 'features/registration/registrationApiSlice' import './style.scss' import { SuccessRegistration } from './SuccessRegistration' @@ -78,6 +79,11 @@ export const OSPConsent = () => { ) } + const setConsents = (data: SubmitData) => { + setCompanyRoleChecked(data.roles) + setAgreementChecked(data.consents) + } + const handleSubmit = async () => { setLoading(true) const companyRoles = Object.keys(companyRoleChecked).filter( @@ -131,6 +137,7 @@ export const OSPConsent = () => { loading={loading} handleSubmit={handleSubmit} submitError={submitError} + updateConsents={setConsents} /> ) } diff --git a/src/components/pages/Organization/UnSubscribeOverlay.tsx b/src/components/pages/Organization/UnSubscribeOverlay.tsx index c7e18e135..2fb218a49 100644 --- a/src/components/pages/Organization/UnSubscribeOverlay.tsx +++ b/src/components/pages/Organization/UnSubscribeOverlay.tsx @@ -34,10 +34,12 @@ import { import Box from '@mui/material/Box' import { useFetchSubscriptionAppQuery } from 'features/apps/apiSlice' import './Organization.scss' +import { type SubscribeTechnicalUserData } from 'features/apps/types' import { - type SubscribeTechnicalUserData, - SubscriptionStatus, -} from 'features/apps/types' + OfferSubscriptionStatus, + useFetchSubscriptionServiceQuery, +} from 'features/serviceSubscription/serviceSubscriptionApiSlice' +import LoadingProgress from 'components/shared/basic/LoadingProgress' interface UnSubscribeOverlayProps { openDialog?: boolean @@ -47,6 +49,7 @@ interface UnSubscribeOverlayProps { subscriptionId: string appId: string enableErrorMessage: boolean + activeTab: number } const UnSubscribeOverlay = ({ @@ -57,10 +60,35 @@ const UnSubscribeOverlay = ({ subscriptionId, appId, enableErrorMessage, + activeTab, }: UnSubscribeOverlayProps) => { const { t } = useTranslation() - const { data } = useFetchSubscriptionAppQuery({ appId, subscriptionId }) + const { + data: appData, + error: appError, + isFetching: isAppFetching, + } = useFetchSubscriptionAppQuery( + { appId, subscriptionId }, + { skip: activeTab === 1 } + ) + const { + data: serviceData, + error: serviceError, + isFetching: isServiceFetching, + } = useFetchSubscriptionServiceQuery( + { serviceId: appId, subscriptionId }, + { skip: activeTab === 0 } + ) const [checkBoxSelected, setCheckBoxSelected] = useState(false) + + const data = activeTab === 0 ? appData : serviceData + + const isFetching: boolean = isAppFetching ?? isServiceFetching + + // To-Do fix the type issue with status and data from FetchBaseQueryError + // eslint-disable-next-line + const error: any = activeTab === 0 ? appError : serviceError + return (
- - {t('content.organization.unsubscribe.descriptionNote')} - - - {t('content.organization.unsubscribe.description')} - - - - userdata.name - ) - .toString() ?? '', - ], - ], - }} - horizontal={false} - /> - - - { - setCheckBoxSelected(!checkBoxSelected) - }} - /> - - {t('content.organization.unsubscribe.checkBoxLabelDescription')} + + {t('content.organization.unsubscribe.descriptionNote')} + + {t('content.organization.unsubscribe.description')} + +
+ + {isFetching ? ( +
+ +
+ ) : ( + <> + {error ? ( + + {error.data.title} + + ) : ( + <> + + userdata.name + ) + .toString() ?? '', + ], + ], + }} + horizontal={false} + /> + + { + setCheckBoxSelected(!checkBoxSelected) + }} + /> + + {t( + 'content.organization.unsubscribe.checkBoxLabelDescription' + )} + + + + )} + + )}
@@ -179,7 +244,7 @@ const UnSubscribeOverlay = ({ ) : (
diff --git a/src/components/shared/templates/StaticTemplateResponsive/Cards/RenderImage.tsx b/src/components/shared/templates/StaticTemplateResponsive/Cards/RenderImage.tsx index f72101790..386f83e3a 100644 --- a/src/components/shared/templates/StaticTemplateResponsive/Cards/RenderImage.tsx +++ b/src/components/shared/templates/StaticTemplateResponsive/Cards/RenderImage.tsx @@ -20,8 +20,8 @@ import { ImageItem } from '@catena-x/portal-shared-components' import { useMediaQuery } from '@mui/material' +import { type CSSProperties } from 'react' import '../StaticTemplate.scss' -import { type SxProps } from '@mui/system' export default function RenderImage({ url, @@ -30,7 +30,7 @@ export default function RenderImage({ width, }: Readonly<{ url: string - additionalStyles?: SxProps + additionalStyles?: CSSProperties height?: string width?: string }>) { diff --git a/src/components/shared/templates/Subscription/index.tsx b/src/components/shared/templates/Subscription/index.tsx index 10b617e77..29380105b 100644 --- a/src/components/shared/templates/Subscription/index.tsx +++ b/src/components/shared/templates/Subscription/index.tsx @@ -304,15 +304,6 @@ export default function Subscription({ appFiltersData = data?.content } - useEffect(() => { - if (appFiltersData?.length) { - setState({ - type: ActionKind.SET_APP_FILTERS, - payload: appFiltersData, - }) - } - }, [appFiltersData, type]) - const { data, refetch, @@ -331,6 +322,25 @@ export default function Subscription({ } }, [data]) + useEffect(() => { + if (data?.content && appFiltersData) { + const fillers: AppFiltersResponse[] = [] + appFiltersData.forEach((item) => { + data.content.forEach((base: { offerId: string }) => { + if (base.offerId === item.id) { + fillers.push(item) + } + }) + }) + if (fillers?.length) { + setState({ + type: ActionKind.SET_APP_FILTERS, + payload: fillers, + }) + } + } + }, [appFiltersData, data]) + const setView = (e: React.MouseEvent) => { let status = '' if (e.currentTarget.value === FilterType.REQUEST) { diff --git a/src/components/styles/_breakpoints.scss b/src/components/styles/_breakpoints.scss index d79f12826..9564890c6 100644 --- a/src/components/styles/_breakpoints.scss +++ b/src/components/styles/_breakpoints.scss @@ -24,6 +24,7 @@ $breakpoints: ( md: 627px, lg: 1056px, xl: 1312px, + xxl-sm: 1500px, xxl: 1584px, ); diff --git a/src/features/admin/serviceApiSlice.ts b/src/features/admin/serviceApiSlice.ts index 2df1ec815..6a9f18203 100644 --- a/src/features/admin/serviceApiSlice.ts +++ b/src/features/admin/serviceApiSlice.ts @@ -54,12 +54,18 @@ export enum ServiceAccountStatus { PENDING_DELETION = 'PENDING_DELETION', } +export enum UserType { + INTERNAL = 'internal', + EXTERNAL = 'external', +} + export interface ServiceAccountListEntry { serviceAccountId: string clientId: string name: string status: ServiceAccountStatus isOwner?: boolean + usertype: UserType offer?: { name?: string } @@ -78,6 +84,8 @@ export interface ServiceAccountDetail extends ServiceAccountListEntry { connector: ConnectedObject offer: ConnectedObject companyServiceAccountTypeId: companyServiceAccountType + usertype: UserType + authenticationServiceUrl: string } export type AppRoleCreate = { diff --git a/src/features/apps/apiSlice.ts b/src/features/apps/apiSlice.ts index 99393e63d..a6ef97de1 100644 --- a/src/features/apps/apiSlice.ts +++ b/src/features/apps/apiSlice.ts @@ -26,7 +26,6 @@ import { import i18next from 'i18next' import { getApiBase } from 'services/EnvironmentService' import { apiBaseQuery } from 'utils/rtkUtil' -import { PAGE_SIZE } from 'types/Constants' import { type AppDetails, type AppMarketplaceApp, @@ -38,12 +37,13 @@ import { type SubscriptionAppRequest, type AgreementRequest, type ActiveSubscription, - type ActiveSubscriptionDetails, type FetchSubscriptionAppQueryType, type SubscribedActiveApps, StatusIdEnum, + type FetchSubscriptionResponseType, CompanySubscriptionFilterType, } from './types' +import { PAGE_SIZE } from 'types/Constants' export const apiSlice = createApi({ reducerPath: 'rtk/apps/marketplace', @@ -149,11 +149,15 @@ export const apiSlice = createApi({ query: () => '/api/apps/subscribed/activesubscriptions', }), fetchSubscriptionApp: builder.query< - ActiveSubscriptionDetails, + FetchSubscriptionResponseType, FetchSubscriptionAppQueryType >({ query: (obj) => `/api/apps/${obj.appId}/subscription/${obj.subscriptionId}/subscriber`, + transformErrorResponse: (res) => ({ + status: res.status, + data: res.data, + }), }), unsubscribeApp: builder.mutation({ query: (subscriptionId) => ({ @@ -165,14 +169,18 @@ export const apiSlice = createApi({ PaginResult, PaginFetchArgs >({ - query: (fetchArgs) => { - if ( - fetchArgs.args.statusId && - fetchArgs.args.statusId !== CompanySubscriptionFilterType.SHOW_ALL - ) { - return `/api/Apps/subscribed/subscription-status?size=${PAGE_SIZE}&page=${fetchArgs.page}&statusId=${fetchArgs.args.statusId}` - } else { - return `/api/Apps/subscribed/subscription-status?size=${PAGE_SIZE}&page=${fetchArgs.page}` + query: (body) => { + const url = `/api/apps/subscribed/subscription-status?size=${PAGE_SIZE}&page=${body.page}` + const statusId = + body.args.statusId && + body.args.statusId !== CompanySubscriptionFilterType.SHOW_ALL + ? `&status=${body.args.statusId}` + : '' + const companyName = body.args.expr + ? `&companyName=${body.args.expr}` + : '' + return { + url: `${url}${statusId}${companyName}`, } }, }), diff --git a/src/features/apps/types.ts b/src/features/apps/types.ts index 899e1a203..33d58abae 100644 --- a/src/features/apps/types.ts +++ b/src/features/apps/types.ts @@ -20,6 +20,10 @@ import type { CardItems } from '@catena-x/portal-shared-components' import type { PrivacyPolicyType } from 'features/adminBoard/adminBoardApiSlice' import type { UseCaseType } from 'features/appManagement/types' +import { + type OfferSubscriptionStatus, + type OfferSubscriptionDataType, +} from 'features/serviceSubscription/serviceSubscriptionApiSlice' export type ImageType = { src: string @@ -150,6 +154,7 @@ export type AppDetails = AppMarketplaceApp & { technicalUserProfile?: { [key: string]: string[] | null } + offerSubscriptionDetailData?: OfferSubscriptionDataType[] } export type Documents = { @@ -251,9 +256,9 @@ export const initialState: AppsControlState = { } export enum CompanySubscriptionFilterType { - PENDING = 'pending', - ACTIVE = 'active', - INACTIVE = 'inactive', + PENDING = 'PENDING', + ACTIVE = 'ACTIVE', + INACTIVE = 'INACTIVE', SHOW_ALL = 'show all', } @@ -273,3 +278,13 @@ export enum StatusIdEnum { WIP = 'WIP', All = 'All', } + +export interface FetchSubscriptionResponseType { + id: string + offerSubscriptionStatus: OfferSubscriptionStatus + name: string + provider: string + contact: string[] + technicalUserData: SubscribeTechnicalUserData[] + connectorData: SubscribeConnectorData[] +} diff --git a/src/features/companyCertification/companyCertificateApiSlice.tsx b/src/features/companyCertification/companyCertificateApiSlice.tsx index 4774cc17b..8ac140267 100644 --- a/src/features/companyCertification/companyCertificateApiSlice.tsx +++ b/src/features/companyCertification/companyCertificateApiSlice.tsx @@ -45,7 +45,7 @@ export interface UploadDocumentType { expiryDate: string | undefined } -export interface CertificateTypes { +export type CertificateTypes = { certificateType: string certificateVersion: string } diff --git a/src/features/overlay/slice.ts b/src/features/overlay/slice.ts index 6483b0122..9bc30780f 100644 --- a/src/features/overlay/slice.ts +++ b/src/features/overlay/slice.ts @@ -43,6 +43,7 @@ const dialog = createSlice({ }, resetDialog: (state) => { state.isConfirmed = false + state.isOpen = false }, }, }) diff --git a/src/features/registration/registrationApiSlice.ts b/src/features/registration/registrationApiSlice.ts index 21e6b3150..44208c927 100644 --- a/src/features/registration/registrationApiSlice.ts +++ b/src/features/registration/registrationApiSlice.ts @@ -111,6 +111,11 @@ export type companyRole = { descriptions: { de: string; en: string } } +export type SubmitData = { + roles: { [key: string]: boolean } + consents: { [key: string]: boolean } +} + export enum CONSENT_STATUS { ACTIVE = 'ACTIVE', INACTIVE = 'INACTIVE', diff --git a/src/features/serviceSubscription/serviceSubscriptionApiSlice.ts b/src/features/serviceSubscription/serviceSubscriptionApiSlice.ts index eeb95ca87..adc4524b6 100644 --- a/src/features/serviceSubscription/serviceSubscriptionApiSlice.ts +++ b/src/features/serviceSubscription/serviceSubscriptionApiSlice.ts @@ -18,8 +18,21 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +import { + type PaginFetchArgs, + type PaginResult, +} from '@catena-x/portal-shared-components' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import { type PrivacyPolicyType } from 'features/adminBoard/adminBoardApiSlice' +import { + CompanySubscriptionFilterType, + type Documents, + type FetchSubscriptionResponseType, + type SubscribedActiveApps, +} from 'features/apps/types' import { type ProcessStep } from 'features/appSubscription/appSubscriptionApiSlice' +import i18next from 'i18next' +import { PAGE_SIZE } from 'types/Constants' import { apiBaseQuery } from 'utils/rtkUtil' export interface SubscriptionRequestType { @@ -38,7 +51,7 @@ export interface SubscriptionResponseContentType { export type SubscriptionContent = { offerId: string - serviceName: string + offerName: string companySubscriptionStatuses: SubscriptionResponseContentType[] } @@ -90,6 +103,54 @@ export type SubscriptionDetailResponse = { processStepTypeId?: ProcessStep } +export enum LicenseType { + COTS = 'COTS', + FOSS = 'FOSS', +} + +export enum OfferSubscriptionStatus { + PENDING = 'PENDING', + ACTIVE = 'ACTIVE', + INACTIVE = 'INACTIVE', +} + +export interface OfferSubscriptionDataType { + offerSubscriptionId: string + offerSubscriptionStatus: OfferSubscriptionStatus +} + +export enum ServiceTypes { + DATASPACE_SERVICE = 'DATASPACE_SERVICE', + CONSULTANCY_SERVICE = 'CONSULTANCY_SERVICE', +} + +export interface ServiceDetailsResponse { + id: string + title: string + provider: string + contactEmail: string + description: string + licenseType: LicenseType + price: string + offerSubscriptionDetailData: OfferSubscriptionDataType[] + serviceTypes: ServiceTypes + technicalUserProfile: { + [key: string]: string[] | null + } + leadPictureId?: string + isSubscribed: string + longDescription: string + languages: string[] + images: string[] + privacyPolicies: PrivacyPolicyType[] + documents: Documents +} + +export interface SubscriptionServiceRequestType { + serviceId: string + subscriptionId: string +} + export const apiSlice = createApi({ reducerPath: 'rtk/services/serviceSubscription', baseQuery: fetchBaseQuery(apiBaseQuery()), @@ -121,7 +182,49 @@ export const apiSlice = createApi({ SubscriptionDetailRequestBody >({ query: (body) => - `/api/Services/${body.appId}/subscription/${body.subscriptionId}/provider`, + `/api/services/${body.appId}/subscription/${body.subscriptionId}/provider`, + }), + fetchSubscriptionService: builder.query< + FetchSubscriptionResponseType, + SubscriptionServiceRequestType + >({ + query: (obj) => + `/api/services/${obj.serviceId}/subscription/${obj.subscriptionId}/subscriber`, + transformErrorResponse: (res) => { + return { + status: res.status, + data: res.data, + } + }, + }), + fetchServiceDetails: builder.query({ + query: (id: string) => `/api/services/${id}?lang=${i18next.language}`, + }), + fetchCompanyServiceSubscriptions: builder.query< + PaginResult, + PaginFetchArgs + >({ + query: (body) => { + console.log(body) + const url = `/api/services/subscribed/subscription-status?size=${PAGE_SIZE}&page=${body.page}` + const statusId = + body.args.statusId && + body.args.statusId !== CompanySubscriptionFilterType.SHOW_ALL + ? `&status=${body.args.statusId}` + : '' + const companyName = body.args.expr + ? `&companyName=${body.args.expr}` + : '' + return { + url: `${url}${statusId}${companyName}`, + } + }, + }), + unsubscribeService: builder.mutation({ + query: (subscriptionId) => ({ + url: `/api/services/${subscriptionId}/unsubscribe`, + method: 'PUT', + }), }), }), }) @@ -130,4 +233,8 @@ export const { useFetchServiceSubscriptionsQuery, useFetchServiceFiltersQuery, useFetchServiceSubDetailQuery, + useFetchSubscriptionServiceQuery, + useFetchServiceDetailsQuery, + useFetchCompanyServiceSubscriptionsQuery, + useUnsubscribeServiceMutation, } = apiSlice diff --git a/src/types/MainTypes.ts b/src/types/MainTypes.ts index aabd0df2d..642e81ba9 100644 --- a/src/types/MainTypes.ts +++ b/src/types/MainTypes.ts @@ -147,3 +147,10 @@ export const initErrorServiceState: ErrorServiceState = { homePageLink: '', homeButtonTitle: '', } + +export const IMAGE_TYPES: Record = { + '3c': 'image/svg+xml', + ffd8ff: 'image/jpeg', + '89504e': 'image/png', + 474946: 'image/gif', +} diff --git a/src/types/Patterns.ts b/src/types/Patterns.ts index 130a733b6..168946180 100644 --- a/src/types/Patterns.ts +++ b/src/types/Patterns.ts @@ -81,10 +81,10 @@ export const Patterns = { offerCard: { serviceName: /^[^-\s()'"#@.&](?!.*[%&?,';:!\s-]{2}).{2,200}$/, serviceType: /^([A-Za-z])$/, - shortDescription: /^.{10,120}$/, + shortDescription: /^(?! *$).{10,120}$/, }, offerPage: { - longDescription: /^.{10,1999}$/, + longDescription: /^(?! *$).{10,1999}$/, }, appPage: { longDescriptionEN: diff --git a/yarn.lock b/yarn.lock index cd1b3ac54..a246f4310 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.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-3.5.1.tgz#d2caf4acf4c0197bca392054e0b9f8f1b451b704" - integrity sha512-xWAqM2FuTTBF3Koyjkvf1ZJi36DDwDuXin3nlkbB8hRwBKKV5fWLGQN+3bcwCoA6quNK/Juj7PQ2BYYqrQEDRw== +"@catena-x/portal-shared-components@^3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@catena-x/portal-shared-components/-/portal-shared-components-3.6.1.tgz#750438522b65d4c59976404044cd75fd5883649c" + integrity sha512-mmOETT9z3vDSa/xyM1i+F2N9k20GuqXuW54oF/I7FB7iHAegJbNedj0gaFH4E7LHJQGP9vHtGQ6J4X73ndrsRg== dependencies: "@date-io/date-fns" "^3.0.0" "@emotion/react" "^11.11.4" @@ -1445,10 +1445,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.12": - version "29.5.12" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.12.tgz#7f7dc6eb4cf246d2474ed78744b05d06ce025544" - integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== +"@types/jest@^29.5.13": + version "29.5.13" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.13.tgz#8bc571659f401e6a719a7bf0dbcb8b78c71a8adc" + integrity sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg== dependencies: expect "^29.0.0" pretty-format "^29.0.0"