]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-23299 Update conditions dialog
authorViktor Vorona <viktor.vorona@sonarsource.com>
Fri, 25 Oct 2024 14:41:35 +0000 (16:41 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 5 Nov 2024 20:03:01 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/UpdateConditionsFromOtherModeModal.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-gates/utils.ts
server/sonar-web/src/main/js/queries/quality-gates.ts
server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index c815e3a8daa3f0ebb2fe0fba0ef2a6dda77ca751..be3d0fb8f827d82145979c4298d5744105abd70e 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+import { Button, Spinner } from '@sonarsource/echoes-react';
 import { uniqBy } from 'lodash';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
@@ -31,7 +32,6 @@ import {
   LightPrimary,
   Link,
   Note,
-  Spinner,
   SubHeading,
 } from '~design-system';
 import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
@@ -42,9 +42,16 @@ import { ModalProps } from '../../../components/controls/ModalButton';
 import { DocLink } from '../../../helpers/doc-links';
 import { useDocUrl } from '../../../helpers/docs';
 import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
+import { useStandardExperienceMode } from '../../../queries/settings';
+import { MetricKey } from '../../../sonar-aligned/types/metrics';
 import { Feature } from '../../../types/features';
 import { CaycStatus, Condition as ConditionType, QualityGate } from '../../../types/types';
-import { groupAndSortByPriorityConditions, isQualityGateOptimized } from '../utils';
+import {
+  groupAndSortByPriorityConditions,
+  isQualityGateOptimized,
+  MQR_CONDITIONS_MAP,
+  STANDARD_CONDITIONS_MAP,
+} from '../utils';
 import AddConditionModal from './AddConditionModal';
 import AIGeneratedIcon from './AIGeneratedIcon';
 import CaycCompliantBanner from './CaycCompliantBanner';
@@ -54,6 +61,7 @@ import CaycFixOptimizeBanner from './CaycFixOptimizeBanner';
 import CaycReviewUpdateConditionsModal from './ConditionReviewAndUpdateModal';
 import ConditionsTable from './ConditionsTable';
 import QGRecommendedIcon from './QGRecommendedIcon';
+import UpdateConditionsFromOtherModeModal from './UpdateConditionsFromOtherModeModal';
 
 interface Props {
   isFetching?: boolean;
@@ -66,6 +74,7 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
   const [editing, setEditing] = React.useState<boolean>(caycStatus === CaycStatus.NonCompliant);
   const metrics = useMetrics();
   const { hasFeature } = useAvailableFeatures();
+  const { data: isStandardMode, isLoading } = useStandardExperienceMode();
 
   const canEdit = Boolean(actions?.manageConditions);
   const existingConditions = conditions.filter((condition) => metrics[condition.metric]);
@@ -116,10 +125,11 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
     [qualityGate, conditions, metrics, isOptimizing, canEdit],
   );
 
+  const conditionsToOtherModeMap = isStandardMode ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+
   return (
-    <div>
+    <Spinner isLoading={isLoading}>
       <CaYCConditionsSimplificationGuide qualityGate={qualityGate} />
-
       {isBuiltIn && (
         <div className="sw-flex sw-items-center">
           <QGRecommendedIcon className="sw-mr-1" />
@@ -138,7 +148,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
           </LightLabel>
         </div>
       )}
-
       {isAICodeAssuranceQualityGate && (
         <div className="sw-flex sw-items-center sw-mt-2">
           <AIGeneratedIcon className="sw-mr-1" />
@@ -157,7 +166,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
           </LightLabel>
         </div>
       )}
-
       {isCompliantCustomQualityGate && !isOptimizing && <CaycCompliantBanner />}
       {isCompliantCustomQualityGate && isOptimizing && canEdit && (
         <CaycFixOptimizeBanner renderCaycModal={renderCaycModal} isOptimizing />
@@ -165,7 +173,26 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
       {caycStatus === CaycStatus.NonCompliant && canEdit && (
         <CaycFixOptimizeBanner renderCaycModal={renderCaycModal} />
       )}
-
+      <UpdateConditionsFromOtherModeModal
+        qualityGateName={qualityGate.name}
+        newCodeConditions={newCodeConditions.filter(
+          (c) => conditionsToOtherModeMap[c.metric as MetricKey] !== undefined,
+        )}
+        overallCodeConditions={overallCodeConditions.filter(
+          (c) => conditionsToOtherModeMap[c.metric as MetricKey] !== undefined,
+        )}
+      >
+        {/* TODO test example  */}
+        <Button>Review and update metrics</Button>
+      </UpdateConditionsFromOtherModeModal>
+      <UpdateConditionsFromOtherModeModal
+        qualityGateName={qualityGate.name}
+        isSingleMetric
+        condition={newCodeConditions.find((c) => c.metric.includes('rating'))!}
+      >
+        {/* TODO test example  */}
+        <Button>Test single</Button>
+      </UpdateConditionsFromOtherModeModal>
       <header className="sw-flex sw-items-center sw-mt-9 sw-mb-4 sw-justify-between">
         <div className="sw-flex">
           <HeadingDark className="sw-typo-lg-semibold sw-m-0">
@@ -185,7 +212,7 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
               <HelperHintIcon />
             </DocHelpTooltip>
           )}
-          <Spinner loading={isFetching} className="sw-ml-4 sw-mt-1" />
+          <Spinner isLoading={isFetching} className="sw-ml-4 sw-mt-1" />
         </div>
         <div>
           {(caycStatus === CaycStatus.NonCompliant || editing) && canEdit && (
@@ -193,7 +220,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
           )}
         </div>
       </header>
-
       {uniqDuplicates.length > 0 && (
         <FlagMessage variant="warning" className="sw-flex sw-mb-4">
           <div>
@@ -206,7 +232,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
           </div>
         </FlagMessage>
       )}
-
       <div className="sw-flex sw-flex-col sw-gap-8">
         {caycConditions.length > 0 && (
           <div>
@@ -288,7 +313,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
           </div>
         )}
       </div>
-
       {caycStatus !== CaycStatus.NonCompliant && !editing && canEdit && (
         <div className="sw-mt-4 it__qg-unfollow-cayc">
           <SubHeading as="p" className="sw-mb-2 sw-typo-default">
@@ -305,12 +329,11 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
           </ButtonSecondary>
         </div>
       )}
-
       {existingConditions.length === 0 && (
         <div className="sw-mt-4 sw-typo-default">
           <LightPrimary as="p">{translate('quality_gates.no_conditions')}</LightPrimary>
         </div>
       )}
-    </div>
+    </Spinner>
   );
 }
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/UpdateConditionsFromOtherModeModal.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/UpdateConditionsFromOtherModeModal.tsx
new file mode 100644 (file)
index 0000000..50775c7
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import {
+  Button,
+  ButtonVariety,
+  Heading,
+  IconArrowRight,
+  Modal,
+  ModalSize,
+  Text,
+  TextSize,
+} from '@sonarsource/echoes-react';
+import * as React from 'react';
+import { FormattedMessage, useIntl } from 'react-intl';
+import { useMetrics } from '../../../app/components/metrics/withMetricsContext';
+import DocumentationLink from '../../../components/common/DocumentationLink';
+import { ContentCell, FlagMessageV2, Table, TableRow } from '../../../design-system';
+import { DocLink } from '../../../helpers/doc-links';
+import { translate } from '../../../helpers/l10n';
+import { getOperatorLabel } from '../../../helpers/qualityGates';
+import { useUpdateOrDeleteConditionsMutation } from '../../../queries/quality-gates';
+import { useStandardExperienceMode } from '../../../queries/settings';
+import { MetricKey } from '../../../sonar-aligned/types/metrics';
+import { Condition } from '../../../types/types';
+import {
+  getLocalizedMetricNameNoDiffMetric,
+  MQR_CONDITIONS_MAP,
+  STANDARD_CONDITIONS_MAP,
+} from '../utils';
+import ConditionValue from './ConditionValue';
+
+type Props = React.PropsWithChildren & { qualityGateName: string } & (
+    | {
+        condition?: never;
+        isSingleMetric?: false;
+        newCodeConditions: Condition[];
+        overallCodeConditions: Condition[];
+      }
+    | {
+        condition: Condition;
+        isSingleMetric: true;
+        newCodeConditions?: never;
+        overallCodeConditions?: never;
+      }
+  );
+
+export default function UpdateConditionsFromOtherModeModal({
+  newCodeConditions,
+  overallCodeConditions,
+  qualityGateName,
+  isSingleMetric,
+  condition,
+  children,
+}: Readonly<Props>) {
+  const { data: isStandard } = useStandardExperienceMode();
+  const [isOpen, setOpen] = React.useState(false);
+  const [error, setError] = React.useState(false);
+  const intl = useIntl();
+  const mapper = isStandard ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+  const { mutate: updateConditions, isPending } = useUpdateOrDeleteConditionsMutation(
+    qualityGateName,
+    isSingleMetric,
+  );
+
+  const onSubmit = () => {
+    const conditions = isSingleMetric
+      ? [condition]
+      : [...newCodeConditions, ...overallCodeConditions];
+
+    updateConditions(
+      conditions.map((c) => ({ ...c, metric: mapper[c.metric as MetricKey] })),
+      {
+        onSuccess: () => setOpen(false),
+        onError: () => setError(true),
+      },
+    );
+  };
+
+  return (
+    <Modal
+      isOpen={isOpen}
+      onOpenChange={setOpen}
+      title={intl.formatMessage(
+        {
+          id: `quality_gates.update_conditions.header${isSingleMetric ? '.single_metric' : ''}`,
+        },
+        { qualityGate: qualityGateName },
+      )}
+      size={isSingleMetric ? ModalSize.Default : ModalSize.Wide}
+      primaryButton={
+        <Button
+          isDisabled={isPending}
+          isLoading={isPending}
+          onClick={onSubmit}
+          id="update-metrics-button"
+          variety={ButtonVariety.Primary}
+        >
+          {intl.formatMessage({
+            id: isSingleMetric ? 'update_verb' : 'quality_gates.update_conditions.update_metrics',
+          })}
+        </Button>
+      }
+      secondaryButton={<Button onClick={() => setOpen(false)}>{translate('cancel')}</Button>}
+      content={
+        <>
+          {error && (
+            <div>
+              <FlagMessageV2 variant="error">
+                {intl.formatMessage({ id: 'quality_gates.update_conditions.error' })}
+              </FlagMessageV2>
+            </div>
+          )}
+          <Text>
+            <FormattedMessage
+              id="quality_gates.update_conditions.description.line1"
+              values={{
+                b: (chunks) => <Text isHighlighted>{chunks}</Text>,
+                mode: intl.formatMessage({
+                  id: `settings.mode.${isStandard ? 'standard' : 'mqr'}.name`,
+                }),
+              }}
+            />
+          </Text>
+
+          {isSingleMetric && <SingleMetric condition={condition} />}
+          <Text>
+            <FormattedMessage
+              id="quality_gates.update_conditions.description.line2"
+              values={{
+                b: (chunks) => <Text isHighlighted>{chunks}</Text>,
+              }}
+            />
+            <br />
+            <br />
+            <FormattedMessage
+              id="quality_gates.update_conditions.description.line3"
+              values={{
+                mode: intl.formatMessage({
+                  id: `settings.mode.${isStandard ? 'standard' : 'mqr'}.name`,
+                }),
+                link: (
+                  <DocumentationLink to={isStandard ? DocLink.ModeStandard : DocLink.ModeMQR}>
+                    {intl.formatMessage({
+                      id: 'quality_gates.update_conditions.description.link',
+                    })}
+                  </DocumentationLink>
+                ),
+              }}
+            />
+          </Text>
+          {!isSingleMetric && (
+            <>
+              {newCodeConditions.length > 0 && (
+                <>
+                  <Heading as="h3" className="sw-mt-8">
+                    {intl.formatMessage({ id: 'overview.new_code' })}
+                  </Heading>
+                  <Table
+                    columnCount={3}
+                    columnWidths={['35%', '35%', 'auto']}
+                    className="sw-my-2"
+                    header={<Header />}
+                  >
+                    {newCodeConditions.map((condition) => (
+                      <ConditionRow condition={condition} key={condition.id} />
+                    ))}
+                  </Table>
+                </>
+              )}
+              {overallCodeConditions.length > 0 && (
+                <>
+                  <Heading as="h3" className="sw-mt-8">
+                    {intl.formatMessage({ id: 'overview.overall_code' })}
+                  </Heading>
+                  <Table
+                    columnCount={3}
+                    columnWidths={['35%', '35%', 'auto']}
+                    className="sw-my-2"
+                    header={<Header />}
+                  >
+                    {overallCodeConditions.map((condition) => (
+                      <ConditionRow condition={condition} key={condition.id} />
+                    ))}
+                  </Table>
+                </>
+              )}
+            </>
+          )}
+        </>
+      }
+    >
+      {React.cloneElement(children as React.ReactElement, { onClick: () => setOpen(true) })}
+    </Modal>
+  );
+}
+
+function SingleMetric({ condition }: Readonly<{ condition: Condition }>) {
+  const { data: isStandard } = useStandardExperienceMode();
+  const intl = useIntl();
+  const metrics = useMetrics();
+  const metric = metrics[condition.metric];
+  const mapper = isStandard ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+  const metricFromOtherMode = mapper[metric.key as MetricKey];
+
+  return (
+    <div className="sw-flex sw-justify-between sw-my-8 sw-items-center sw-w-[80%]">
+      <div>
+        <Text as="div" size={TextSize.Small}>
+          {intl.formatMessage({
+            id: `quality_gates.update_conditions.${isStandard ? 'mqr' : 'standard'}_mode_header`,
+          })}
+        </Text>
+        <Text as="div" size={TextSize.Small} isHighlighted>
+          {getLocalizedMetricNameNoDiffMetric(metrics[condition.metric], metrics)}
+        </Text>
+      </div>
+      <IconArrowRight />
+      <div>
+        {metricFromOtherMode ? (
+          <>
+            <Text as="div" size={TextSize.Small}>
+              {intl.formatMessage({
+                id: `quality_gates.update_conditions.${isStandard ? 'standard' : 'mqr'}_mode_header`,
+              })}
+            </Text>
+            <Text as="div" size={TextSize.Small} isHighlighted>
+              {getLocalizedMetricNameNoDiffMetric(metrics[metricFromOtherMode], metrics)}
+            </Text>
+          </>
+        ) : (
+          <Text size={TextSize.Small}>
+            {intl.formatMessage({ id: 'quality_gates.update_conditions.removed' })}
+          </Text>
+        )}
+      </div>
+    </div>
+  );
+}
+
+function Header() {
+  const intl = useIntl();
+  const { data: isStandard } = useStandardExperienceMode();
+
+  return (
+    <TableRow>
+      <ContentCell className="sw-justify-between sw-pr-4">
+        <Heading as="h4" className="sw-typo-semibold sw-m-0 sw-whitespace-nowrap">
+          {intl.formatMessage({
+            id: `quality_gates.update_conditions.${isStandard ? 'mqr' : 'standard'}_mode_header`,
+          })}
+        </Heading>
+        <IconArrowRight />
+      </ContentCell>
+      <ContentCell>
+        <Heading as="h4" className="sw-typo-semibold sw-m-0 sw-whitespace-nowrap">
+          {intl.formatMessage({
+            id: `quality_gates.update_conditions.${isStandard ? 'standard' : 'mqr'}_mode_header`,
+          })}
+        </Heading>
+      </ContentCell>
+      <ContentCell>
+        <Heading as="h4" className="sw-typo-semibold sw-m-0 sw-whitespace-nowrap">
+          {intl.formatMessage({ id: 'quality_gates.update_conditions.operator_and_value_header' })}
+        </Heading>
+      </ContentCell>
+      <ContentCell />
+    </TableRow>
+  );
+}
+
+function ConditionRow({ condition }: Readonly<{ condition: Condition }>) {
+  const { data: isStandard } = useStandardExperienceMode();
+  const intl = useIntl();
+  const metrics = useMetrics();
+  const { op = 'GT' } = condition;
+  const metric = metrics[condition.metric];
+  const mapper = isStandard ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+  const metricFromOtherMode = mapper[metric?.key as MetricKey];
+
+  return (
+    <TableRow>
+      <ContentCell>{getLocalizedMetricNameNoDiffMetric(metric, metrics)}</ContentCell>
+      <ContentCell>
+        {metricFromOtherMode ? (
+          getLocalizedMetricNameNoDiffMetric(metrics[metricFromOtherMode], metrics)
+        ) : (
+          <Text colorOverride="echoes-color-text-danger">
+            {intl.formatMessage({ id: 'quality_gates.update_conditions.removed' })}
+          </Text>
+        )}
+      </ContentCell>
+
+      <ContentCell className="sw-whitespace-nowrap">
+        {metricFromOtherMode && (
+          <Text isSubdued>
+            {getOperatorLabel(op, metric)}&nbsp;
+            <ConditionValue condition={condition} metric={metric} />
+          </Text>
+        )}
+      </ContentCell>
+    </TableRow>
+  );
+}
index 8cfc4f50e87d61443e39b8e75c4c220b72e95465..99e3aad55b373677e5eb6cbec64b7655391fa09e 100644 (file)
@@ -20,6 +20,7 @@
 
 import { sortBy } from 'lodash';
 import { MetricKey } from '~sonar-aligned/types/metrics';
