mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Rules migration] UI updates (#207789)
## Summary [Internal link](https://github.com/elastic/security-team/issues/10820) to the feature details This PR includes next improvements and fixes ### Improvements 1. Updated copies 2. Added `Last updated` label in Translation Rule details flyout <img width="1274" alt="Screenshot 2025-01-22 at 14 15 24 copy" src="https://github.com/user-attachments/assets/6974698f-3a26-48f1-96fc-d5458aa81a0a" /> 3. Added rule installation information callout in Translation Rule details flyout <img width="1274" alt="Screenshot 2025-01-22 at 14 15 24" src="https://github.com/user-attachments/assets/c350f0c2-8acf-4821-99a8-f6b510ae8dc5" /> 4. Added horizontal line underneath the Translation Rules header > [!NOTE] > This feature needs `siemMigrationsEnabled` experimental flag enabled to work.
This commit is contained in:
parent
0a577beec5
commit
835141857a
10 changed files with 158 additions and 27 deletions
|
@ -19,7 +19,8 @@ export const siemMigrationsLinks: LinkItem = {
|
|||
id: SecurityPageName.siemMigrationsRules,
|
||||
title: SIEM_MIGRATIONS_RULES,
|
||||
description: i18n.translate('xpack.securitySolution.appLinks.siemMigrationsRulesDescription', {
|
||||
defaultMessage: 'SIEM Rule Migrations.',
|
||||
defaultMessage:
|
||||
'Our generative AI powered SIEM migration tool automates some of the most time consuming migrations tasks and processed.',
|
||||
}),
|
||||
landingIcon: SiemMigrationsIcon,
|
||||
path: SIEM_MIGRATIONS_RULES_PATH,
|
||||
|
|
|
@ -48,6 +48,7 @@ import {
|
|||
isMigrationCustomRule,
|
||||
} from '../../../../../common/siem_migrations/rules/utils';
|
||||
import { useUpdateMigrationRules } from '../../logic/use_update_migration_rules';
|
||||
import { UpdatedByLabel } from './updated_by';
|
||||
|
||||
/*
|
||||
* Fixes tabs to the top and allows the content to scroll.
|
||||
|
@ -244,7 +245,8 @@ export const MigrationRuleDetailsFlyout: React.FC<MigrationRuleDetailsFlyoutProp
|
|||
i18n.UNKNOWN_MIGRATION_RULE_TITLE}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiSpacer size="s" />
|
||||
<UpdatedByLabel ruleMigration={ruleMigration} />
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiSkeletonLoading
|
||||
|
|
|
@ -10,29 +10,37 @@ import React from 'react';
|
|||
import type { IconType } from '@elastic/eui';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
import {
|
||||
RuleMigrationTranslationResultEnum,
|
||||
type RuleMigration,
|
||||
type RuleMigrationTranslationResult,
|
||||
} from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
|
||||
import * as i18n from './translations';
|
||||
|
||||
type RuleMigrationTranslationCallOutMode = RuleMigrationTranslationResult | 'mapped';
|
||||
|
||||
const getCallOutInfo = (
|
||||
translationResult: RuleMigrationTranslationResult
|
||||
mode: RuleMigrationTranslationCallOutMode
|
||||
): { title: string; message?: string; icon: IconType; color: 'success' | 'warning' | 'danger' } => {
|
||||
switch (translationResult) {
|
||||
case RuleMigrationTranslationResultEnum.full:
|
||||
switch (mode) {
|
||||
case 'mapped':
|
||||
return {
|
||||
title: i18n.CALLOUT_MAPPED_TRANSLATED_RULE_TITLE,
|
||||
icon: 'checkInCircleFilled',
|
||||
color: 'success',
|
||||
};
|
||||
case 'full':
|
||||
return {
|
||||
title: i18n.CALLOUT_TRANSLATED_RULE_TITLE,
|
||||
icon: 'checkInCircleFilled',
|
||||
color: 'success',
|
||||
};
|
||||
case RuleMigrationTranslationResultEnum.partial:
|
||||
case 'partial':
|
||||
return {
|
||||
title: i18n.CALLOUT_PARTIALLY_TRANSLATED_RULE_TITLE,
|
||||
message: i18n.CALLOUT_PARTIALLY_TRANSLATED_RULE_DESCRIPTION,
|
||||
icon: 'warningFilled',
|
||||
color: 'warning',
|
||||
};
|
||||
case RuleMigrationTranslationResultEnum.untranslatable:
|
||||
case 'untranslatable':
|
||||
return {
|
||||
title: i18n.CALLOUT_NOT_TRANSLATED_RULE_TITLE,
|
||||
message: i18n.CALLOUT_NOT_TRANSLATED_RULE_DESCRIPTION,
|
||||
|
@ -43,23 +51,29 @@ const getCallOutInfo = (
|
|||
};
|
||||
|
||||
export interface TranslationCallOutProps {
|
||||
translationResult: RuleMigrationTranslationResult;
|
||||
ruleMigration: RuleMigration;
|
||||
}
|
||||
|
||||
export const TranslationCallOut: FC<TranslationCallOutProps> = React.memo(
|
||||
({ translationResult }) => {
|
||||
const { title, message, icon, color } = getCallOutInfo(translationResult);
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
color={color}
|
||||
title={title}
|
||||
iconType={icon}
|
||||
data-test-subj={`ruleMigrationCallOut-${translationResult}`}
|
||||
>
|
||||
{message}
|
||||
</EuiCallOut>
|
||||
);
|
||||
export const TranslationCallOut: FC<TranslationCallOutProps> = React.memo(({ ruleMigration }) => {
|
||||
if (!ruleMigration.translation_result) {
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
const mode = ruleMigration.elastic_rule?.prebuilt_rule_id
|
||||
? 'mapped'
|
||||
: ruleMigration.translation_result;
|
||||
const { title, message, icon, color } = getCallOutInfo(mode);
|
||||
|
||||
return (
|
||||
<EuiCallOut
|
||||
color={color}
|
||||
title={title}
|
||||
iconType={icon}
|
||||
size={'s'}
|
||||
data-test-subj={`ruleMigrationCallOut-${mode}`}
|
||||
>
|
||||
{message}
|
||||
</EuiCallOut>
|
||||
);
|
||||
});
|
||||
TranslationCallOut.displayName = 'TranslationCallOut';
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { useMemo } from 'react';
|
|||
import {
|
||||
EuiAccordion,
|
||||
EuiBadge,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
|
@ -18,6 +19,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { RuleTranslationResult } from '../../../../../../../common/siem_migrations/constants';
|
||||
import type { RuleResponse } from '../../../../../../../common/api/detection_engine';
|
||||
import type { RuleMigration } from '../../../../../../../common/siem_migrations/model/rule_migration.gen';
|
||||
import { TranslationTabHeader } from './header';
|
||||
|
@ -78,7 +80,7 @@ export const TranslationTab: React.FC<TranslationTabProps> = React.memo(
|
|||
<EuiSpacer size="m" />
|
||||
{ruleMigration.translation_result && !isInstalled && (
|
||||
<>
|
||||
<TranslationCallOut translationResult={ruleMigration.translation_result} />
|
||||
<TranslationCallOut ruleMigration={ruleMigration} />
|
||||
<EuiSpacer size="m" />
|
||||
</>
|
||||
)}
|
||||
|
@ -131,6 +133,19 @@ export const TranslationTab: React.FC<TranslationTabProps> = React.memo(
|
|||
</EuiSplitPanel.Outer>
|
||||
</EuiFlexItem>
|
||||
</EuiAccordion>
|
||||
{ruleMigration.translation_result === RuleTranslationResult.FULL && (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiCallOut
|
||||
color={'primary'}
|
||||
title={i18n.CALLOUT_TRANSLATED_RULE_INFO_TITLE}
|
||||
iconType={'iInCircle'}
|
||||
size={'s'}
|
||||
>
|
||||
{i18n.CALLOUT_TRANSLATED_RULE_INFO_DESCRIPTION}
|
||||
</EuiCallOut>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,14 @@ export const CALLOUT_TRANSLATED_RULE_TITLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const CALLOUT_MAPPED_TRANSLATED_RULE_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.mappedTranslatedRuleCalloutTitle',
|
||||
{
|
||||
defaultMessage:
|
||||
'This rule was mapped to an Elastic authored rule. Click Install & enable rule to complete migration. You can fine-tune it later.',
|
||||
}
|
||||
);
|
||||
|
||||
export const CALLOUT_PARTIALLY_TRANSLATED_RULE_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutTitle',
|
||||
{
|
||||
|
@ -114,3 +122,18 @@ export const CALLOUT_NOT_TRANSLATED_RULE_DESCRIPTION = i18n.translate(
|
|||
'This might be caused by feature differences between SIEM products. If possible, update the rule manually.',
|
||||
}
|
||||
);
|
||||
|
||||
export const CALLOUT_TRANSLATED_RULE_INFO_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutTitle',
|
||||
{
|
||||
defaultMessage: 'Translation successful. Install the rule to customize it.',
|
||||
}
|
||||
);
|
||||
|
||||
export const CALLOUT_TRANSLATED_RULE_INFO_DESCRIPTION = i18n.translate(
|
||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.translationTab.partiallyTranslatedRuleCalloutDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'After you install the rule, you can modify or update it with full access to all features.',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -41,3 +41,10 @@ export const CLOSE_BUTTON_LABEL = i18n.translate(
|
|||
defaultMessage: 'Close',
|
||||
}
|
||||
);
|
||||
|
||||
export const LAST_UPDATED_LABEL = i18n.translate(
|
||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.lastUpdatedLabel',
|
||||
{
|
||||
defaultMessage: 'Last updated',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
|
||||
import { FormattedDate } from '../../../../../common/components/formatted_date';
|
||||
import { useBulkGetUserProfiles } from '../../../../../common/components/user_profiles/use_bulk_get_user_profiles';
|
||||
import type { RuleMigration } from '../../../../../../common/siem_migrations/model/rule_migration.gen';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
interface UpdatedByLabelProps {
|
||||
ruleMigration: RuleMigration;
|
||||
}
|
||||
|
||||
export const UpdatedByLabel: React.FC<UpdatedByLabelProps> = React.memo(
|
||||
({ ruleMigration }: UpdatedByLabelProps) => {
|
||||
const userProfileId = useMemo(
|
||||
() => new Set([ruleMigration.updated_by ?? ruleMigration.created_by]),
|
||||
[ruleMigration.created_by, ruleMigration.updated_by]
|
||||
);
|
||||
const { isLoading: isLoadingUserProfiles, data: userProfiles } = useBulkGetUserProfiles({
|
||||
uids: userProfileId,
|
||||
});
|
||||
|
||||
if (isLoadingUserProfiles || !userProfiles?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const userProfile = userProfiles[0];
|
||||
const updatedBy = userProfile.user.full_name ?? userProfile.user.username;
|
||||
const updatedAt = ruleMigration.updated_at ?? ruleMigration['@timestamp'];
|
||||
return (
|
||||
<EuiText size="xs">
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.siemMigrations.rules.translationDetails.updatedByLabel"
|
||||
defaultMessage="{updated}: {by} on {date}"
|
||||
values={{
|
||||
updated: <b>{i18n.LAST_UPDATED_LABEL}</b>,
|
||||
by: updatedBy,
|
||||
date: <FormattedDate value={updatedAt} fieldName="updated_at" />,
|
||||
}}
|
||||
/>
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
);
|
||||
UpdatedByLabel.displayName = 'UpdatedByLabel';
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const LAST_UPDATED_LABEL = i18n.translate(
|
||||
'xpack.securitySolution.siemMigrations.rules.translationDetails.lastUpdated.label',
|
||||
{
|
||||
defaultMessage: 'Last updated',
|
||||
}
|
||||
);
|
|
@ -135,7 +135,7 @@ export const MigrationRulesPage: React.FC<MigrationRulesPageProps> = React.memo(
|
|||
<MissingPrivilegesCallOut />
|
||||
|
||||
<SecuritySolutionPageWrapper>
|
||||
<HeaderPage title={pageTitle}>
|
||||
<HeaderPage title={pageTitle} border>
|
||||
<HeaderButtons
|
||||
ruleMigrationsStats={ruleMigrationsStats}
|
||||
selectedMigrationId={migrationId}
|
||||
|
|
|
@ -30,7 +30,7 @@ export const PageTitle: React.FC = React.memo(() => {
|
|||
|
||||
<EuiFlexItem
|
||||
css={css`
|
||||
margin: ${euiTheme.size.m} 0 0 ${euiTheme.size.m};
|
||||
margin: ${euiTheme.size.s} 0 0 ${euiTheme.size.m};
|
||||
`}
|
||||
grow={false}
|
||||
>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue