diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a98bf468..d3a7711d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,8 @@
- App Overview
- Enhance Sub Menu by adding 'Change Documents' for active apps
- Add roles for active apps
+- My Organization
+ - Unsubscribe subscription flow
- Service Subscription
- UI Changes
- Add filters
diff --git a/src/assets/locales/de/main.json b/src/assets/locales/de/main.json
index 64cc49c3d..57a138de4 100644
--- a/src/assets/locales/de/main.json
+++ b/src/assets/locales/de/main.json
@@ -372,6 +372,19 @@
"subscriptions": {
"title": "App-Abonnements",
"error": "Fehler - keine Daten gefunden"
+ },
+ "unsubscribe": {
+ "title": "Geschäftsanwendung Deaktivieren",
+ "description": "Möchten Sie sich die Geschäftsanwendung wirklich deaktivieren?",
+ "note": "Bitte beachten Sie: Alle möglicherweise verbundenen Objekte (siehe Details oben) werden ebenfalls deaktiviert. Die Abmeldung ist nicht rückgängig zu machen.",
+ "table": {
+ "app": "App",
+ "status": "Status",
+ "connector": "Connector",
+ "techUser": "Technischer User"
+ },
+ "checkBoxLabel": "Ja, ich stimme zu, dass ich die Auswirkungen der Abmeldung verstanden habe und mit dem Vorgang fortfahren möchte.",
+ "buttonText": "Abbestellen"
}
},
"datamanagement": {
@@ -1739,7 +1752,8 @@
"error": "Error",
"retry": "Retry",
"exit": "exit",
- "loading": "Loading..."
+ "loading": "Loading...",
+ "unsubscribe": "Unsubscribe"
},
"state": {
"enabled": "aktiv",
diff --git a/src/assets/locales/en/main.json b/src/assets/locales/en/main.json
index f9a9ae512..38b9d32ba 100644
--- a/src/assets/locales/en/main.json
+++ b/src/assets/locales/en/main.json
@@ -371,6 +371,21 @@
"subscriptions": {
"title": "App Subscriptions",
"error": "error - no data found"
+ },
+ "unsubscribe": {
+ "title": "Unsubscribe Business Application",
+ "description": "Are you sure to unsubscribe from the business application?",
+ "note": "Please Note: all possibly connected objects (see details above) will get deactivated as well.The unsubscription is not revertible.",
+ "table": {
+ "app": "App",
+ "status": "Status",
+ "connector": "Connector",
+ "techUser": "Technical User"
+ },
+ "checkBoxLabel": "Yes I agree that I understood the unsubscribe impact and want to proceed with the process.",
+ "buttonText": "Unsubscribe",
+ "unsubscribeSuccess": "Unsubscribe subscription is successful",
+ "unsubscribeError": "Something went wrong. Try again after sometime or contact administrator"
}
},
"datamanagement": {
@@ -1702,7 +1717,8 @@
"error": "Error",
"retry": "Retry",
"exit": "exit",
- "loading": "Loading..."
+ "loading": "Loading...",
+ "unsubscribe": "Unsubscribe"
},
"state": {
"enabled": "enabled",
diff --git a/src/components/pages/Organization/AppSubscriptions.tsx b/src/components/pages/Organization/AppSubscriptions.tsx
index 2bf883246..af8370a87 100644
--- a/src/components/pages/Organization/AppSubscriptions.tsx
+++ b/src/components/pages/Organization/AppSubscriptions.tsx
@@ -19,8 +19,10 @@
********************************************************************************/
import { SubscriptionStatus, ImageType } from 'features/apps/apiSlice'
-import { Image, LogoGrayData } from '@catena-x/portal-shared-components'
+import { Button, Image, LogoGrayData } from '@catena-x/portal-shared-components'
import { fetchImageWithToken } from 'services/ImageService'
+import './Organization.scss'
+import { useTranslation } from 'react-i18next'
export default function AppSubscriptions({
name,
@@ -28,13 +30,16 @@ export default function AppSubscriptions({
status,
image,
onButtonClick = () => {},
+ onUnsubscribe = () => {},
}: {
name: string
provider: string
status: SubscriptionStatus | undefined
image: ImageType | undefined
onButtonClick?: React.MouseEventHandler
+ onUnsubscribe?: React.MouseEventHandler
}) {
+ const { t } = useTranslation()
const colorCode = [
{ name: SubscriptionStatus.PENDING, code: ' #969696' },
{ name: SubscriptionStatus.INACTIVE, code: 'red' },
@@ -47,27 +52,37 @@ export default function AppSubscriptions({
return (
-
+
+
-
- {name} - by {provider} -
-
- {status ? (
-
- {' '}
- {'\u00a0' + capitalizeFirstLetter(status.toLocaleLowerCase())}
-
- ) : null}
+
+ {name} - by {provider} -
+ {status ? (
+
+ {' '}
+ {'\u00a0' + capitalizeFirstLetter(status.toLocaleLowerCase())}
+
+ ) : null}
+
+
+
+
)
}
diff --git a/src/components/pages/Organization/Organization.scss b/src/components/pages/Organization/Organization.scss
index 6e93d0b46..e0de8586d 100644
--- a/src/components/pages/Organization/Organization.scss
+++ b/src/components/pages/Organization/Organization.scss
@@ -46,9 +46,57 @@
cursor: pointer;
display: flex;
align-items: center;
+ justify-content: space-between;
+ padding: 10px 15px;
+ border-bottom: 1px solid rgb(220, 220, 220);
}
.organization-error {
padding: 10px;
}
}
+ .iconNameContainer {
+ display: flex;
+ }
+ .title {
+ font-size: 16px !important;
+ font-weight: 700 !important;
+ background-color: rgb(237, 240, 244) !important;
+ padding: 15px !important;
+ margin-bottom: 20px !important;
+ font-family: inherit !important;
+ }
+
+ .unsubButton {
+ height: 40px;
+ border-color: #fb6540 !important;
+ background-color: #fb6540 !important;
+ color: #ffffff !important;
+ text-transform: lowercase !important;
+ }
+}
+
+@media (max-width: 480px) {
+ .name {
+ max-width: 130px;
+ }
+ .unsubButton {
+ width: auto;
+ height: 30px;
+ font-size: 8px !important;
+ margin-left: 10px !important;
+ }
+}
+
+.noteBox {
+ margin: 0px 50px;
+ border: 1px solid #e3e3e3;
+ background: #e3e3e3;
+}
+
+.noteText {
+ padding: 20px;
+}
+
+.detailsTable {
+ margin: 50px 50px;
}
diff --git a/src/components/pages/Organization/UnSubscribeOverlay.tsx b/src/components/pages/Organization/UnSubscribeOverlay.tsx
new file mode 100644
index 000000000..f2b2de26f
--- /dev/null
+++ b/src/components/pages/Organization/UnSubscribeOverlay.tsx
@@ -0,0 +1,146 @@
+/********************************************************************************
+ * Copyright (c) 2021, 2023 BMW Group AG
+ * Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+import React, { useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+ Dialog,
+ DialogContent,
+ Button,
+ DialogActions,
+ DialogHeader,
+ Typography,
+ Checkbox,
+ StaticTable,
+ LoadingButton,
+} from '@catena-x/portal-shared-components'
+import Box from '@mui/material/Box'
+import { useFetchSubscriptionAppQuery } from 'features/apps/apiSlice'
+import './Organization.scss'
+
+interface UnSubscribeOverlayProps {
+ openDialog?: boolean
+ handleOverlayClose: React.MouseEventHandler
+ handleConfirmClick: React.MouseEventHandler
+ loading?: boolean
+ subscriptionId: string
+ appId: string
+}
+
+const UnSubscribeOverlay = ({
+ openDialog = false,
+ handleOverlayClose,
+ handleConfirmClick,
+ loading,
+ subscriptionId,
+ appId,
+}: UnSubscribeOverlayProps) => {
+ const { t } = useTranslation()
+ const { data } = useFetchSubscriptionAppQuery({ appId, subscriptionId })
+ const [checkBoxSelected, setCheckBoxSelected] = useState(false)
+ return (
+
+
+
+ )
+}
+
+export default UnSubscribeOverlay
diff --git a/src/components/pages/Organization/index.tsx b/src/components/pages/Organization/index.tsx
index 301472dc1..43545a8a8 100644
--- a/src/components/pages/Organization/index.tsx
+++ b/src/components/pages/Organization/index.tsx
@@ -18,28 +18,30 @@
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/
-import {
- PageHeader,
- StaticTable,
- TableType,
-} from '@catena-x/portal-shared-components'
+import { PageHeader, Typography } from '@catena-x/portal-shared-components'
import { useTranslation } from 'react-i18next'
import AppSubscriptions from './AppSubscriptions'
import { useDispatch } from 'react-redux'
import { show } from 'features/control/overlay'
import './Organization.scss'
import { OVERLAYS } from 'types/Constants'
-import { useFetchSubscribedActiveAppsQuery } from 'features/apps/apiSlice'
+import {
+ useFetchSubscribedActiveAppsQuery,
+ useUnsubscribeAppMutation,
+} from 'features/apps/apiSlice'
import { subscribedApps } from 'features/apps/mapper'
import { useFetchOwnCompanyDetailsQuery } from 'features/admin/userApiSlice'
import LoadingError from './LoadingError'
import { CompanyDetailsToCards } from 'features/admin/mapper'
import { UserDetailCard } from 'components/shared/basic/UserDetailInfo/UserDetailCard'
+import { useState } from 'react'
+import UnSubscribeOverlay from './UnSubscribeOverlay'
+import { error, success } from 'services/NotifyService'
export default function Organization() {
const { t } = useTranslation()
const dispatch = useDispatch()
- const { data } = useFetchSubscribedActiveAppsQuery()
+ const { data, refetch } = useFetchSubscribedActiveAppsQuery()
const appSubscribedData = data && subscribedApps(data)
const {
data: companyDetails,
@@ -48,31 +50,44 @@ export default function Organization() {
} = useFetchOwnCompanyDetailsQuery()
const companyDetailsData =
companyDetails && CompanyDetailsToCards(companyDetails)
+ const [subscriptionId, setSubscriptionId] = useState('')
+ const [appId, setAppId] = useState('')
+ const [showUnsubscribeOverlay, setShowUnsubscribeOverlay] =
+ useState(false)
+ const [loading, setLoading] = useState(false)
+ const [unsubscribeMutation] = useUnsubscribeAppMutation()
const handleClick = (id: string | undefined) => {
dispatch(show(OVERLAYS.APP, id, t('content.organization.company.title')))
}
- const appSubscriptionsTableBody =
- appSubscribedData?.map((app) => [
- () => (
- handleClick(app.offerId)}
- name={app.name || ''}
- provider={app.provider}
- status={app.status}
- />
- ),
- ]) || []
-
- const appSubscriptionsTableData: TableType = {
- head: [t('content.organization.subscriptions.title')],
- body: appSubscriptionsTableBody,
+ const onUnsubscribe = async () => {
+ setLoading(true)
+ await unsubscribeMutation(subscriptionId)
+ .unwrap()
+ .then(() => {
+ success(t('content.organization.unsubscribe.unsubscribeSuccess'))
+ })
+ .catch(() => {
+ error(t('content.organization.unsubscribe.unsubscribeError'))
+ })
+ setLoading(false)
+ setShowUnsubscribeOverlay(false)
+ refetch()
}
return (
+ {showUnsubscribeOverlay && (
+ setShowUnsubscribeOverlay(false)}
+ handleConfirmClick={() => onUnsubscribe()}
+ loading={loading}
+ appId={appId}
+ subscriptionId={subscriptionId}
+ />
+ )}
+ {appSubscribedData?.length !== 0 && (
+
+ {t('content.organization.subscriptions.title')}
+
+ )}
-
+ {appSubscribedData?.map((app) => (
+
handleClick(app.offerId)}
+ name={app.name || ''}
+ provider={app.provider}
+ status={app.status}
+ onUnsubscribe={(e) => {
+ setShowUnsubscribeOverlay(true)
+ setAppId(app.offerId)
+ setSubscriptionId(app.subscriptionId)
+ e.stopPropagation()
+ }}
+ />
+ ))}
diff --git a/src/components/shared/templates/Subscription/Subscription.scss b/src/components/shared/templates/Subscription/Subscription.scss
index 5a50be06e..30a6ad909 100644
--- a/src/components/shared/templates/Subscription/Subscription.scss
+++ b/src/components/shared/templates/Subscription/Subscription.scss
@@ -177,11 +177,17 @@
.firstSection {
width: 30%;
font-weight: 600;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
}
.secondSection {
width: 35%;
font-weight: 600;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
}
.viewDetails {
diff --git a/src/features/apps/apiSlice.ts b/src/features/apps/apiSlice.ts
index e241b0c99..e8ca45757 100644
--- a/src/features/apps/apiSlice.ts
+++ b/src/features/apps/apiSlice.ts
@@ -185,6 +185,35 @@ export interface ActiveSubscription {
name: string
provider: string
image: string
+ subscriptionId: string
+}
+
+export interface SubscribeTechnicalUserData {
+ id: string
+ name: string
+ permissions: Array
+}
+
+export interface SubscribeConnectorData {
+ id: string
+ name: string
+ endpoint: string
+}
+
+export interface ActiveSubscriptionDetails {
+ offerId: string
+ name: string
+ provider: string
+ image: string
+ subscriptionId: string
+ offerSubscriptionStatus: string
+ technicalUserData: SubscribeTechnicalUserData[]
+ connectorData: SubscribeConnectorData[]
+}
+
+interface FetchSubscriptionAppQueryType {
+ subscriptionId: string
+ appId: string
}
export const apiSlice = createApi({
@@ -281,6 +310,19 @@ export const apiSlice = createApi({
fetchSubscribedActiveApps: builder.query({
query: () => '/api/apps/subscribed/activesubscriptions',
}),
+ fetchSubscriptionApp: builder.query<
+ ActiveSubscriptionDetails,
+ FetchSubscriptionAppQueryType
+ >({
+ query: (obj) =>
+ `/api/apps/${obj.appId}/subscription/${obj.subscriptionId}/subscriber`,
+ }),
+ unsubscribeApp: builder.mutation({
+ query: (subscriptionId) => ({
+ url: `/api/apps/${subscriptionId}/unsubscribe`,
+ method: 'PUT',
+ }),
+ }),
}),
})
@@ -296,4 +338,6 @@ export const {
useFetchAgreementsQuery,
useDeactivateAppMutation,
useFetchSubscribedActiveAppsQuery,
+ useFetchSubscriptionAppQuery,
+ useUnsubscribeAppMutation,
} = apiSlice