+import { SOFTWARE_QUALITY_RATING_METRICS_MAP } from '../../helpers/constants';
 import { getLocalizedMetricName } from '../../helpers/l10n';
 import { isDiffMetric } from '../../helpers/measures';
 import { CaycStatus, Condition, Dict, Group, Metric, QualityGate } from '../../types/types';
@@ -148,6 +149,33 @@ const CAYC_CONDITIONS_WITH_FIXED_VALUE: AllCaycMetricKeys[] = [
 ];
 const NON_EDITABLE_CONDITIONS: MetricKey[] = [MetricKey.prioritized_rule_issues];
 
+export const STANDARD_CONDITIONS_MAP: Partial<Record<MetricKey, MetricKey>> = {
+  [MetricKey.new_blocker_violations]: MetricKey.new_software_quality_blocker_issues,
+  [MetricKey.new_critical_violations]: MetricKey.new_software_quality_high_issues,
+  [MetricKey.new_major_violations]: MetricKey.new_software_quality_medium_issues,
+  [MetricKey.new_minor_violations]: MetricKey.new_software_quality_low_issues,
+  [MetricKey.new_info_violations]: MetricKey.new_software_quality_info_issues,
+  [MetricKey.blocker_violations]: MetricKey.software_quality_blocker_issues,
+  [MetricKey.critical_violations]: MetricKey.software_quality_high_issues,
+  [MetricKey.major_violations]: MetricKey.software_quality_medium_issues,
+  [MetricKey.minor_violations]: MetricKey.software_quality_low_issues,
+  [MetricKey.info_violations]: MetricKey.software_quality_info_issues,
+  [MetricKey.new_vulnerabilities]: MetricKey.new_software_quality_security_issues,
+  [MetricKey.new_bugs]: MetricKey.new_software_quality_reliability_issues,
+  [MetricKey.new_code_smells]: MetricKey.new_software_quality_maintainability_issues,
+  [MetricKey.vulnerabilities]: MetricKey.software_quality_security_issues,
+  [MetricKey.bugs]: MetricKey.software_quality_reliability_issues,
+  [MetricKey.code_smells]: MetricKey.software_quality_maintainability_issues,
+  ...SOFTWARE_QUALITY_RATING_METRICS_MAP,
+};
+
+export const MQR_CONDITIONS_MAP: Partial<Record<MetricKey, MetricKey | null>> = {
+  ...Object.fromEntries(
+    Object.entries(STANDARD_CONDITIONS_MAP).map(([key, value]) => [value, key]),
+  ),
+  [MetricKey.high_impact_accepted_issues]: null,
+};
+
 export function isConditionWithFixedValue(condition: Condition) {
   return CAYC_CONDITIONS_WITH_FIXED_VALUE.includes(condition.metric as OptimizedCaycMetricKeys);
 }
index 5b2524e4be290f35e1754a446ed04ca42d2c0dbd..acc880b1e731a8d57a11f0e2e1096c596f2517c0 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 import { queryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { useIntl } from 'react-intl';
 import { addGlobalSuccessMessage } from '~design-system';
 import { BranchParameters } from '~sonar-aligned/types/branch-like';
 import {
@@ -238,6 +239,42 @@ export function useUpdateConditionMutation(gateName: string) {
   });
 }
 
+export function useUpdateOrDeleteConditionsMutation(gateName: string, isSingleMetric?: boolean) {
+  const queryClient = useQueryClient();
+  const intl = useIntl();
+
+  return useMutation({
+    mutationFn: (
+      conditions: (Omit<Condition, 'metric'> & { metric: string | null | undefined })[],
+    ) => {
+      const promiseArr = conditions.map((condition) =>
+        condition.metric
+          ? updateCondition(condition as Condition)
+          : deleteCondition({ id: condition.id }),
+      );
+
+      return Promise.all(promiseArr);
+    },
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: qualityQuery.list() });
+      queryClient.invalidateQueries({ queryKey: qualityQuery.detail(gateName) });
+      addGlobalSuccessMessage(
+        intl.formatMessage(
+          {
+            id: isSingleMetric
+              ? 'quality_gates.condition_updated'
+              : 'quality_gates.conditions_updated_to_the_mode',
+          },
+          { qualityGateName: gateName },
+        ),
+      );
+    },
+    onError: () => {
+      queryClient.invalidateQueries({ queryKey: qualityQuery.detail(gateName) });
+    },
+  });
+}
+
 export function useDeleteConditionMutation(gateName: string) {
   const queryClient = useQueryClient();
 
index d711330cfa7ef0182cad815c7aedfbc6aed13702..db99c08030be91d63bdb1e0f75f705f03625f510 100644 (file)
@@ -22,13 +22,16 @@ export enum MetricKey {
   accepted_issues = 'accepted_issues',
   alert_status = 'alert_status',
   blocker_violations = 'blocker_violations',
+  software_quality_blocker_issues = 'software_quality_blocker_issues',
   branch_coverage = 'branch_coverage',
   bugs = 'bugs',
+  software_quality_reliability_issues = 'software_quality_reliability_issues',
   burned_budget = 'burned_budget',
   business_value = 'business_value',
   class_complexity = 'class_complexity',
   classes = 'classes',
   code_smells = 'code_smells',
+  software_quality_maintainability_issues = 'software_quality_maintainability_issues',
   cognitive_complexity = 'cognitive_complexity',
   comment_lines = 'comment_lines',
   comment_lines_data = 'comment_lines_data',
@@ -40,6 +43,7 @@ export enum MetricKey {
   confirmed_issues = 'confirmed_issues',
   coverage = 'coverage',
   critical_violations = 'critical_violations',
+  software_quality_high_issues = 'software_quality_high_issues',
   development_cost = 'development_cost',
   directories = 'directories',
   duplicated_blocks = 'duplicated_blocks',
@@ -63,6 +67,7 @@ export enum MetricKey {
   generated_ncloc = 'generated_ncloc',
   high_impact_accepted_issues = 'high_impact_accepted_issues',
   info_violations = 'info_violations',
+  software_quality_info_issues = 'software_quality_info_issues',
   issues = 'issues',
   last_change_on_maintainability_rating = 'last_change_on_maintainability_rating',
   last_change_on_releasability_rating = 'last_change_on_releasability_rating',
@@ -83,23 +88,30 @@ export enum MetricKey {
   maintainability_rating_effort = 'maintainability_rating_effort',
   software_quality_maintainability_rating_effort = 'software_quality_maintainability_rating_effort',
   major_violations = 'major_violations',
+  software_quality_medium_issues = 'software_quality_medium_issues',
   minor_violations = 'minor_violations',
+  software_quality_low_issues = 'software_quality_low_issues',
   ncloc = 'ncloc',
   ncloc_data = 'ncloc_data',
   ncloc_language_distribution = 'ncloc_language_distribution',
   new_accepted_issues = 'new_accepted_issues',
   new_blocker_violations = 'new_blocker_violations',
+  new_software_quality_blocker_issues = 'new_software_quality_blocker_issues',
   new_branch_coverage = 'new_branch_coverage',
   new_bugs = 'new_bugs',
+  new_software_quality_reliability_issues = 'new_software_quality_reliability_issues',
   new_code_smells = 'new_code_smells',
+  new_software_quality_maintainability_issues = 'new_software_quality_maintainability_issues',
   new_conditions_to_cover = 'new_conditions_to_cover',
   new_coverage = 'new_coverage',
   new_critical_violations = 'new_critical_violations',
+  new_software_quality_high_issues = 'new_software_quality_high_issues',
   new_development_cost = 'new_development_cost',
   new_duplicated_blocks = 'new_duplicated_blocks',
   new_duplicated_lines = 'new_duplicated_lines',
   new_duplicated_lines_density = 'new_duplicated_lines_density',
   new_info_violations = 'new_info_violations',
+  new_software_quality_info_issues = 'new_software_quality_info_issues',
   new_issues = 'new_issues',
   new_line_coverage = 'new_line_coverage',
   new_lines = 'new_lines',
@@ -110,7 +122,9 @@ export enum MetricKey {
   new_maintainability_rating_distribution = 'new_maintainability_rating_distribution',
   new_software_quality_maintainability_rating_distribution = 'new_software_quality_maintainability_rating_distribution',
   new_major_violations = 'new_major_violations',
+  new_software_quality_medium_issues = 'new_software_quality_medium_issues',
   new_minor_violations = 'new_minor_violations',
+  new_software_quality_low_issues = 'new_software_quality_low_issues',
   new_reliability_issues = 'new_reliability_issues',
   new_reliability_rating = 'new_reliability_rating',
   new_software_quality_reliability_rating = 'new_software_quality_reliability_rating',
@@ -138,6 +152,7 @@ export enum MetricKey {
   new_violations = 'new_violations',
   new_violations_rating = 'new_violations_rating',
   new_vulnerabilities = 'new_vulnerabilities',
+  new_software_quality_security_issues = 'new_software_quality_security_issues',
   open_issues = 'open_issues',
   prioritized_rule_issues = 'prioritized_rule_issues',
   projects = 'projects',
@@ -195,6 +210,7 @@ export enum MetricKey {
   violations = 'violations',
   violations_rating = 'violations_rating',
   vulnerabilities = 'vulnerabilities',
+  software_quality_security_issues = 'software_quality_security_issues',
   wont_fix_issues = 'wont_fix_issues',
 }
 
index e0abaec5dc30594a986b32d1df0640dc2e50409c..eb50f757551fcaabf7fb93bc2a21eec27ce3f491 100644 (file)
@@ -2446,6 +2446,7 @@ quality_gates.condition_added=Successfully added condition.
 quality_gates.update_condition=Update Condition
 quality_gates.condition_updated=Successfully updated condition.
 quality_gates.conditions_updated=Successfully updated conditions.
+quality_gates.conditions_updated_to_the_mode=The metrics of "{qualityGateName}" gate were successfully updated
 quality_gates.no_conditions=No Conditions
 quality_gates.health_icons=Project health icons represent:
 quality_gates.projects_for_default=Every project not specifically associated to a quality gate will be associated to this one by default.
@@ -2554,6 +2555,19 @@ quality_gates.ai_generated.tootltip.message=Sonar way ensures clean AI-generated
 quality_gates.ai_generated.description=Sonar way ensures {link}
 quality_gates.ai_generated.description.clean_ai_generated_code=clean AI-generated code
 quality_gates.mqr_mode_update.tooltip.message=Update the metrics of this quality gate
+quality_gates.update_conditions.update_metrics=Update metrics
+quality_gates.update_conditions.header=Update all metrics of “{qualityGate}” gate
+quality_gates.update_conditions.header.single_metric=Update this metric
+quality_gates.update_conditions.description.line1=<b>Metrics</b> of the conditions listed below will be updated to align with the {mode} mode of this instance.
+quality_gates.update_conditions.description.line2=They will be calculated differently even if the names of the conditions persist between the Standard Experience and the MQR modes. <b>Operator</b> and <b>value</b> will remain unchanged.
+quality_gates.update_conditions.description.line3=Note that the update to {mode} might cause your quality gate to fail. {link}
+quality_gates.update_conditions.description.link=For more information, refer to the documentation.
+quality_gates.update_conditions.standard_mode_header=Standard Experience Metric
+quality_gates.update_conditions.mqr_mode_header=MQR Mode Metric
+quality_gates.update_conditions.operator_and_value_header=Operator and Value
+quality_gates.update_conditions.removed=Condition will be removed
+quality_gates.update_conditions.error=Failed to update some conditions
+
 
 #------------------------------------------------------------------------------
 #