Alerting: Remove start page of legacy upgrade preview (#82010)

Alerting: Remove start page of upgrade preview

Alerting upgrade page will now always show the summary table even before
upgrading any alerts or notification channels. There a few reasons for this:

- The information on the start page is redundant as it's now contained in the
documentation.
- Previously, if some unexpected issue prevented performing a full upgrade, a
user would have limited to no means to using the preview tool to help fix the
problem. This is because you could not see the summary table until the full
upgrade was performed at least once. Now, you can upgrade individual alerts and
notification channels from the beginning.
This commit is contained in:
Matthew Jacobson 2024-02-15 17:34:00 -05:00 committed by GitHub
parent 8de9c4c373
commit 118e4a50b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 302 deletions

View File

@ -59,7 +59,7 @@ When upgrading with either method, your legacy dashboard alerts and notification
In **Alerts & IRM**, the **Alerting** section provides a preview of Grafana Alerting where you can review and modify your upgraded alerts before finalizing the upgrade.
In the **Alerting (legacy) -> Alerting upgrade** section, you can initiate the process to automatically upgrade your existing alert rules and notification channels, and view a summary of the upgrade to Grafana Alerting.
In the **Alerting (legacy) -> Alerting upgrade** section, you can upgrade your existing alert rules and notification channels, and view a summary of the upgrade to Grafana Alerting.
Finalize your upgrade by restarting Grafana with the `[unified_alerting]` section enabled in your configuration.
@ -70,8 +70,8 @@ Alerts generated by the new alerting system are visible in the **Alerting** sect
### To upgrade with preview, complete the following steps.
1. **Preview the Upgrade**:
- **Initiate the process**: Access the upgrade functionality within Grafana by visiting the **Alerting upgrade** page in the **Alerting (legacy)** section of the navigation panel. Click on "Preview upgrade" to initiate the process. This will automatically upgrade your existing alert rules and notification channels to the new Grafana Alerting system.
- **Review the summary table:** Review the detailed table outlining how your existing alert rules and notification channels were mapped to resources in the new Grafana Alerting system.
- **Initiate the process**: Access the upgrade functionality within Grafana by visiting the **Alerting upgrade** page in the **Alerting (legacy)** section of the navigation panel. From this page you can upgrade your existing alert rules and notification channels to the new Grafana Alerting system.
- **Review the summary table:** Review the detailed table outlining how your existing alert rules and notification channels were upgraded to resources in the new Grafana Alerting system.
2. **Investigate and Resolve Errors**:
- **Identify errors**: Carefully examine the previewed upgrade:
- Any alert rules or notification channels that couldn't be automatically upgraded will be highlighted with error indicators.

View File

@ -35,9 +35,9 @@ func (srv *UpgradeSrv) RoutePostUpgradeOrg(c *contextmodel.ReqContext) response.
summary, err := srv.upgradeService.MigrateOrg(c.Req.Context(), c.OrgID, c.QueryBool("skipExisting"))
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, summary)
}
@ -46,9 +46,9 @@ func (srv *UpgradeSrv) RouteGetOrgUpgrade(c *contextmodel.ReqContext) response.R
state, err := srv.upgradeService.GetOrgMigrationState(c.Req.Context(), c.OrgID)
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, state)
}
@ -57,9 +57,9 @@ func (srv *UpgradeSrv) RouteDeleteOrgUpgrade(c *contextmodel.ReqContext) respons
err := srv.upgradeService.RevertOrg(c.Req.Context(), c.OrgID)
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, util.DynMap{"message": "Grafana Alerting resources deleted for this organization."})
}
@ -78,9 +78,9 @@ func (srv *UpgradeSrv) RoutePostUpgradeAlert(c *contextmodel.ReqContext, dashboa
summary, err := srv.upgradeService.MigrateAlert(c.Req.Context(), c.OrgID, dashboardId, panelId)
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, summary)
}
@ -94,9 +94,9 @@ func (srv *UpgradeSrv) RoutePostUpgradeDashboard(c *contextmodel.ReqContext, das
summary, err := srv.upgradeService.MigrateDashboardAlerts(c.Req.Context(), c.OrgID, dashboardId, c.QueryBool("skipExisting"))
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, summary)
}
@ -105,9 +105,9 @@ func (srv *UpgradeSrv) RoutePostUpgradeAllDashboards(c *contextmodel.ReqContext)
summary, err := srv.upgradeService.MigrateAllDashboardAlerts(c.Req.Context(), c.OrgID, c.QueryBool("skipExisting"))
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, summary)
}
@ -121,9 +121,9 @@ func (srv *UpgradeSrv) RoutePostUpgradeChannel(c *contextmodel.ReqContext, chann
summary, err := srv.upgradeService.MigrateChannel(c.Req.Context(), c.OrgID, channelId)
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, summary)
}
@ -132,9 +132,9 @@ func (srv *UpgradeSrv) RoutePostUpgradeAllChannels(c *contextmodel.ReqContext) r
summary, err := srv.upgradeService.MigrateAllChannels(c.Req.Context(), c.OrgID, c.QueryBool("skipExisting"))
if err != nil {
if errors.Is(err, migration.ErrUpgradeInProgress) {
return response.Error(http.StatusConflict, "Upgrade already in progress", err)
return ErrResp(http.StatusConflict, err, "Upgrade already in progress")
}
return response.Error(http.StatusInternalServerError, "Server error", err)
return ErrResp(http.StatusInternalServerError, err, "Server error")
}
return response.JSON(http.StatusOK, summary)
}

View File

@ -73,13 +73,23 @@ func ProvideService(
type operation func(ctx context.Context) (*definitions.OrgMigrationSummary, error)
// verifyTry verifies that the org has been migrated, and then attempts to execute the operation. If another operation
// is already in progress, ErrUpgradeInProgress will be returned.
func (ms *migrationService) verifyTry(ctx context.Context, orgID int64, op operation) (definitions.OrgMigrationSummary, error) {
if err := ms.verifyMigrated(ctx, orgID); err != nil {
return definitions.OrgMigrationSummary{}, err
// tryAndSet attempts to execute the operation and then sets the migrated status to true.
// If another operation is already in progress, ErrUpgradeInProgress will be returned
func (ms *migrationService) tryAndSet(ctx context.Context, orgID int64, op operation) (definitions.OrgMigrationSummary, error) {
opAndSet := func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
s, err := op(ctx)
if err != nil {
return nil, err
}
err = ms.migrationStore.SetMigrated(ctx, orgID, true)
if err != nil {
return nil, fmt.Errorf("setting migration status: %w", err)
}
return s, nil
}
return ms.try(ctx, op)
return ms.try(ctx, opAndSet)
}
// try attempts to execute the operation. If another operation is already in progress, ErrUpgradeInProgress will be returned.
@ -110,7 +120,7 @@ func (ms *migrationService) try(ctx context.Context, op operation) (definitions.
// MigrateChannel migrates a single legacy notification channel to a unified alerting contact point.
func (ms *migrationService) MigrateChannel(ctx context.Context, orgID int64, channelID int64) (definitions.OrgMigrationSummary, error) {
return ms.verifyTry(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
return ms.tryAndSet(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
summary := definitions.OrgMigrationSummary{}
om := ms.newOrgMigration(orgID)
oldState, err := om.migrationStore.GetOrgMigrationState(ctx, orgID)
@ -161,7 +171,7 @@ func (ms *migrationService) MigrateChannel(ctx context.Context, orgID int64, cha
// MigrateAllChannels migrates all legacy notification channel to unified alerting contact points.
func (ms *migrationService) MigrateAllChannels(ctx context.Context, orgID int64, skipExisting bool) (definitions.OrgMigrationSummary, error) {
return ms.verifyTry(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
return ms.tryAndSet(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
summary := definitions.OrgMigrationSummary{}
om := ms.newOrgMigration(orgID)
pairs, err := om.migrateOrgChannels(ctx)
@ -181,7 +191,7 @@ func (ms *migrationService) MigrateAllChannels(ctx context.Context, orgID int64,
// MigrateAlert migrates a single dashboard alert from legacy alerting to unified alerting.
func (ms *migrationService) MigrateAlert(ctx context.Context, orgID int64, dashboardID int64, panelID int64) (definitions.OrgMigrationSummary, error) {
return ms.verifyTry(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
return ms.tryAndSet(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
summary := definitions.OrgMigrationSummary{}
om := ms.newOrgMigration(orgID)
oldState, err := om.migrationStore.GetOrgMigrationState(ctx, orgID)
@ -238,7 +248,7 @@ func (ms *migrationService) MigrateAlert(ctx context.Context, orgID int64, dashb
// MigrateDashboardAlerts migrates all legacy dashboard alerts from a single dashboard to unified alerting.
func (ms *migrationService) MigrateDashboardAlerts(ctx context.Context, orgID int64, dashboardID int64, skipExisting bool) (definitions.OrgMigrationSummary, error) {
return ms.verifyTry(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
return ms.tryAndSet(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
summary := definitions.OrgMigrationSummary{}
om := ms.newOrgMigration(orgID)
alerts, err := ms.migrationStore.GetDashboardAlerts(ctx, orgID, dashboardID)
@ -259,7 +269,7 @@ func (ms *migrationService) MigrateDashboardAlerts(ctx context.Context, orgID in
// MigrateAllDashboardAlerts migrates all legacy alerts to unified alerting contact points.
func (ms *migrationService) MigrateAllDashboardAlerts(ctx context.Context, orgID int64, skipExisting bool) (definitions.OrgMigrationSummary, error) {
return ms.verifyTry(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
return ms.tryAndSet(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
summary := definitions.OrgMigrationSummary{}
om := ms.newOrgMigration(orgID)
dashboardUpgrades, err := om.migrateOrgAlerts(ctx)
@ -279,7 +289,7 @@ func (ms *migrationService) MigrateAllDashboardAlerts(ctx context.Context, orgID
// MigrateOrg executes the migration for a single org.
func (ms *migrationService) MigrateOrg(ctx context.Context, orgID int64, skipExisting bool) (definitions.OrgMigrationSummary, error) {
return ms.try(ctx, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
return ms.tryAndSet(ctx, orgID, func(ctx context.Context) (*definitions.OrgMigrationSummary, error) {
summary := definitions.OrgMigrationSummary{}
ms.log.Info("Starting legacy migration for org", "orgId", orgID, "skipExisting", skipExisting)
om := ms.newOrgMigration(orgID)
@ -293,11 +303,6 @@ func (ms *migrationService) MigrateOrg(ctx context.Context, orgID int64, skipExi
return nil, err
}
err = ms.migrationStore.SetMigrated(ctx, orgID, true)
if err != nil {
return nil, fmt.Errorf("setting migration status: %w", err)
}
summary.Add(s)
return &summary, nil
})
@ -306,9 +311,6 @@ func (ms *migrationService) MigrateOrg(ctx context.Context, orgID int64, skipExi
// GetOrgMigrationState returns the current migration state for an org. This is a potentially expensive operation as it
// requires re-hydrating the entire migration state from the database against all current alerting resources.
func (ms *migrationService) GetOrgMigrationState(ctx context.Context, orgID int64) (*definitions.OrgMigrationState, error) {
if migrated, err := ms.migrationStore.IsMigrated(ctx, orgID); err != nil || !migrated {
return &definitions.OrgMigrationState{OrgID: orgID}, err
}
dState, err := ms.migrationStore.GetOrgMigrationState(ctx, orgID)
if err != nil {
return nil, err
@ -525,18 +527,6 @@ func (ms *migrationService) RevertAllOrgs(ctx context.Context) error {
return err
}
// verifyMigrated returns an error if the org has not been migrated.
func (ms *migrationService) verifyMigrated(ctx context.Context, orgID int64) error {
migrated, err := ms.migrationStore.IsMigrated(ctx, orgID)
if err != nil {
return fmt.Errorf("check if migrated: %w", err)
}
if !migrated {
return fmt.Errorf("not migrated")
}
return nil
}
// fromDashboardUpgrades converts DashboardUpgrades to their api representation. This requires rehydrating information
// from the database for the current state of dashboards, alerts, and rules.
func (ms *migrationService) fromDashboardUpgrades(ctx context.Context, orgID int64, migratedDashboards map[int64]*migrationStore.DashboardUpgrade, amConfig *migmodels.Alertmanager) ([]*definitions.DashboardUpgrade, error) {
@ -616,7 +606,7 @@ func (ms *migrationService) fromDashboardUpgrades(ctx context.Context, orgID int
} else {
// We could potentially set an error here, but it's not really an error. It just means that the
// user deleted the migrated rule after the migration. This could just as easily be intentional.
ms.log.Info("Could not find rule for migrated alert", "alertId", a.ID, "ruleUid", p.NewRuleUID)
ms.log.Debug("Could not find rule for migrated alert", "alertId", a.ID, "ruleUid", p.NewRuleUID)
}
}
}

View File

@ -8,14 +8,12 @@ import { useLocation } from 'react-router-dom';
import { useLocalStorage } from 'react-use';
import { GrafanaTheme2, UrlQueryMap } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Stack } from '@grafana/experimental';
import { locationService } from '@grafana/runtime';
import {
Alert,
Badge,
Button,
CallToActionCard,
ConfirmModal,
FilterInput,
HorizontalGroup,
@ -80,7 +78,6 @@ export const UpgradePage = () => {
const showError = isFetchError;
const showLoading = isLoading;
const showStart = !isLoading && !isFetchError && !hasData;
const showData = !isLoading && !isFetchError && hasData;
return (
@ -92,7 +89,6 @@ export const UpgradePage = () => {
</Alert>
)}
{showLoading && <Loading text={'Loading...'} />}
{showStart && <CTAElement />}
{showData && (
<>
<ErrorSummary errors={errors} />
@ -188,12 +184,12 @@ const CancelUpgradeButton = () => {
icon={'trash-alt'}
className={''}
>
{'Cancel upgrade'}
{'Reset upgrade'}
</Button>
{showConfirmStartOver && (
<ConfirmModal
isOpen={true}
title="Cancel upgrade"
title="Reset upgrade"
body={
<Stack direction="column" gap={0.5}>
<Text color="primary">All new Grafana Alerting resources will be deleted.</Text>
@ -205,7 +201,7 @@ const CancelUpgradeButton = () => {
<Text color="primary">No legacy alerts or notification channels will be affected.</Text>
</Stack>
}
confirmText="Cancel upgrade"
confirmText="Reset upgrade"
onConfirm={cancelUpgrade}
dismissText={'Keep reviewing'}
onDismiss={() => setShowConfirmStartOver(false)}
@ -240,249 +236,6 @@ function getActiveTabFromUrl(queryParams: UrlQueryMap): QueryParamValues {
};
}
const CTAElement = () => {
const styles = useStyles2(getContentBoxStyles);
const { useUpgradeOrgMutation } = upgradeApi;
const [startUpgrade, { isLoading: isUpgradeLoading }] = useUpgradeOrgMutation({
fixedCacheKey: 'upgrade-org-loading',
});
const [, { isLoading: isCancelLoading }] = upgradeApi.useCancelOrgUpgradeMutation({
fixedCacheKey: 'cancel-org-upgrade-loading',
});
const isLoading = isUpgradeLoading || isCancelLoading;
const upgradeAlerting = async () => {
await startUpgrade({ skipExisting: false });
};
if (isLoading) {
return <Loading text={isCancelLoading ? 'Cancelling upgrade...' : 'Upgrade in progress...'} />;
}
const footer = (
<>
<span key="proTipFooter">
<p>
Note:{' '}
{'Previewing the upgrade process will not affect your existing legacy alerts and can be stopped at any time.'}
</p>
</span>
</>
);
const cta = (
<div>
<Stack direction="column" gap={1}>
<Stack direction="row" gap={1}>
<Button
size="lg"
variant="primary"
onClick={upgradeAlerting}
icon={'bell'}
className={''}
data-testid={selectors.components.CallToActionCard.buttonV2('Preview upgrade')}
>
{'Preview upgrade'}
</Button>
</Stack>
</Stack>
</div>
);
return (
<div className={styles.grid}>
<ContentBox className={styles.processBlock}>
<h3 className={styles.header}>How it works</h3>
<Stack direction="column" alignItems="space-between">
<div className={styles.list}>
<h4>Automatic Upgrade</h4>
<div className={styles.step}>
<p>
The upgrade process seamlessly transfers your existing legacy alert rules and notification channels to
the new Grafana Alerting system. This means your alerting configurations are preserved during the
transition.
</p>
</div>
<h4>Preview and Modification</h4>
<div className={styles.step}>
<p>
Alert Rules, Contact Points, and Notification Policies generated during the upgrade are available for
your review and potential adjustments. However, please note that they won&apos;t actively trigger alerts
or send notifications at this stage.
</p>
</div>
<h4>Limitations on Real-Time Updates</h4>
<div className={styles.step}>
<p>
Any changes made to your configurations after initiating the upgrade won&apos;t be immediately reflected
in the summary table. You have the flexibility to re-upgrade specific resources like dashboards, alert
rules, and notification channels at any time.
</p>
</div>
<h4>Cancellation and Restart</h4>
<div className={styles.step}>
<p>
If necessary, you can cancel and restart the upgrade process. However, it&apos;s important to be aware
that canceling the upgrade will result in the removal of all Grafana Alerting resources created during
the process, including any manual modifications.
</p>
</div>
<h4>Completing the Upgrade</h4>
<div className={styles.step}>
<p>
To enable Grafana Alerting, you&apos;ll need to modify the Grafana configuration and restart. Until this
step is completed, Grafana Alerting will remain inactive.
</p>
</div>
</div>
</Stack>
</ContentBox>
<ContentBox className={styles.getStartedBlock}>
<h3 className={styles.header}>Get started</h3>
<Stack direction="column" alignItems="space-between">
<div className={styles.list}>
<h4>Step 1: Preview the Upgrade</h4>
<div className={styles.step}>
<p>
Start the upgrade process by clicking on &quot;Preview upgrade.&quot; This action will display a summary
table showing how your existing alert rules and notification channels will be mapped to resources in the
new Grafana Alerting system.
</p>
</div>
<h4>Step 2: Investigate and Resolve Errors</h4>
<div className={styles.step}>
<p>
Review the previewed upgrade carefully. Alert rules or notification channels that couldn&apos;t be
automatically upgraded will be marked as errors. You have two options to address these errors:
</p>
<ul className={styles.list}>
<li>
Fix the issues on the legacy side: If possible, resolve the problems within your legacy alerting
setup, and then attempt the upgrade again.
</li>
<li>
Manually create new resources: If fixing legacy issues isn&apos;t feasible, manually create new alert
rules, notification policies, or contact points in the new Grafana Alerting system to replace the
problematic ones.
</li>
</ul>
</div>
<h4>Step 3: Update Your As-Code Setup (Optional)</h4>
<div className={styles.step}>
<p>
In the new Grafana Alerting, Legacy Alerting methods of provisioning will no longer work. If you use
provisioning to manage alert rules and notification channels, you can export the upgraded versions to
generate Grafana Alerting-compatible provisioning files. This can all be done before completeing the
upgrade process.
</p>
</div>
<h4>Step 4: Perform the Upgrade to Grafana Alerting</h4>
<div className={styles.step}>
<p>
Once you are satisfied with the state of your Grafana Alerting setup, it&apos;s time to proceed with the
upgrade:
</p>
<ul className={styles.list}>
<li>
Contact your Grafana server administrator to restart Grafana with the [unified_alerting] section
enabled in your configuration.
</li>
<li>
During this process, all organizations that have undergone the above upgrade process will continue to
use their configured setup.
</li>
<li>
Any organization that has not yet started the upgrade process will be automatically upgraded as part
of this restart.
</li>
<li>
Note: If the automatic upgrade fails for any reason, Grafana will not start, so it&apos;s safer to
address any issues before initiating this step.
</li>
</ul>
</div>
</div>
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Text color={'secondary'}>For more information, please refer to the</Text>
<TextLink external href={'https://grafana.com/docs/grafana/latest/alerting/set-up/migrating-alerts/'}>
Grafana Alerting Migration Guide
</TextLink>
</Stack>
</Stack>
</ContentBox>
<ContentBox className={styles.ctaBlock}>
<CallToActionCard
className={styles.ctaStyle}
message={'Start the upgrade to the new Grafana Alerting.'}
footer={footer}
callToActionElement={cta}
/>
</ContentBox>
</div>
);
};
function ContentBox({ children, className }: React.PropsWithChildren<{ className?: string }>) {
const styles = useStyles2(getContentBoxStyles);
return <div className={cx(styles.box, className)}>{children}</div>;
}
const getContentBoxStyles = (theme: GrafanaTheme2) => {
const color = theme.colors['warning'];
return {
box: css({
padding: theme.spacing(2),
backgroundColor: theme.colors.background.secondary,
borderRadius: theme.shape.radius.default,
}),
warningIcon: css({
color: color.text,
}),
grid: css({
display: 'grid',
gridTemplateRows: 'min-content auto auto',
gridTemplateColumns: '1fr 1fr 1fr 1fr 1fr',
gap: theme.spacing(2),
}),
list: css({
margin: `${theme.spacing(0)} ${theme.spacing(2)}`,
'& > li': {
marginBottom: theme.spacing(1),
},
}),
ctaStyle: css({
textAlign: 'center',
}),
processBlock: css({
gridColumn: '1 / span 2',
justifyContent: 'space-between',
}),
getStartedBlock: css({
gridColumn: '3 / span 3',
justifyContent: 'space-between',
}),
ctaBlock: css({
gridColumn: '1 / span 5',
}),
header: css({
marginBottom: theme.spacing(2),
}),
step: css({
paddingLeft: theme.spacing(2),
}),
};
};
const AlertTabContentWrapper = () => {
const columns = useAlertColumns();
const filterParam = 'alertFilter';