diff --git a/workspaces/ilert/.changeset/silent-wasps-explain.md b/workspaces/ilert/.changeset/silent-wasps-explain.md new file mode 100644 index 00000000000..3ae43d39ffa --- /dev/null +++ b/workspaces/ilert/.changeset/silent-wasps-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage-community/plugin-ilert': minor +--- + +Migrate plugin from mui to bui diff --git a/workspaces/ilert/plugins/ilert/package.json b/workspaces/ilert/plugins/ilert/package.json index ae71fb7fabd..b0687e3414b 100644 --- a/workspaces/ilert/plugins/ilert/package.json +++ b/workspaces/ilert/plugins/ilert/package.json @@ -44,11 +44,8 @@ "@backstage/errors": "backstage:^", "@backstage/frontend-plugin-api": "backstage:^", "@backstage/plugin-catalog-react": "backstage:^", - "@date-io/luxon": "1.x", - "@material-ui/core": "^4.12.2", - "@material-ui/icons": "^4.9.1", - "@material-ui/lab": "4.0.0-alpha.61", - "@material-ui/pickers": "^3.3.10", + "@backstage/ui": "backstage:^", + "@remixicon/react": "^4.2.0", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "humanize-duration": "^3.26.0", "luxon": "^3.0.0", diff --git a/workspaces/ilert/plugins/ilert/report.api.md b/workspaces/ilert/plugins/ilert/report.api.md index c9da8970e6c..0615866f4da 100644 --- a/workspaces/ilert/plugins/ilert/report.api.md +++ b/workspaces/ilert/plugins/ilert/report.api.md @@ -11,7 +11,7 @@ import { Entity } from '@backstage/catalog-model'; import { FetchApi } from '@backstage/core-plugin-api'; import { JSX as JSX_2 } from 'react/jsx-runtime'; import { RouteRef } from '@backstage/core-plugin-api'; -import { SvgIconProps } from '@material-ui/core/SvgIcon'; +import { SVGAttributes } from 'react'; // @public (undocumented) export const ACCEPTED = 'ACCEPTED'; @@ -531,7 +531,7 @@ export class ILertClient implements ILertApi { } // @public (undocumented) -export const ILertIcon: (props: SvgIconProps) => JSX_2.Element; +export const ILertIcon: (props: SVGAttributes) => JSX_2.Element; // @public (undocumented) export const ILertPage: () => JSX_2.Element; diff --git a/workspaces/ilert/plugins/ilert/src/ILertIcon.tsx b/workspaces/ilert/plugins/ilert/src/ILertIcon.tsx index 264bf04d0d4..43a9317fb69 100644 --- a/workspaces/ilert/plugins/ilert/src/ILertIcon.tsx +++ b/workspaces/ilert/plugins/ilert/src/ILertIcon.tsx @@ -14,13 +14,29 @@ * limitations under the License. */ -import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon'; +/* + * Copyright 2022 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. + */ + +import { SVGAttributes } from 'react'; /** * @public */ -export const ILertIcon = (props: SvgIconProps) => ( - +export const ILertIcon = (props: SVGAttributes) => ( + ( d="M 10.355469 5.945312 C 10.355469 8.613281 11.957031 11.019531 14.417969 12.050781 L 14.417969 9.917969 C 15.972656 10.925781 17.972656 10.925781 19.527344 9.917969 L 19.527344 12.050781 C 20.847656 11.496094 21.953125 10.53125 22.679688 9.300781 L 22.679688 4.105469 C 22.679688 1.847656 20.832031 0 18.574219 0 L 14.066406 0 C 11.796875 1.113281 10.355469 3.417969 10.355469 5.945312 Z M 16.972656 2.5625 C 18.84375 2.5625 20.355469 4.078125 20.355469 5.945312 C 20.355469 7.8125 18.84375 9.328125 16.972656 9.328125 C 15.105469 9.328125 13.589844 7.8125 13.589844 5.945312 C 13.59375 4.078125 15.105469 2.566406 16.972656 2.5625 Z M 16.972656 2.5625" /> - + ); diff --git a/workspaces/ilert/plugins/ilert/src/components/Alert/AlertActionsMenu.tsx b/workspaces/ilert/plugins/ilert/src/components/Alert/AlertActionsMenu.tsx index ff908c6f4db..d0a838fee7b 100644 --- a/workspaces/ilert/plugins/ilert/src/components/Alert/AlertActionsMenu.tsx +++ b/workspaces/ilert/plugins/ilert/src/components/Alert/AlertActionsMenu.tsx @@ -13,12 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import IconButton from '@material-ui/core/IconButton'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; -import Typography from '@material-ui/core/Typography'; -import MoreVertIcon from '@material-ui/icons/MoreVert'; -import type { MouseEvent, ReactNode } from 'react'; +import { ButtonIcon, MenuTrigger, Menu, MenuItem, Text } from '@backstage/ui'; +import { RiMore2Line } from '@remixicon/react'; import { useState } from 'react'; import { ilertApiRef } from '../../api'; import { useAlertActions } from '../../hooks/useAlertActions'; @@ -42,28 +38,15 @@ export const AlertActionsMenu = ({ const ilertApi = useApi(ilertApiRef); const alertApi = useApi(toastApiRef); const identityApi = useApi(identityApiRef); - const [anchorEl, setAnchorEl] = useState(null); const callback = onAlertChanged || ((_: Alert): void => {}); const setProcessing = setIsLoading || ((_: boolean): void => {}); const [isAssignAlertModalOpened, setIsAssignAlertModalOpened] = useState(false); - const [{ alertActions, isLoading }] = useAlertActions( - alert, - Boolean(anchorEl), - ); - - const handleClick = (event: MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleCloseMenu = () => { - setAnchorEl(null); - }; + const [{ alertActions, isLoading }] = useAlertActions(alert, true); const handleAccept = async (): Promise => { try { - handleCloseMenu(); setProcessing(true); const { userEntityRef } = await identityApi.getBackstageIdentity(); @@ -84,7 +67,6 @@ export const AlertActionsMenu = ({ const handleResolve = async (): Promise => { try { - handleCloseMenu(); setProcessing(true); const { userEntityRef } = await identityApi.getBackstageIdentity(); const { name: userName } = parseEntityRef(userEntityRef, { @@ -103,13 +85,11 @@ export const AlertActionsMenu = ({ }; const handleAssign = () => { - handleCloseMenu(); setIsAssignAlertModalOpened(true); }; const handleTriggerAction = (action: AlertAction) => async () => { try { - handleCloseMenu(); setProcessing(true); await ilertApi.triggerAlertAction(alert, action); alertApi.post({ title: 'Alert action triggered.' }); @@ -120,86 +100,67 @@ export const AlertActionsMenu = ({ } }; - const actions: ReactNode[] = alertActions.map(a => { - const successTrigger = a.history - ? a.history.find(h => h.success) - : undefined; - const triggeredBy = - successTrigger && successTrigger.actor - ? `${successTrigger.actor.firstName} ${successTrigger.actor.lastName}` - : ''; - return ( - - - {triggeredBy ? `${a.name} (by ${triggeredBy})` : a.name} - - - ); - }); - return ( <> - - - - - {alert.status === 'PENDING' ? ( - - - Accept - + + } + /> + + {alert.status === 'PENDING' ? ( + + Accept + + ) : null} + + {alert.status !== 'RESOLVED' ? ( + + Resolve + + ) : null} + + {alert.status !== 'RESOLVED' ? ( + + Assign + + ) : null} + + {isLoading ? ( + + + + ) : ( + alertActions.map(a => { + const successTrigger = a.history + ? a.history.find(h => h.success) + : undefined; + const triggeredBy = + successTrigger && successTrigger.actor + ? `${successTrigger.actor.firstName} ${successTrigger.actor.lastName}` + : ''; + return ( + + + {triggeredBy ? `${a.name} (by ${triggeredBy})` : a.name} + + + ); + }) + )} + + + + View in ilert + - ) : null} - - {alert.status !== 'RESOLVED' ? ( - - - Resolve - - - ) : null} - - {alert.status !== 'RESOLVED' ? ( - - - Assign - - - ) : null} - - {isLoading ? ( - - - - ) : ( - actions - )} - - - - View in ilert - - - + + ({ - container: { - display: 'flex', - flexWrap: 'wrap', - }, - formControl: { - minWidth: 120, - width: '100%', - }, - option: { - fontSize: 15, - '& > span': { - marginRight: 10, - fontSize: 18, - }, - }, - optionWrapper: { - display: 'flex', - width: '100%', - }, - sourceImage: { - height: 22, - paddingRight: 4, - }, -})); +import styles from './AlertAssignModal.module.css'; export const AlertAssignModal = ({ alert, @@ -73,7 +47,6 @@ export const AlertAssignModal = ({ const callback = onAlertChanged || ((_: Alert): void => {}); const ilertApi = useApi(ilertApiRef); const alertApi = useApi(toastApiRef); - const classes = useStyles(); const handleClose = () => { setAlertRespondersList([]); @@ -102,79 +75,90 @@ export const AlertAssignModal = ({ const canAssign = !!alertResponder; return ( - - - Select responder to assign - - - - - This action will assign the alert to the selected responder. - - - { - setAlertResponder(newValue); - }} - autoHighlight - groupBy={option => { - switch (option.group) { - case 'SUGGESTED': - return 'Suggested responders'; - case 'USER': - return 'Users'; - case 'ESCALATION_POLICY': - return 'Escalation policies'; - case 'ON_CALL_SCHEDULE': - return 'Schedules'; - default: - return ''; - } - }} - getOptionLabel={a => a.name} - renderOption={a => ( -
- {a.name} -
- )} - renderInput={params => ( - + { + if (!open) handleClose(); + }} + > + Select responder to assign + +
+ + This action will assign the alert to the selected responder. + +
+
+ + +
+
+ + + + +
+ ); }; diff --git a/workspaces/ilert/plugins/ilert/src/components/Alert/AlertNewModal.module.css b/workspaces/ilert/plugins/ilert/src/components/Alert/AlertNewModal.module.css new file mode 100644 index 00000000000..5cd0971b5f8 --- /dev/null +++ b/workspaces/ilert/plugins/ilert/src/components/Alert/AlertNewModal.module.css @@ -0,0 +1,47 @@ +.formControl { + min-width: 120px; + width: 100%; + margin-bottom: 16px; + display: flex; + flex-direction: column; +} + +.label { + font-weight: 500; + font-size: 14px; + margin-bottom: 8px; + color: var(--bui-fg); +} + +.select, +.textInput, +.textarea { + padding: 8px 12px; + border: 2px solid var(--bui-border-2); + border-radius: 4px; + font-size: 14px; + font-family: inherit; + background-color: var(--bui-bg-surface); + color: var(--bui-fg); +} + +.select:focus, +.textInput:focus, +.textarea:focus { + outline: none; + border-color: var(--bui-primary); + box-shadow: 0 0 0 2px rgba(var(--bui-primary-rgb), 0.1); +} + +.selectContainer { + position: relative; +} + +.textarea { + resize: vertical; + min-height: 80px; +} + +.textInput { + height: 36px; +} diff --git a/workspaces/ilert/plugins/ilert/src/components/Alert/AlertNewModal.tsx b/workspaces/ilert/plugins/ilert/src/components/Alert/AlertNewModal.tsx index 12a15b46afb..35135e2d389 100644 --- a/workspaces/ilert/plugins/ilert/src/components/Alert/AlertNewModal.tsx +++ b/workspaces/ilert/plugins/ilert/src/components/Alert/AlertNewModal.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2026 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. @@ -16,46 +16,19 @@ import { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model'; import { identityApiRef, useApi } from '@backstage/core-plugin-api'; import { toastApiRef } from '@backstage/frontend-plugin-api'; -import Typography from '@material-ui/core/Typography'; -import Button from '@material-ui/core/Button'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import { makeStyles } from '@material-ui/core/styles'; -import TextField from '@material-ui/core/TextField'; -import useMediaQuery from '@material-ui/core/useMediaQuery'; -import Alert from '@material-ui/lab/Alert'; -import Autocomplete from '@material-ui/lab/Autocomplete'; +import { + Button, + Dialog, + DialogBody, + DialogFooter, + DialogHeader, + DialogTrigger, + Text, +} from '@backstage/ui'; import { ilertApiRef } from '../../api'; import { useNewAlert } from '../../hooks/useNewAlert'; import { AlertSource } from '../../types'; - -const useStyles = makeStyles(() => ({ - container: { - display: 'flex', - flexWrap: 'wrap', - }, - formControl: { - minWidth: 120, - width: '100%', - }, - option: { - fontSize: 15, - '& > span': { - marginRight: 10, - fontSize: 18, - }, - }, - optionWrapper: { - display: 'flex', - width: '100%', - }, - sourceImage: { - height: 22, - paddingRight: 4, - }, -})); +import styles from './AlertNewModal.module.css'; export const AlertNewModal = ({ isModalOpened, @@ -78,8 +51,6 @@ export const AlertNewModal = ({ const alertApi = useApi(toastApiRef); const identityApi = useApi(identityApiRef); const source = window.location.toString(); - const classes = useStyles(); - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); const handleClose = () => { setIsModalOpened(false); @@ -122,112 +93,104 @@ export const AlertNewModal = ({ const canCreate = !!integrationKey && !!summary; return ( - - - {entityName ? ( -
- This action will trigger an alert for{' '} - "{entityName}". -
- ) : ( - 'New alert' - )} -
- - - - Please describe the problem you want to report. Be as descriptive as - possible. Your signed in user and a reference to the current page - will automatically be amended to the alarm so that the receiver can - reach out to you if necessary. - - - {!initialAlertSource ? ( - { - setAlertSource(newValue); + + + + {entityName ? ( +
+ This action will trigger an alert for{' '} + {entityName}. +
+ ) : ( + 'New alert' + )} +
+ +
a.name} - renderOption={a => ( -
- {a.name} - {a.name} + > + + Please describe the problem you want to report. Be as descriptive + as possible. Your signed in user and a reference to the current + page will automatically be amended to the alarm so that the + receiver can reach out to you if necessary. + +
+ {!initialAlertSource ? ( +
+ +
+
- )} - renderInput={params => ( - - )} - /> - ) : null} - { - setSummary(event.target.value); - }} - /> - { - setDetails(event.target.value); - }} - /> - - - - - -
+ + ) : null} +
+ + setSummary(e.target.value)} + /> +
+
+ +