diff --git a/frontend/src/component/common/Notifications/EmptyNotifications.tsx b/frontend/src/component/common/Notifications/EmptyNotifications.tsx index 691077575f..24206d0a74 100644 --- a/frontend/src/component/common/Notifications/EmptyNotifications.tsx +++ b/frontend/src/component/common/Notifications/EmptyNotifications.tsx @@ -16,11 +16,15 @@ const StyledNotificationsIcon = styled(NotificationsIcon)(({ theme }) => ({ marginBottom: theme.spacing(1), })); -export const EmptyNotifications = () => { +interface IEmptyNotificationsProps { + text: string; +} + +export const EmptyNotifications = ({ text }: IEmptyNotificationsProps) => { return ( - No new notifications + {text} ); }; diff --git a/frontend/src/component/common/Notifications/Notification.tsx b/frontend/src/component/common/Notifications/Notification.tsx index cafd4ce4b4..9df5175d8e 100644 --- a/frontend/src/component/common/Notifications/Notification.tsx +++ b/frontend/src/component/common/Notifications/Notification.tsx @@ -1,5 +1,5 @@ -import { useTheme } from '@mui/material'; -import { Box, ListItem, Typography, styled } from '@mui/material'; +import { ListItemButton, useTheme } from '@mui/material'; +import { Box, Typography, styled } from '@mui/material'; import { NotificationsSchemaItem, NotificationsSchemaItemNotificationType, @@ -26,7 +26,7 @@ const StyledContainerBox = styled(Box, { left: 7, })); -const StyledListItem = styled(ListItem)(({ theme }) => ({ +const StyledListItemButton = styled(ListItemButton)(({ theme }) => ({ position: 'relative', cursor: 'pointer', margin: theme.spacing(2, 0), @@ -109,7 +109,7 @@ export const Notification = ({ }; return ( - onNotificationClick(notification)}> + onNotificationClick(notification)}> {resolveIcon(notification.notificationType)}{' '} @@ -121,6 +121,6 @@ export const Notification = ({ - + ); }; diff --git a/frontend/src/component/common/Notifications/Notifications.tsx b/frontend/src/component/common/Notifications/Notifications.tsx index d78bdbb3fd..9e9e9c4b53 100644 --- a/frontend/src/component/common/Notifications/Notifications.tsx +++ b/frontend/src/component/common/Notifications/Notifications.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { KeyboardEvent, useState } from 'react'; import { Paper, Typography, @@ -7,6 +7,7 @@ import { styled, ClickAwayListener, Button, + Switch, } from '@mui/material'; import { useNotifications } from 'hooks/api/getters/useNotifications/useNotifications'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; @@ -20,6 +21,7 @@ import { useNavigate } from 'react-router-dom'; import { useNotificationsApi } from 'hooks/api/actions/useNotificationsApi/useNotificationsApi'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import { flexRow } from 'themes/themeStyles'; import { Feedback } from 'component/common/Feedback/Feedback'; const StyledPrimaryContainerBox = styled(Box)(() => ({ @@ -45,8 +47,10 @@ const StyledPaper = styled(Paper)(({ theme }) => ({ boxShadow: theme.boxShadows.popup, borderRadius: `${theme.shape.borderRadiusLarge}px`, position: 'absolute', - right: -20, + right: -100, top: 60, + maxHeight: '80vh', + overflowY: 'auto', })); const StyledDotBox = styled(Box)(({ theme }) => ({ @@ -59,15 +63,24 @@ const StyledDotBox = styled(Box)(({ theme }) => ({ right: 4, })); +const StyledHeaderBox = styled(Box)(() => ({ + ...flexRow, +})); + +const StyledHeaderTypography = styled(Typography)(({ theme }) => ({ + fontSize: theme.fontSizes.smallerBody, +})); + export const Notifications = () => { const [showNotifications, setShowNotifications] = useState(false); const { notifications, refetchNotifications } = useNotifications({ - refreshInterval: 15, + refreshInterval: 15000, }); const navigate = useNavigate(); const { markAsRead } = useNotificationsApi(); const { uiConfig } = useUiConfig(); const { trackEvent } = usePlausibleTracker(); + const [showUnread, setShowUnread] = useState(false); const onNotificationClick = (notification: NotificationsSchemaItem) => { if (notification.link) { @@ -110,6 +123,12 @@ export const Notifications = () => { } }; + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + setShowNotifications(false); + } + }; + const unreadNotifications = notifications?.filter( notification => notification.readAt === null ); @@ -118,6 +137,24 @@ export const Notifications = () => { unreadNotifications && unreadNotifications.length > 0 ); + const filterUnread = (notification: NotificationsSchemaItem) => { + if (showUnread) { + return !Boolean(notification.readAt); + } + + return true; + }; + + const notificationComponents = notifications + ?.filter(filterUnread) + .map(notification => ( + + )); + return ( { setShowNotifications(false)} > - - + + + + + Show only unread + + + setShowUnread(!showUnread) + } + /> + + { } />{' '} } - /> - - {notifications?.map(notification => ( - - ))} - - + + {notificationComponents} + + 0 && + !showUnread + )} + show={ + <> + +
+ + } /> -
} diff --git a/frontend/src/component/common/Notifications/NotificationsHeader.tsx b/frontend/src/component/common/Notifications/NotificationsHeader.tsx index 298eea2e03..3ddff8a6de 100644 --- a/frontend/src/component/common/Notifications/NotificationsHeader.tsx +++ b/frontend/src/component/common/Notifications/NotificationsHeader.tsx @@ -1,30 +1,29 @@ import Settings from '@mui/icons-material/Settings'; -import { Typography, Box, IconButton, styled } from '@mui/material'; +import { Typography, IconButton, styled, Box } from '@mui/material'; import { flexRow } from 'themes/themeStyles'; const StyledOuterContainerBox = styled(Box)(({ theme }) => ({ - padding: theme.spacing(1, 3), + padding: theme.spacing(1.5, 3, 0.5, 3), display: 'flex', justifyContent: 'space-between', alignItems: 'center', })); -const StyledSettingsContainer = styled(Box)(() => ({ - ...flexRow, +const StyledInnerBox = styled(Box)(({ theme }) => ({ + boxShadow: theme.boxShadows.separator, + width: '100%', + height: '4px', })); -export const NotificationsHeader = () => { +export const NotificationsHeader: React.FC = ({ children }) => { return ( <> Notifications - - - - - - + {children} + + ); }; diff --git a/frontend/src/component/feature/FeatureToggleList/ExportDialog.tsx b/frontend/src/component/feature/FeatureToggleList/ExportDialog.tsx index a1002cc70c..fd001ee445 100644 --- a/frontend/src/component/feature/FeatureToggleList/ExportDialog.tsx +++ b/frontend/src/component/feature/FeatureToggleList/ExportDialog.tsx @@ -3,10 +3,9 @@ import { Dialogue } from 'component/common/Dialogue/Dialogue'; import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect'; import { useExportApi } from 'hooks/api/actions/useExportApi/useExportApi'; import useToast from 'hooks/useToast'; -import { IEnvironment } from 'interfaces/environments'; import { FeatureSchema } from 'openapi'; -import { createRef, useState } from 'react'; +import { createRef, useEffect, useState } from 'react'; import { formatUnknownError } from 'utils/formatUnknownError'; interface IExportDialogProps { diff --git a/frontend/src/hooks/api/actions/useNotificationsApi/useNotificationsApi.ts b/frontend/src/hooks/api/actions/useNotificationsApi/useNotificationsApi.ts index 541f6dbc6a..296a75ed5a 100644 --- a/frontend/src/hooks/api/actions/useNotificationsApi/useNotificationsApi.ts +++ b/frontend/src/hooks/api/actions/useNotificationsApi/useNotificationsApi.ts @@ -14,9 +14,7 @@ export const useNotificationsApi = () => { }); try { - const res = await makeRequest(req.caller, req.id); - - return res.json(); + await makeRequest(req.caller, req.id); } catch (e) { throw e; } diff --git a/frontend/src/themes/dark-theme.ts b/frontend/src/themes/dark-theme.ts index 51dd591554..07ac3042f5 100644 --- a/frontend/src/themes/dark-theme.ts +++ b/frontend/src/themes/dark-theme.ts @@ -24,6 +24,7 @@ export default createTheme({ elevated: '0px 1px 20px rgba(45, 42, 89, 0.1)', popup: '0px 2px 6px rgba(0, 0, 0, 0.25)', primaryHeader: '0px 8px 24px rgba(97, 91, 194, 0.2)', + separator: '0px 2px 3px hsl(0deg 0% 78% / 50%)', }, typography: { fontFamily: 'Sen, Roboto, sans-serif', diff --git a/frontend/src/themes/theme.ts b/frontend/src/themes/theme.ts index a9296f3571..c78e879de6 100644 --- a/frontend/src/themes/theme.ts +++ b/frontend/src/themes/theme.ts @@ -17,6 +17,7 @@ export default createTheme({ elevated: '0px 1px 20px rgba(45, 42, 89, 0.1)', popup: '0px 2px 6px rgba(0, 0, 0, 0.25)', primaryHeader: '0px 8px 24px rgba(97, 91, 194, 0.2)', + separator: '0px 2px 3px hsl(0deg 0% 78% / 50%)', }, typography: { fontFamily: 'Sen, Roboto, sans-serif', diff --git a/frontend/src/themes/themeTypes.ts b/frontend/src/themes/themeTypes.ts index 70bccb2909..c63b5346df 100644 --- a/frontend/src/themes/themeTypes.ts +++ b/frontend/src/themes/themeTypes.ts @@ -28,6 +28,7 @@ declare module '@mui/material/styles' { elevated: string; popup: string; primaryHeader: string; + separator: string; }; }