diff --git a/src/components/pages/NotificationCenter/NotificationItem.tsx b/src/components/pages/NotificationCenter/NotificationItem.tsx index a3fe25137..6d87115dc 100644 --- a/src/components/pages/NotificationCenter/NotificationItem.tsx +++ b/src/components/pages/NotificationCenter/NotificationItem.tsx @@ -163,18 +163,27 @@ const NotificationContent = ({ const NotificationConfig = ({ item }: { item: CXNotificationContent }) => { switch (item.typeId) { case NotificationType.APP_SUBSCRIPTION_ACTIVATION: - return + return ( + + ) case NotificationType.WELCOME: - return + return case NotificationType.WELCOME_APP_MARKETPLACE: - return + return ( + + ) case NotificationType.WELCOME_CONNECTOR_REGISTRATION: - return + return ( + + ) case NotificationType.WELCOME_USE_CASES: - return + return case NotificationType.WELCOME_SERVICE_PROVIDER: return ( - + ) case NotificationType.APP_SUBSCRIPTION_REQUEST: return ( @@ -237,9 +246,14 @@ export default function NotificationItem({ const dispatch = useDispatch() const [userRead, setUserRead] = useState(false) - const setRead = async (id: string, value: boolean) => { + item = { + ...item, + contentParsed: JSON.parse(item.content ?? '{}'), + } + + const setRead = async (id: string, flag: boolean) => { try { - await setNotificationRead({ id, flag: value }) + await setNotificationRead({ id, flag }) } catch (error: unknown) { console.log(error) } diff --git a/src/components/pages/NotificationCenter/NotificationList.tsx b/src/components/pages/NotificationCenter/NotificationList.tsx index d0cc5076a..9570fa76a 100644 --- a/src/components/pages/NotificationCenter/NotificationList.tsx +++ b/src/components/pages/NotificationCenter/NotificationList.tsx @@ -18,7 +18,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -import { type CXNotificationContent } from 'features/notification/types' +import { + type NotificationFetchType, + type CXNotificationContent, +} from 'features/notification/types' import NotificationItem from './NotificationItem' import dayjs from 'dayjs' import isToday from 'dayjs/plugin/isToday' @@ -28,29 +31,52 @@ import { useGetNotificationsQuery } from 'features/notification/apiSlice' import './Notifications.scss' import { useSelector } from 'react-redux' import { notificationFetchSelector } from 'features/notification/slice' -import NoItems from '../NoItems' +import NotificationPager from './NotificationPager' dayjs.extend(isToday) dayjs.extend(isYesterday) dayjs.extend(relativeTime) -const NotificationGroup = ({ items }: { items: CXNotificationContent[] }) => { +const NotificationItems = ({ items }: { items: CXNotificationContent[] }) => { return (
    - {items.length > 0 ? ( - items.map((item: CXNotificationContent) => ( - - )) - ) : ( - - )} + {items.map((item: CXNotificationContent) => ( + + ))} +
+ ) +} + +const NotificationGroup = ({ + fetchArgs, + page, +}: { + fetchArgs: NotificationFetchType + page: number +}) => { + const { data } = useGetNotificationsQuery({ + ...fetchArgs, + page, + }) + return ( +
    +
) } export default function NotificationList() { const fetchArgs = useSelector(notificationFetchSelector) - const { data } = useGetNotificationsQuery(fetchArgs) - return + return ( + <> + {fetchArgs && + new Array(fetchArgs.page + 1) + .fill(0) + .map((_, i) => ( + + ))} + {fetchArgs && } + + ) } diff --git a/src/components/pages/NotificationCenter/NotificationPager.tsx b/src/components/pages/NotificationCenter/NotificationPager.tsx new file mode 100644 index 000000000..8a9b7a374 --- /dev/null +++ b/src/components/pages/NotificationCenter/NotificationPager.tsx @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright (c) 2021, 2024 BMW Group AG + * Copyright (c) 2021, 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 { Box } from '@mui/material' +import { CircleProgress } from '@catena-x/portal-shared-components' +import { notificationFetchSelector, setPage } from 'features/notification/slice' +import { useRef } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import useOnScreen from 'utils/useOnScreen' + +export default function NotificationPager() { + const fetchArgs = useSelector(notificationFetchSelector) + const dispatch = useDispatch() + const ref = useRef(null) + const isVisible = useOnScreen(ref) + + const triggerLoad = () => { + setTimeout(() => dispatch(setPage(fetchArgs.page + 1)), 700) + return true + } + + return ( + + {' '} + {isVisible && ( + + )} +
+ {isVisible && triggerLoad()} +
+
+ ) +} diff --git a/src/components/pages/NotificationCenter/NotificationSearch.tsx b/src/components/pages/NotificationCenter/NotificationSearch.tsx index 9104cd504..66977630c 100644 --- a/src/components/pages/NotificationCenter/NotificationSearch.tsx +++ b/src/components/pages/NotificationCenter/NotificationSearch.tsx @@ -19,11 +19,38 @@ ********************************************************************************/ import DebouncedSearchInput from 'components/shared/basic/Input/DebouncedSearchInput' -import { useDispatch } from 'react-redux' -import { setSearch } from 'features/notification/slice' +import { useDispatch, useSelector } from 'react-redux' +import { + notificationFetchSelector, + setOrder, + setSearch, +} from 'features/notification/slice' +import { useTranslation } from 'react-i18next' +import { SortOption } from '@catena-x/portal-shared-components' +import SortImage from 'components/shared/frame/SortImage' +import { NotificationSortingType } from 'features/notification/types' +import { useState } from 'react' export default function NotificationSearch() { + const [showOrder, setShowOrder] = useState(false) + const order = useSelector(notificationFetchSelector).args.sorting const dispatch = useDispatch() + const { t } = useTranslation('notification') + + const sortOptions = [ + { + label: t('sortOptions.new'), + value: NotificationSortingType.DateDesc, + }, + { + label: t('sortOptions.oldest'), + value: NotificationSortingType.DateAsc, + }, + { + label: t('sortOptions.unread'), + value: NotificationSortingType.ReadStatusAsc, + }, + ] return (
@@ -31,6 +58,25 @@ export default function NotificationSearch() { debounceTime={500} onSearch={(expr: string) => dispatch(setSearch(expr))} /> +
+ { + setShowOrder(true) + }} + selected={showOrder} + /> +
+ { + dispatch(setOrder(value as NotificationSortingType)) + setShowOrder(false) + }} + sortOptions={sortOptions} + /> +
+
) } diff --git a/src/features/notification/apiSlice.ts b/src/features/notification/apiSlice.ts index 98e10c870..cced06dd6 100644 --- a/src/features/notification/apiSlice.ts +++ b/src/features/notification/apiSlice.ts @@ -27,6 +27,7 @@ import { type CXNotification, type CXNotificationMeta, type NotificationFetchType, + NotificationSortingType, } from './types' export interface NotificationArgsProps { @@ -51,7 +52,7 @@ export const apiSlice = createApi({ }), getNotifications: builder.query({ query: (fetchArgs) => - `/api/notification?page=${0}&size=${20}${ + `/api/notification?page=${fetchArgs?.page ?? 0}&size=${10}${ fetchArgs?.args?.notificationTopic && fetchArgs?.args?.notificationTopic !== NOTIFICATION_TOPIC.ALL ? `¬ificationTopicId=${fetchArgs?.args?.notificationTopic}` @@ -60,6 +61,8 @@ export const apiSlice = createApi({ fetchArgs?.args?.searchTypeIds ?.map((typeId: NotificationType) => `&searchTypeIds=${typeId}`) .join('') ?? '' + }&sorting=${ + fetchArgs?.args?.sorting ?? NotificationSortingType.DateDesc }`, }), setNotificationRead: builder.mutation({ diff --git a/src/features/notification/slice.ts b/src/features/notification/slice.ts index eeae3cce5..ea443ee96 100644 --- a/src/features/notification/slice.ts +++ b/src/features/notification/slice.ts @@ -47,10 +47,18 @@ export const slice = createSlice({ ...state, fetch: payload.initialNotificationState, }), + setPage: (state, action: PayloadAction) => ({ + ...state, + fetch: { + ...state.fetch, + page: action.payload, + }, + }), setOrder: (state, action: PayloadAction) => ({ ...state, fetch: { ...state.fetch, + page: 0, args: { ...state.fetch.args, sorting: action.payload, @@ -61,6 +69,7 @@ export const slice = createSlice({ ...state, fetch: { ...state.fetch, + page: 0, args: { ...state.fetch.args, searchTypeIds: I18nService.searchNotifications(action.payload), @@ -71,6 +80,7 @@ export const slice = createSlice({ ...state, fetch: { ...state.fetch, + page: 0, args: { ...state.fetch.args, notificationTopic: action.payload, @@ -95,6 +105,7 @@ export const { setTopic, setSearch, setOrder, + setPage, } = slice.actions export default slice diff --git a/src/utils/useOnScreen.ts b/src/utils/useOnScreen.ts new file mode 100644 index 000000000..7341174b2 --- /dev/null +++ b/src/utils/useOnScreen.ts @@ -0,0 +1,43 @@ +/******************************************************************************** + * 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 { type RefObject, useEffect, useMemo, useState } from 'react' + +export default function useOnScreen(ref: RefObject) { + const [isIntersecting, setIsIntersecting] = useState(false) + + const observer = useMemo( + () => + new IntersectionObserver(([entry]) => { + setIsIntersecting(entry.isIntersecting) + }), + [ref] + ) + + useEffect(() => { + if (!ref.current) return + observer.observe(ref.current) + return () => { + observer.disconnect() + } + }, []) + + return isIntersecting +}