- {/* The
here is a workaround for the Tooltip which does not work for a "disabled" child */}
- {
- onSwitchSavedStatus([...selectedNotifications], !isOneSaved);
- }}
- >
-
-
-
-
-
+
+ {
+ onSwitchSavedStatus([...selectedNotifications], !isOneSaved);
+ }}
+ icon={ }
+ variant="secondary"
+ />
+ {markAsSavedText}
+
-
-
-
- {
- onSwitchReadStatus([...selectedNotifications], !isOneRead);
- }}
- >
-
-
-
-
-
-
+
+ {
+ onSwitchReadStatus([...selectedNotifications], !isOneRead);
+ }}
+ icon={ }
+ variant="secondary"
+ />
+ {markAsReadText}
+
+
);
};
diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx
index 762900c2b9..760473d064 100644
--- a/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx
+++ b/plugins/notifications/src/components/NotificationsTable/NotificationDescription.tsx
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import Typography from '@material-ui/core/Typography';
-import Button from '@material-ui/core/Button';
+import { Text, Button } from '@backstage/ui';
import { useState } from 'react';
const MAX_LENGTH = 100;
@@ -25,37 +24,35 @@ export const NotificationDescription = (props: { description: string }) => {
const isLong = description.length > MAX_LENGTH;
if (!isLong) {
- return
{description} ;
+ return
{description} ;
}
if (shown) {
return (
-
+
{description}{' '}
{
+ variant="tertiary"
+ onPress={() => {
setShown(false);
}}
>
Show less
-
+
);
}
return (
-
+
{description.substring(0, MAX_LENGTH)}...{' '}
{
+ variant="tertiary"
+ onPress={() => {
setShown(true);
}}
>
Show more
-
+
);
};
diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx
index 4698bb355e..d1f420a6d1 100644
--- a/plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx
+++ b/plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { Notification } from '@backstage/plugin-notifications-common';
-import SvgIcon from '@material-ui/core/SvgIcon';
import { useApp } from '@backstage/core-plugin-api';
import { SeverityIcon } from './SeverityIcon';
@@ -26,8 +25,10 @@ export const NotificationIcon = ({
const app = useApp();
if (notification.payload.icon) {
- const Icon = app.getSystemIcon(notification.payload.icon) ?? SvgIcon;
- return
;
+ const Icon = app.getSystemIcon(notification.payload.icon);
+ if (Icon) {
+ return
;
+ }
}
return
;
};
diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.module.css b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.module.css
new file mode 100644
index 0000000000..013268c88c
--- /dev/null
+++ b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.module.css
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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.
+ */
+
+@layer components {
+ .severityItem {
+ align-content: center;
+ display: flex;
+ align-items: center;
+ }
+
+ .broadcastIcon {
+ font-size: 1rem;
+ vertical-align: text-bottom;
+ }
+
+ .notificationInfoRow {
+ margin-right: var(--bui-space-1);
+ }
+
+ .notificationInfoRow:not(:first-child) {
+ margin-left: var(--bui-space-1);
+ }
+}
diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx
index 7bedde5f1b..72f8a08dd6 100644
--- a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx
+++ b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx
@@ -17,15 +17,20 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import throttle from 'lodash/throttle';
// @ts-ignore
import RelativeTime from 'react-relative-time';
-import Box from '@material-ui/core/Box';
-import Grid from '@material-ui/core/Grid';
-import CheckBox from '@material-ui/core/Checkbox';
-import Typography from '@material-ui/core/Typography';
-import { makeStyles } from '@material-ui/core/styles';
import { Notification } from '@backstage/plugin-notifications-common';
-import { useConfirm } from 'material-ui-confirm';
-import BroadcastIcon from '@material-ui/icons/RssFeed';
-import { alertApiRef, useApi } from '@backstage/core-plugin-api';
+import {
+ Button,
+ Checkbox,
+ Dialog,
+ DialogBody,
+ DialogFooter,
+ DialogHeader,
+ Flex,
+ Text,
+} from '@backstage/ui';
+import { RiRssFill } from '@remixicon/react';
+import { useApi } from '@backstage/core-plugin-api';
+import { toastApiRef } from '@backstage/frontend-plugin-api';
import {
Link,
Table,
@@ -41,23 +46,9 @@ import { BulkActions } from './BulkActions';
import { NotificationIcon } from './NotificationIcon';
import { NotificationDescription } from './NotificationDescription';
-const ThrottleDelayMs = 1000;
+import styles from './NotificationsTable.module.css';
-const useStyles = makeStyles(theme => ({
- severityItem: {
- alignContent: 'center',
- },
- broadcastIcon: {
- fontSize: '1rem',
- verticalAlign: 'text-bottom',
- },
- notificationInfoRow: {
- marginRight: theme.spacing(0.5),
- '&:not(:first-child)': {
- marginLeft: theme.spacing(0.5),
- },
- },
-}));
+const ThrottleDelayMs = 1000;
/** @public */
export type NotificationsTableProps = Pick<
@@ -89,10 +80,9 @@ export const NotificationsTable = ({
totalCount,
}: NotificationsTableProps) => {
const { t } = useTranslationRef(notificationsTranslationRef);
- const classes = useStyles();
const notificationsApi = useApi(notificationsApiRef);
- const alertApi = useApi(alertApiRef);
- const confirm = useConfirm();
+ const toastApi = useApi(toastApiRef);
+ const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
const [selectedNotifications, setSelectedNotifications] = useState(
new Set
(),
@@ -136,38 +126,26 @@ export const NotificationsTable = ({
[notificationsApi, onUpdate],
);
- const onMarkAllRead = useCallback(() => {
- confirm({
- title: 'Are you sure?',
- description: (
- <>
- Mark all notifications as read .
- >
- ),
- confirmationText: 'Mark All',
- })
- .then(async () => {
- const ids = (
- await notificationsApi.getNotifications({ read: false })
- ).notifications?.map(notification => notification.id);
-
- return notificationsApi
- .updateNotifications({
- ids,
- read: true,
- })
- .then(onUpdate);
- })
- .catch(e => {
- if (e) {
- // if e === undefined, the Cancel button has been hit
- alertApi.post({
- message: 'Failed to mark all notifications as read',
- severity: 'error',
- });
- }
+ const doMarkAllRead = useCallback(async () => {
+ setConfirmDialogOpen(false);
+ try {
+ const result = await notificationsApi.getNotifications({ read: false });
+ const ids =
+ result.notifications?.map(notification => notification.id) ?? [];
+ if (ids.length === 0) return;
+ await notificationsApi.updateNotifications({ ids, read: true });
+ onUpdate();
+ } catch {
+ toastApi.post({
+ title: t('table.errors.markAllReadFailed'),
+ status: 'danger',
});
- }, [alertApi, confirm, notificationsApi, onUpdate]);
+ }
+ }, [notificationsApi, onUpdate, toastApi, t]);
+
+ const onMarkAllRead = useCallback(() => {
+ setConfirmDialogOpen(true);
+ }, []);
const throttledContainsTextHandler = useMemo(
() => throttle(setContainsText, ThrottleDelayMs),
@@ -190,6 +168,7 @@ export const NotificationsTable = ({
{
/* selection column */
width: '1rem',
+ cellStyle: { paddingRight: '2.5rem' },
title: showToolbar ? (
) : undefined,
render: (notification: Notification) => (
-
+
onNotificationsSelectChange([notification.id], checked)
}
/>
@@ -216,75 +195,66 @@ export const NotificationsTable = ({
/* compact-data column */
customFilterAndSearch: () =>
true /* Keep sorting&filtering on backend due to pagination. */,
+ cellStyle: { paddingLeft: 0 },
render: (notification: Notification) => {
// Compact content
return (
-
-
+
+
-
-
-
-
- {notification.payload.link ? (
- {
- if (markAsReadOnLinkOpen && !notification.read) {
- onSwitchReadStatus([notification.id], true);
- }
- }}
- >
- {notification.payload.title}
-
- ) : (
- notification.payload.title
- )}
-
- {notification.payload.description ? (
-
- ) : null}
+
+
+
+ {notification.payload.link ? (
+ {
+ if (markAsReadOnLinkOpen && !notification.read) {
+ onSwitchReadStatus([notification.id], true);
+ }
+ }}
+ >
+ {notification.payload.title}
+
+ ) : (
+ notification.payload.title
+ )}
+
+ {notification.payload.description ? (
+
+ ) : null}
-
- {!notification.user && (
- <>
-
- >
- )}
- {notification.origin && (
- <>
-
- {notification.origin}
-
- •
- >
- )}
- {notification.payload.topic && (
- <>
-
- {notification.payload.topic}
-
- •
- >
- )}
- {notification.created && (
-
- )}
-
-
-
-
+
+ {!notification.user && (
+
+ )}
+ {notification.origin && (
+ <>
+
+ {notification.origin}
+
+ •
+ >
+ )}
+ {notification.payload.topic && (
+ <>
+
+ {notification.payload.topic}
+
+ •
+ >
+ )}
+ {notification.created && (
+
+ )}
+
+
+
);
},
},
@@ -319,44 +289,63 @@ export const NotificationsTable = ({
onSwitchSavedStatus,
onMarkAllRead,
onNotificationsSelectChange,
- classes.severityItem,
- classes.broadcastIcon,
- classes.notificationInfoRow,
markAsReadOnLinkOpen,
]);
return (
-
- isLoading={isLoading}
- options={{
- padding: 'dense',
- search: true,
- paging: true,
- pageSize,
- header: true,
- sorting: false,
- }}
- title={title}
- onPageChange={onPageChange}
- onRowsPerPageChange={onRowsPerPageChange}
- page={page}
- totalCount={totalCount}
- onSearchChange={throttledContainsTextHandler}
- data={notifications}
- columns={compactColumns}
- localization={{
- body: {
- emptyDataSourceMessage: t('table.emptyMessage'),
- },
- pagination: {
- firstTooltip: t('table.pagination.firstTooltip'),
- labelDisplayedRows: t('table.pagination.labelDisplayedRows'),
- labelRowsSelect: t('table.pagination.labelRowsSelect'),
- lastTooltip: t('table.pagination.lastTooltip'),
- nextTooltip: t('table.pagination.nextTooltip'),
- previousTooltip: t('table.pagination.previousTooltip'),
- },
- }}
- />
+ <>
+
+ isLoading={isLoading}
+ options={{
+ padding: 'dense',
+ search: true,
+ paging: true,
+ pageSize,
+ header: true,
+ sorting: false,
+ }}
+ title={title}
+ onPageChange={onPageChange}
+ onRowsPerPageChange={onRowsPerPageChange}
+ page={page}
+ totalCount={totalCount}
+ onSearchChange={throttledContainsTextHandler}
+ data={notifications}
+ columns={compactColumns}
+ localization={{
+ body: {
+ emptyDataSourceMessage: t('table.emptyMessage'),
+ },
+ pagination: {
+ firstTooltip: t('table.pagination.firstTooltip'),
+ labelDisplayedRows: t('table.pagination.labelDisplayedRows'),
+ labelRowsSelect: t('table.pagination.labelRowsSelect'),
+ lastTooltip: t('table.pagination.lastTooltip'),
+ nextTooltip: t('table.pagination.nextTooltip'),
+ previousTooltip: t('table.pagination.previousTooltip'),
+ },
+ }}
+ />
+
+ {t('table.confirmDialog.title')}
+
+
+ {t('table.confirmDialog.markAllReadDescription')}
+
+
+
+
+ {t('table.confirmDialog.markAllReadConfirmation')}
+
+
+ {t('table.confirmDialog.cancel')}
+
+
+
+ >
);
};
diff --git a/plugins/notifications/src/components/NotificationsTable/SelectAll.module.css b/plugins/notifications/src/components/NotificationsTable/SelectAll.module.css
new file mode 100644
index 0000000000..80a2294e76
--- /dev/null
+++ b/plugins/notifications/src/components/NotificationsTable/SelectAll.module.css
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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.
+ */
+
+@layer components {
+ .label {
+ display: flex;
+ align-items: center;
+ margin-left: 0;
+ max-width: 4rem;
+ gap: var(--bui-space-1);
+ cursor: pointer;
+ font-size: var(--bui-font-size-1);
+ color: var(--bui-fg-primary);
+ }
+}
diff --git a/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx b/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx
index f6d6ff48c9..8df66bafad 100644
--- a/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx
+++ b/plugins/notifications/src/components/NotificationsTable/SelectAll.tsx
@@ -13,21 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import Checkbox from '@material-ui/core/Checkbox';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
-import { makeStyles } from '@material-ui/core/styles';
-import Tooltip from '@material-ui/core/Tooltip';
-
-const useStyles = makeStyles({
- label: {
- marginLeft: '0px',
- maxWidth: '2rem',
- '& span': {
- paddingRight: '0px',
- marginRight: '2px',
- },
- },
-});
+import { Checkbox, Tooltip, TooltipTrigger } from '@backstage/ui';
+import styles from './SelectAll.module.css';
export const SelectAll = ({
count,
@@ -38,23 +25,19 @@ export const SelectAll = ({
totalCount: number;
onSelectAll: () => void;
}) => {
- const classes = useStyles();
-
return (
- 0 ? `(${count})` : undefined}
- className={classes.label}
- control={
-
- 0}
- indeterminate={count > 0 && totalCount !== count}
- onChange={onSelectAll}
- />
-
- }
- />
+
+ 0}
+ isIndeterminate={count > 0 && totalCount !== count}
+ onChange={() => onSelectAll()}
+ >
+ {count > 0 ? `(${count})` : undefined}
+
+ Select all
+
);
};
diff --git a/plugins/notifications/src/components/NotificationsTable/SeverityIcon.module.css b/plugins/notifications/src/components/NotificationsTable/SeverityIcon.module.css
new file mode 100644
index 0000000000..6289ae319b
--- /dev/null
+++ b/plugins/notifications/src/components/NotificationsTable/SeverityIcon.module.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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.
+ */
+
+@layer components {
+ .critical {
+ color: var(--bui-fg-danger);
+ }
+
+ .high {
+ color: var(--bui-fg-warning);
+ }
+
+ .normal {
+ color: var(--bui-fg-success);
+ }
+
+ .low {
+ color: var(--bui-fg-info);
+ }
+}
diff --git a/plugins/notifications/src/components/NotificationsTable/SeverityIcon.tsx b/plugins/notifications/src/components/NotificationsTable/SeverityIcon.tsx
index a54101626d..2e838d59f1 100644
--- a/plugins/notifications/src/components/NotificationsTable/SeverityIcon.tsx
+++ b/plugins/notifications/src/components/NotificationsTable/SeverityIcon.tsx
@@ -14,43 +14,56 @@
* limitations under the License.
*/
import { NotificationSeverity } from '@backstage/plugin-notifications-common';
-import NormalIcon from '@material-ui/icons/CheckOutlined';
-import CriticalIcon from '@material-ui/icons/ErrorOutline';
-import HighIcon from '@material-ui/icons/WarningOutlined';
-import LowIcon from '@material-ui/icons/InfoOutlined';
-import { makeStyles } from '@material-ui/core/styles';
-
-const useStyles = makeStyles(theme => ({
- critical: {
- color: theme.palette.status.error,
- },
- high: {
- color: theme.palette.status.warning,
- },
- normal: {
- color: theme.palette.status.ok,
- },
- low: {
- color: theme.palette.status.running,
- },
-}));
+import {
+ RiCheckLine,
+ RiErrorWarningLine,
+ RiAlertLine,
+ RiInformationLine,
+} from '@remixicon/react';
+import styles from './SeverityIcon.module.css';
export const SeverityIcon = ({
severity,
+ className,
+ style,
}: {
severity?: NotificationSeverity;
+ className?: string;
+ style?: React.CSSProperties;
}) => {
- const classes = useStyles();
-
switch (severity) {
case 'critical':
- return ;
+ return (
+
+ );
case 'high':
- return ;
+ return (
+
+ );
case 'low':
- return ;
+ return (
+
+ );
case 'normal':
default:
- return ;
+ return (
+
+ );
}
};
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.module.css b/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.module.css
new file mode 100644
index 0000000000..b152c9ccf3
--- /dev/null
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.module.css
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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.
+ */
+
+@layer components {
+ .cell {
+ border-bottom: none;
+ padding: var(--bui-space-2) var(--bui-space-3);
+ vertical-align: middle;
+ }
+}
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.tsx
index 71cd52f8a8..ddd3b23f43 100644
--- a/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.tsx
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/NoBorderTableCell.tsx
@@ -14,11 +14,17 @@
* limitations under the License.
*/
-import { withStyles } from '@material-ui/core/styles';
-import MuiTableCell from '@material-ui/core/TableCell';
+import { ReactNode } from 'react';
+import styles from './NoBorderTableCell.module.css';
-export const NoBorderTableCell = withStyles({
- root: {
- borderBottom: 'none',
- },
-})(MuiTableCell);
+export const NoBorderTableCell = ({
+ children,
+ align,
+}: {
+ children?: ReactNode;
+ align?: 'left' | 'center' | 'right';
+}) => (
+
+ {children}
+
+);
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/OriginRow.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/OriginRow.tsx
index 4483b4c634..6120b1d09a 100644
--- a/plugins/notifications/src/components/UserNotificationSettingsCard/OriginRow.tsx
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/OriginRow.tsx
@@ -19,12 +19,8 @@ import {
NotificationSettings,
OriginSetting,
} from '@backstage/plugin-notifications-common';
-import IconButton from '@material-ui/core/IconButton';
-import Switch from '@material-ui/core/Switch';
-import TableRow from '@material-ui/core/TableRow';
-import Tooltip from '@material-ui/core/Tooltip';
-import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
-import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp';
+import { ButtonIcon, Switch, Tooltip, TooltipTrigger } from '@backstage/ui';
+import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react';
import { NoBorderTableCell } from './NoBorderTableCell';
import { useNotificationFormat } from './UserNotificationSettingsCard';
@@ -43,45 +39,50 @@ export const OriginRow = (props: {
const { origin, settings, handleChange, open, handleRowToggle } = props;
const { formatOriginName } = useNotificationFormat();
return (
-
+
{origin.topics && origin.topics.length > 0 && (
-
-
+ handleRowToggle(origin.id)}
- >
- {open ? : }
-
-
+ onPress={() => handleRowToggle(origin.id)}
+ icon={
+ open ? (
+
+ ) : (
+
+ )
+ }
+ variant="secondary"
+ />
+ {`Show Topics for the ${formatOriginName(
+ origin.id,
+ )} origin`}
+
)}
{formatOriginName(origin.id)}
all
{settings.channels.map(ch => (
-
+
) => {
- handleChange(ch.id, origin.id, null, event.target.checked);
+ onChange={(isSelected: boolean) => {
+ handleChange(ch.id, origin.id, null, isSelected);
}}
/>
-
+ {`Enable or disable ${ch.id.toLocaleLowerCase(
+ 'en-US',
+ )} notifications from ${formatOriginName(origin.id)}`}
+
))}
-
+
);
};
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/TopicRow.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/TopicRow.tsx
index ed2ca8fd05..cf9b708b1f 100644
--- a/plugins/notifications/src/components/UserNotificationSettingsCard/TopicRow.tsx
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/TopicRow.tsx
@@ -20,19 +20,10 @@ import {
OriginSetting,
TopicSetting,
} from '@backstage/plugin-notifications-common';
-import TableRow from '@material-ui/core/TableRow';
-import Tooltip from '@material-ui/core/Tooltip';
-import Switch from '@material-ui/core/Switch';
-import { withStyles } from '@material-ui/core/styles';
+import { Switch, Tooltip, TooltipTrigger } from '@backstage/ui';
import { NoBorderTableCell } from './NoBorderTableCell';
import { useNotificationFormat } from './UserNotificationSettingsCard';
-const TopicTableRow = withStyles({
- root: {
- paddingLeft: '4px',
- },
-})(TableRow);
-
export const TopicRow = (props: {
topic: TopicSetting;
origin: OriginSetting;
@@ -47,33 +38,32 @@ export const TopicRow = (props: {
const { topic, origin, settings, handleChange } = props;
const { formatOriginName, formatTopicName } = useNotificationFormat();
return (
-
+
{formatTopicName(topic.id)}
{settings.channels.map(ch => (
-
-
+
+
) => {
- handleChange(ch.id, origin.id, topic.id, event.target.checked);
+ onChange={(isSelected: boolean) => {
+ handleChange(ch.id, origin.id, topic.id, isSelected);
}}
/>
-
+ {`Enable or disable ${ch.id.toLocaleLowerCase(
+ 'en-US',
+ )} notifications for the ${formatTopicName(
+ topic.id,
+ )} topic from ${formatOriginName(origin.id)}`}
+
))}
-
+
);
};
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.module.css b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.module.css
new file mode 100644
index 0000000000..5ff6e44932
--- /dev/null
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.module.css
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://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.
+ */
+
+@layer components {
+ .table {
+ width: 100%;
+ border-collapse: collapse;
+ }
+
+ .headerCell {
+ border-bottom: 1px solid var(--bui-border-1);
+ padding: var(--bui-space-2) var(--bui-space-3);
+ text-align: left;
+ font-weight: var(--bui-font-weight-bold);
+ }
+}
diff --git a/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx
index 7b1e4fa70a..f6d1de9e27 100644
--- a/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx
+++ b/plugins/notifications/src/components/UserNotificationSettingsCard/UserNotificationSettingsPanel.tsx
@@ -19,21 +19,12 @@ import {
NotificationSettings,
OriginSetting,
} from '@backstage/plugin-notifications-common';
-import Table from '@material-ui/core/Table';
-import MuiTableCell from '@material-ui/core/TableCell';
-import { withStyles } from '@material-ui/core/styles';
-import TableHead from '@material-ui/core/TableHead';
-import Typography from '@material-ui/core/Typography';
-import TableBody from '@material-ui/core/TableBody';
-import TableRow from '@material-ui/core/TableRow';
+import { Text } from '@backstage/ui';
+import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
+import { notificationsTranslationRef } from '../../translation';
import { TopicRow } from './TopicRow';
import { OriginRow } from './OriginRow';
-
-const TableCell = withStyles({
- root: {
- borderBottom: 'none',
- },
-})(MuiTableCell);
+import styles from './UserNotificationSettingsPanel.module.css';
export const UserNotificationSettingsPanel = (props: {
settings: NotificationSettings;
@@ -42,6 +33,7 @@ export const UserNotificationSettingsPanel = (props: {
topicNames?: Record;
}) => {
const { settings, onChange } = props;
+ const { t } = useTranslationRef(notificationsTranslationRef);
const [expandedRows, setExpandedRows] = useState>(new Set());
const handleRowToggle = (originId: string) => {
@@ -106,9 +98,7 @@ export const UserNotificationSettingsPanel = (props: {
if (settings.channels.length === 0) {
return (
-
- No notification settings available, check back later
-
+ {t('settings.noSettingsAvailable')}
);
}
@@ -125,26 +115,29 @@ export const UserNotificationSettingsPanel = (props: {
const uniqueOrigins = Array.from(uniqueOriginsMap);
return (
-
-
-
-
-
- Origin
-
-
- Topic
-
+
+
+
+
+
+ {t('settings.table.origin')}
+
+
+ {t('settings.table.topic')}
+
{settings.channels.map(channel => (
-
-
+
+
{channel.id}
-
-
+
+
))}
-
-
-
+
+
+
{uniqueOrigins.flatMap(origin => [
-
+
+
);
};
diff --git a/plugins/notifications/src/translation.ts b/plugins/notifications/src/translation.ts
index f8c2749c42..b2d4fe2e52 100644
--- a/plugins/notifications/src/translation.ts
+++ b/plugins/notifications/src/translation.ts
@@ -88,8 +88,9 @@ export const notificationsTranslationRef = createTranslationRef({
},
confirmDialog: {
title: 'Are you sure?',
- markAllReadDescription: 'Mark all notifications as read .',
+ markAllReadDescription: 'Mark all notifications as read.',
markAllReadConfirmation: 'Mark All',
+ cancel: 'Cancel',
},
errors: {
markAllReadFailed: 'Failed to mark all notifications as read',
diff --git a/yarn.lock b/yarn.lock
index a4ea35b988..23863cdf4e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6287,14 +6287,11 @@ __metadata:
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"
"@backstage/ui": "workspace:^"
- "@material-ui/core": "npm:^4.9.13"
- "@material-ui/icons": "npm:^4.9.1"
"@remixicon/react": "npm:^4.6.0"
"@testing-library/jest-dom": "npm:^6.0.0"
"@testing-library/react": "npm:^16.0.0"
"@types/react": "npm:^18.0.0"
lodash: "npm:^4.17.21"
- material-ui-confirm: "npm:^3.0.12"
msw: "npm:^1.0.0"
notistack: "npm:^3.0.1"
react: "npm:^18.0.2"
@@ -37837,17 +37834,6 @@ __metadata:
languageName: node
linkType: hard
-"material-ui-confirm@npm:^3.0.12":
- version: 3.0.18
- resolution: "material-ui-confirm@npm:3.0.18"
- peerDependencies:
- "@mui/material": ">= 5.0.0"
- react: ^17.0.0 || ^18.0.0 || ^19.0.0
- react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
- checksum: 10/c2013449b9af11bed9fbf63cb15022d8399379547fedc8ce9f9c4039606536b34cce8b737b82b9628df95291d7b631dba90dd59c6ccbc7c17f9b8dcab50fdccf
- languageName: node
- linkType: hard
-
"material-ui-popup-state@npm:^5.3.6":
version: 5.3.6
resolution: "material-ui-popup-state@npm:5.3.6"