]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20156 Display banner on the project NCD page to notify project admins about...
authorAndrey Luiz <andrey.luiz@sonarsource.com>
Wed, 23 Aug 2023 08:23:09 +0000 (10:23 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 24 Aug 2023 20:03:07 +0000 (20:03 +0000)
Co-authored-by: Ambroise C <ambroise.christea@sonarsource.com>
20 files changed:
server/sonar-web/src/main/js/api/messages.ts
server/sonar-web/src/main/js/app/components/App.tsx
server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/projectNewCode/components/BranchList.tsx
server/sonar-web/src/main/js/apps/projectNewCode/components/BranchNewCodeDefinitionSettingModal.tsx
server/sonar-web/src/main/js/apps/projectNewCode/components/NewCodeDefinitionSettingReferenceBranch.tsx
server/sonar-web/src/main/js/apps/projectNewCode/components/ProjectNewCodeDefinitionApp.tsx
server/sonar-web/src/main/js/apps/projectNewCode/components/ProjectNewCodeDefinitionSelector.tsx
server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/projectNewCode/utils.ts
server/sonar-web/src/main/js/apps/settings/components/NewCodeDefinition.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/NewCodeDefinition-it.tsx
server/sonar-web/src/main/js/components/new-code-definition/NCDAutoUpdateMessage.tsx
server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionDaysOption.tsx
server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionSelector.tsx
server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionWarning.tsx
server/sonar-web/src/main/js/components/new-code-definition/utils.ts
server/sonar-web/src/main/js/components/ui/DismissableAlertComponent.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index c27e726bbc77f30db362c40102534d5e69e4ee50..6269e0e0eab5645f06a31b64c3c0909c3bb10136 100644 (file)
@@ -24,6 +24,7 @@ export enum MessageTypes {
   GlobalNcd90 = 'GLOBAL_NCD_90',
   GlobalNcdPage90 = 'GLOBAL_NCD_PAGE_90',
   ProjectNcd90 = 'PROJECT_NCD_90',
+  ProjectNcdPage90 = 'PROJECT_NCD_PAGE_90',
   BranchNcd90 = 'BRANCH_NCD_90',
 }
 
index 46aa47ac7a7b647b9968f5b7969ad5c7fb9ef8e2..f8a15634a8c9036b41a6075277a1bc392ddc24b2 100644 (file)
@@ -19,7 +19,6 @@
  */
 import * as React from 'react';
 import { Outlet } from 'react-router-dom';
-import NCDAutoUpdateMessage from '../../components/new-code-definition/NCDAutoUpdateMessage';
 import { AppState } from '../../types/appstate';
 import { GlobalSettingKeys } from '../../types/settings';
 import KeyboardShortcutsModal from './KeyboardShortcutsModal';
@@ -90,7 +89,6 @@ export class App extends React.PureComponent<Props> {
   render() {
     return (
       <>
-        <NCDAutoUpdateMessage />
         <PageTracker>{this.renderPreconnectLink()}</PageTracker>
         <Outlet />
         <KeyboardShortcutsModal />
index 2346d06ccac4854b23d9147feddb3156afaee0f9..5fba3cf935c5230b470e8dd14d517336be1f152d 100644 (file)
@@ -25,6 +25,7 @@ import { Outlet, useLocation } from 'react-router-dom';
 import A11yProvider from '../../components/a11y/A11yProvider';
 import A11ySkipLinks from '../../components/a11y/A11ySkipLinks';
 import SuggestionsProvider from '../../components/embed-docs-modal/SuggestionsProvider';
+import NCDAutoUpdateMessage from '../../components/new-code-definition/NCDAutoUpdateMessage';
 import Workspace from '../../components/workspace/Workspace';
 import GlobalFooter from './GlobalFooter';
 import StartupModal from './StartupModal';
@@ -80,6 +81,7 @@ export default function GlobalContainer() {
                           <div className="sw-sticky sw-top-0 sw-z-global-navbar">
                             <SystemAnnouncement />
                             <IndexationNotification />
+                            <NCDAutoUpdateMessage />
                             <UpdateNotification dismissable />
                             <GlobalNav location={location} />
                           </div>
index 88271399b3b7c6475dde5e4991844ecfbd583b6c..1937a131c9896c1c63104562885d51ffdf521033 100644 (file)
@@ -2,7 +2,6 @@
 
 exports[`should render correctly: default 1`] = `
 <Fragment>
-  <withCurrentUserContext(NCDAutoUpdateMessage) />
   <withRouter(withAppStateContext(PageTracker)) />
   <Outlet />
   <KeyboardShortcutsModal />
@@ -11,7 +10,6 @@ exports[`should render correctly: default 1`] = `
 
 exports[`should render correctly: with gravatar 1`] = `
 <Fragment>
-  <withCurrentUserContext(NCDAutoUpdateMessage) />
   <withRouter(withAppStateContext(PageTracker))>
     <link
       href="http://example.com"
index 87b0744bec16178140070c95f156f469dbcd61ed..8b4f0fac7d676050a0b5fad1689dac4296da3cc2 100644 (file)
@@ -36,7 +36,7 @@ interface Props {
   branchList: Branch[];
   component: Component;
   inheritedSetting: NewCodeDefinition;
-  generalSetting: NewCodeDefinition;
+  globalNewCodeDefinition: NewCodeDefinition;
 }
 
 interface State {
@@ -136,7 +136,7 @@ export default class BranchList extends React.PureComponent<Props, State> {
   };
 
   render() {
-    const { branchList, inheritedSetting, generalSetting } = this.props;
+    const { branchList, inheritedSetting, globalNewCodeDefinition } = this.props;
     const { branches, editedBranch, loading } = this.state;
 
     if (branches.length < 1) {
@@ -179,7 +179,7 @@ export default class BranchList extends React.PureComponent<Props, State> {
             component={this.props.component.key}
             onClose={this.closeEditModal}
             inheritedSetting={inheritedSetting}
-            generalSetting={generalSetting}
+            globalNewCodeDefinition={globalNewCodeDefinition}
           />
         )}
       </>
index 06d4c0571ff7a26ff7fefa8be29adb44a9be1bdc..d5b8bc266e6587e2d3c13a76372d067064e16309 100644 (file)
@@ -25,6 +25,7 @@ import { ResetButtonLink, SubmitButton } from '../../../components/controls/butt
 import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption';
 import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption';
 import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning';
+import { NewCodeDefinitionLevels } from '../../../components/new-code-definition/utils';
 import Spinner from '../../../components/ui/Spinner';
 import { toISO8601WithOffsetString } from '../../../helpers/dates';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
@@ -42,7 +43,7 @@ interface Props {
   component: string;
   onClose: (branch?: string, newSetting?: NewCodeDefinition) => void;
   inheritedSetting: NewCodeDefinition;
-  generalSetting: NewCodeDefinition;
+  globalNewCodeDefinition: NewCodeDefinition;
 }
 
 interface State {
@@ -52,7 +53,7 @@ interface State {
   isChanged: boolean;
   referenceBranch: string;
   saving: boolean;
-  selected?: NewCodeDefinitionType;
+  selectedNewCodeDefinitionType?: NewCodeDefinitionType;
 }
 
 export default class BranchNewCodeDefinitionSettingModal extends React.PureComponent<Props, State> {
@@ -61,7 +62,7 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
   constructor(props: Props) {
     super(props);
 
-    const { branch, branchList, inheritedSetting, generalSetting } = props;
+    const { branch, branchList, inheritedSetting, globalNewCodeDefinition } = props;
     const otherBranches = branchList.filter((b) => b.name !== branch.name);
     const defaultBranch = otherBranches.length > 0 ? otherBranches[0].name : '';
 
@@ -69,12 +70,12 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
       analysis: this.getValueFromProps(NewCodeDefinitionType.SpecificAnalysis) || '',
       days:
         this.getValueFromProps(NewCodeDefinitionType.NumberOfDays) ||
-        getNumberOfDaysDefaultValue(generalSetting, inheritedSetting),
+        getNumberOfDaysDefaultValue(globalNewCodeDefinition, inheritedSetting),
       isChanged: false,
       referenceBranch:
         this.getValueFromProps(NewCodeDefinitionType.ReferenceBranch) || defaultBranch,
       saving: false,
-      selected: branch.newCodePeriod?.type,
+      selectedNewCodeDefinitionType: branch.newCodePeriod?.type,
     };
   }
 
@@ -103,9 +104,15 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
     e.preventDefault();
 
     const { branch, component } = this.props;
-    const { analysis, analysisDate, days, referenceBranch, selected: type } = this.state;
+    const {
+      analysis,
+      analysisDate,
+      days,
+      referenceBranch,
+      selectedNewCodeDefinitionType: type,
+    } = this.state;
 
-    const value = getSettingValue({ type, analysis, days, referenceBranch });
+    const value = getSettingValue({ type, analysis, numberOfDays: days, referenceBranch });
 
     if (type) {
       this.setState({ saving: true });
@@ -146,13 +153,17 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
   handleSelectReferenceBranch = (referenceBranch: string) =>
     this.setState({ referenceBranch, isChanged: true });
 
-  handleSelectSetting = (selected: NewCodeDefinitionType) => {
-    this.setState((currentState) => ({ selected, isChanged: selected !== currentState.selected }));
+  handleSelectSetting = (selectedNewCodeDefinitionType: NewCodeDefinitionType) => {
+    this.setState((currentState) => ({
+      selectedNewCodeDefinitionType,
+      isChanged: selectedNewCodeDefinitionType !== currentState.selectedNewCodeDefinitionType,
+    }));
   };
 
   render() {
     const { branch, branchList } = this.props;
-    const { analysis, days, isChanged, referenceBranch, saving, selected } = this.state;
+    const { analysis, days, isChanged, referenceBranch, saving, selectedNewCodeDefinitionType } =
+      this.state;
 
     const header = translateWithParameters('baseline.new_code_period_for_branch_x', branch.name);
 
@@ -160,9 +171,9 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
     const currentSettingValue = branch.newCodePeriod?.value;
 
     const isValid = validateSetting({
-      days,
+      numberOfDays: days,
       referenceBranch,
-      selected,
+      selectedNewCodeDefinitionType,
     });
 
     return (
@@ -177,13 +188,13 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
               newCodeDefinitionType={currentSetting}
               newCodeDefinitionValue={currentSettingValue}
               isBranchSupportEnabled
-              level="branch"
+              level={NewCodeDefinitionLevels.Branch}
             />
             <div className="display-flex-column huge-spacer-bottom sw-gap-4" role="radiogroup">
               <NewCodeDefinitionPreviousVersionOption
                 isDefault={false}
                 onSelect={this.handleSelectSetting}
-                selected={selected === NewCodeDefinitionType.PreviousVersion}
+                selected={selectedNewCodeDefinitionType === NewCodeDefinitionType.PreviousVersion}
               />
               <NewCodeDefinitionDaysOption
                 days={days}
@@ -191,24 +202,27 @@ export default class BranchNewCodeDefinitionSettingModal extends React.PureCompo
                 isValid={isValid}
                 onChangeDays={this.handleSelectDays}
                 onSelect={this.handleSelectSetting}
-                selected={selected === NewCodeDefinitionType.NumberOfDays}
+                selected={selectedNewCodeDefinitionType === NewCodeDefinitionType.NumberOfDays}
+                settingLevel={NewCodeDefinitionLevels.Branch}
               />
               <NewCodeDefinitionSettingReferenceBranch
                 branchList={branchList.map(this.branchToOption)}
                 onChangeReferenceBranch={this.handleSelectReferenceBranch}
                 onSelect={this.handleSelectSetting}
                 referenceBranch={referenceBranch}
-                selected={selected === NewCodeDefinitionType.ReferenceBranch}
-                settingLevel="branch"
+                selected={selectedNewCodeDefinitionType === NewCodeDefinitionType.ReferenceBranch}
+                settingLevel={NewCodeDefinitionLevels.Branch}
               />
               {currentSetting === NewCodeDefinitionType.SpecificAnalysis && (
                 <NewCodeDefinitionSettingAnalysis
                   onSelect={noop}
-                  selected={selected === NewCodeDefinitionType.SpecificAnalysis}
+                  selected={
+                    selectedNewCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis
+                  }
                 />
               )}
             </div>
-            {selected === NewCodeDefinitionType.SpecificAnalysis && (
+            {selectedNewCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis && (
               <BranchAnalysisList
                 analysis={analysis}
                 branch={branch.name}
index 3d0aa3a30b203fe9eef9fdda40faaf31236b0de6..bc41738692a50be64b2440abdb5189f931b67a2f 100644 (file)
@@ -23,6 +23,7 @@ import { components, OptionProps } from 'react-select';
 import Select from '../../../components/controls/Select';
 import Tooltip from '../../../components/controls/Tooltip';
 import AlertErrorIcon from '../../../components/icons/AlertErrorIcon';
+import { NewCodeDefinitionLevels } from '../../../components/new-code-definition/utils';
 import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
@@ -36,7 +37,10 @@ export interface BaselineSettingReferenceBranchProps {
   onSelect: (selection: NewCodeDefinitionType) => void;
   referenceBranch: string;
   selected: boolean;
-  settingLevel: 'project' | 'branch';
+  settingLevel: Exclude<
+    NewCodeDefinitionLevels,
+    NewCodeDefinitionLevels.NewProject | NewCodeDefinitionLevels.Global
+  >;
 }
 
 export interface BranchOption {
@@ -110,7 +114,7 @@ export default function NewCodeDefinitionSettingReferenceBranch(
         </div>
         {selected && (
           <>
-            {settingLevel === 'project' && (
+            {settingLevel === NewCodeDefinitionLevels.Project && (
               <p className="spacer-top">{translate('baseline.reference_branch.description2')}</p>
             )}
             <div className="big-spacer-top display-flex-column">
index 92c1cbbe90143216aa8e9ad5046ecac725f43b06..ad353f95ca305d2931bba43ede5db6a210764d7d 100644 (file)
@@ -62,16 +62,18 @@ interface Props extends WithAvailableFeaturesProps {
 interface State {
   analysis?: string;
   branchList: Branch[];
-  currentSetting?: NewCodeDefinitionType;
-  currentSettingValue?: string;
-  days: string;
-  generalSetting?: NewCodeDefinition;
+  newCodeDefinitionType?: NewCodeDefinitionType;
+  newCodeDefinitionValue?: string;
+  previousNonCompliantValue?: string;
+  projectNcdUpdatedAt?: number;
+  numberOfDays: string;
+  globalNewCodeDefinition?: NewCodeDefinition;
   isChanged: boolean;
   loading: boolean;
-  overrideGeneralSetting?: boolean;
+  overrideGlobalNewCodeDefinition?: boolean;
   referenceBranch?: string;
   saving: boolean;
-  selected?: NewCodeDefinitionType;
+  selectedNewCodeDefinitionType?: NewCodeDefinitionType;
   success?: boolean;
 }
 
@@ -79,7 +81,7 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
   mounted = false;
   state: State = {
     branchList: [],
-    days: getNumberOfDaysDefaultValue(),
+    numberOfDays: getNumberOfDaysDefaultValue(),
     isChanged: false,
     loading: true,
     saving: false,
@@ -105,30 +107,43 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
   }
 
   getUpdatedState(params: {
-    currentSetting?: NewCodeDefinitionType;
-    currentSettingValue?: string;
-    generalSetting: NewCodeDefinition;
+    newCodeDefinitionType?: NewCodeDefinitionType;
+    newCodeDefinitionValue?: string;
+    globalNewCodeDefinition: NewCodeDefinition;
+    previousNonCompliantValue?: string;
+    projectNcdUpdatedAt?: number;
   }) {
-    const { currentSetting, currentSettingValue, generalSetting } = params;
+    const {
+      newCodeDefinitionType,
+      newCodeDefinitionValue,
+      globalNewCodeDefinition,
+      previousNonCompliantValue,
+      projectNcdUpdatedAt,
+    } = params;
     const { referenceBranch } = this.state;
 
-    const defaultDays = getNumberOfDaysDefaultValue(generalSetting);
+    const defaultDays = getNumberOfDaysDefaultValue(globalNewCodeDefinition);
 
     return {
       loading: false,
-      currentSetting,
-      currentSettingValue,
-      generalSetting,
+      newCodeDefinitionType,
+      newCodeDefinitionValue,
+      previousNonCompliantValue,
+      projectNcdUpdatedAt,
+      globalNewCodeDefinition,
       isChanged: false,
-      selected: currentSetting || generalSetting.type,
-      overrideGeneralSetting: Boolean(currentSetting),
-      days:
-        (currentSetting === NewCodeDefinitionType.NumberOfDays && currentSettingValue) ||
+      selectedNewCodeDefinitionType: newCodeDefinitionType ?? globalNewCodeDefinition.type,
+      overrideGlobalNewCodeDefinition: Boolean(newCodeDefinitionType),
+      numberOfDays:
+        (newCodeDefinitionType === NewCodeDefinitionType.NumberOfDays && newCodeDefinitionValue) ||
         defaultDays,
       analysis:
-        (currentSetting === NewCodeDefinitionType.SpecificAnalysis && currentSettingValue) || '',
+        (newCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis &&
+          newCodeDefinitionValue) ||
+        '',
       referenceBranch:
-        (currentSetting === NewCodeDefinitionType.ReferenceBranch && currentSettingValue) ||
+        (newCodeDefinitionType === NewCodeDefinitionType.ReferenceBranch &&
+          newCodeDefinitionValue) ||
         referenceBranch,
     };
   }
@@ -150,21 +165,23 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
         project: component.key,
       }),
     ]).then(
-      ([generalSetting, setting]) => {
+      ([globalNewCodeDefinition, setting]) => {
         if (this.mounted) {
-          if (!generalSetting.type) {
-            generalSetting = { type: DEFAULT_NEW_CODE_DEFINITION_TYPE };
+          if (!globalNewCodeDefinition.type) {
+            globalNewCodeDefinition = { type: DEFAULT_NEW_CODE_DEFINITION_TYPE };
           }
-          const currentSettingValue = setting.value;
-          const currentSetting = setting.inherited
+          const newCodeDefinitionValue = setting.value;
+          const newCodeDefinitionType = setting.inherited
             ? undefined
             : setting.type || DEFAULT_NEW_CODE_DEFINITION_TYPE;
 
           this.setState(
             this.getUpdatedState({
-              generalSetting,
-              currentSetting,
-              currentSettingValue,
+              globalNewCodeDefinition,
+              newCodeDefinitionType,
+              newCodeDefinitionValue,
+              previousNonCompliantValue: setting.previousNonCompliantValue,
+              projectNcdUpdatedAt: setting.updatedAt,
             })
           );
         }
@@ -181,9 +198,9 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
       () => {
         this.setState({
           saving: false,
-          currentSetting: undefined,
+          newCodeDefinitionType: undefined,
           isChanged: false,
-          selected: undefined,
+          selectedNewCodeDefinitionType: undefined,
           success: true,
         });
         this.resetSuccess();
@@ -194,7 +211,7 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
     );
   };
 
-  handleSelectDays = (days: string) => this.setState({ days, isChanged: true });
+  handleSelectDays = (days: string) => this.setState({ numberOfDays: days, isChanged: true });
 
   handleSelectReferenceBranch = (referenceBranch: string) => {
     this.setState({ referenceBranch, isChanged: true });
@@ -203,37 +220,47 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
   handleCancel = () =>
     this.setState(
       ({
-        generalSetting = { type: DEFAULT_NEW_CODE_DEFINITION_TYPE },
-        currentSetting,
-        currentSettingValue,
-      }) => this.getUpdatedState({ generalSetting, currentSetting, currentSettingValue })
+        globalNewCodeDefinition = { type: DEFAULT_NEW_CODE_DEFINITION_TYPE },
+        newCodeDefinitionType,
+        newCodeDefinitionValue,
+      }) =>
+        this.getUpdatedState({
+          globalNewCodeDefinition,
+          newCodeDefinitionType,
+          newCodeDefinitionValue,
+        })
     );
 
-  handleSelectSetting = (selected?: NewCodeDefinitionType) => {
+  handleSelectSetting = (selectedNewCodeDefinitionType?: NewCodeDefinitionType) => {
     this.setState((currentState) => ({
-      selected,
-      isChanged: selected !== currentState.selected,
+      selectedNewCodeDefinitionType,
+      isChanged: selectedNewCodeDefinitionType !== currentState.selectedNewCodeDefinitionType,
     }));
   };
 
-  handleToggleSpecificSetting = (overrideGeneralSetting: boolean) =>
+  handleToggleSpecificSetting = (overrideGlobalNewCodeDefinition: boolean) =>
     this.setState((currentState) => ({
-      overrideGeneralSetting,
-      isChanged: currentState.overrideGeneralSetting !== overrideGeneralSetting,
+      overrideGlobalNewCodeDefinition,
+      isChanged: currentState.overrideGlobalNewCodeDefinition !== overrideGlobalNewCodeDefinition,
     }));
 
   handleSubmit = (e: React.SyntheticEvent<HTMLFormElement>) => {
     e.preventDefault();
 
     const { component } = this.props;
-    const { days, selected: type, referenceBranch, overrideGeneralSetting } = this.state;
+    const {
+      numberOfDays,
+      selectedNewCodeDefinitionType: type,
+      referenceBranch,
+      overrideGlobalNewCodeDefinition,
+    } = this.state;
 
-    if (!overrideGeneralSetting) {
+    if (!overrideGlobalNewCodeDefinition) {
       this.resetSetting();
       return;
     }
 
-    const value = getSettingValue({ type, days, referenceBranch });
+    const value = getSettingValue({ type, numberOfDays, referenceBranch });
 
     if (type) {
       this.setState({ saving: true });
@@ -245,8 +272,10 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
         () => {
           this.setState({
             saving: false,
-            currentSetting: type,
-            currentSettingValue: value || undefined,
+            newCodeDefinitionType: type,
+            newCodeDefinitionValue: value || undefined,
+            previousNonCompliantValue: undefined,
+            projectNcdUpdatedAt: Date.now(),
             isChanged: false,
             success: true,
           });
@@ -264,16 +293,18 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
     const {
       analysis,
       branchList,
-      currentSetting,
-      days,
-      generalSetting,
+      newCodeDefinitionType,
+      numberOfDays,
+      previousNonCompliantValue,
+      projectNcdUpdatedAt,
+      globalNewCodeDefinition,
       isChanged,
       loading,
-      currentSettingValue,
-      overrideGeneralSetting,
+      newCodeDefinitionValue,
+      overrideGlobalNewCodeDefinition,
       referenceBranch,
       saving,
-      selected,
+      selectedNewCodeDefinitionType,
       success,
     } = this.state;
     const branchSupportEnabled = this.props.hasFeature(Feature.BranchSupport);
@@ -290,7 +321,7 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
             <div className="panel-white project-baseline">
               {branchSupportEnabled && <h2>{translate('project_baseline.default_setting')}</h2>}
 
-              {generalSetting && overrideGeneralSetting !== undefined && (
+              {globalNewCodeDefinition && overrideGlobalNewCodeDefinition !== undefined && (
                 <ProjectNewCodeDefinitionSelector
                   analysis={analysis}
                   branch={branchLike}
@@ -298,10 +329,12 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
                   branchesEnabled={branchSupportEnabled}
                   canAdmin={appState.canAdmin}
                   component={component.key}
-                  currentSetting={currentSetting}
-                  currentSettingValue={currentSettingValue}
-                  days={days}
-                  generalSetting={generalSetting}
+                  newCodeDefinitionType={newCodeDefinitionType}
+                  newCodeDefinitionValue={newCodeDefinitionValue}
+                  days={numberOfDays}
+                  previousNonCompliantValue={previousNonCompliantValue}
+                  projectNcdUpdatedAt={projectNcdUpdatedAt}
+                  globalNewCodeDefinition={globalNewCodeDefinition}
                   isChanged={isChanged}
                   onCancel={this.handleCancel}
                   onSelectDays={this.handleSelectDays}
@@ -309,10 +342,10 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
                   onSelectSetting={this.handleSelectSetting}
                   onSubmit={this.handleSubmit}
                   onToggleSpecificSetting={this.handleToggleSpecificSetting}
-                  overrideGeneralSetting={overrideGeneralSetting}
+                  overrideGlobalNewCodeDefinition={overrideGlobalNewCodeDefinition}
                   referenceBranch={referenceBranch}
                   saving={saving}
-                  selected={selected}
+                  selectedNewCodeDefinitionType={selectedNewCodeDefinitionType}
                 />
               )}
 
@@ -322,7 +355,7 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
                   {translate('settings.state.saved')}
                 </span>
               </div>
-              {generalSetting && branchSupportEnabled && (
+              {globalNewCodeDefinition && branchSupportEnabled && (
                 <div className="huge-spacer-top branch-baseline-selector">
                   <hr />
                   <h2>{translate('project_baseline.configure_branches')}</h2>
@@ -330,14 +363,14 @@ class ProjectNewCodeDefinitionApp extends React.PureComponent<Props, State> {
                     branchList={branchList}
                     component={component}
                     inheritedSetting={
-                      currentSetting
+                      newCodeDefinitionType
                         ? {
-                            type: currentSetting,
-                            value: currentSettingValue,
+                            type: newCodeDefinitionType,
+                            value: newCodeDefinitionValue,
                           }
-                        : generalSetting
+                        : globalNewCodeDefinition
                     }
-                    generalSetting={generalSetting}
+                    globalNewCodeDefinition={globalNewCodeDefinition}
                   />
                 </div>
               )}
index 204f4105bf3177ae5f5ee6154c66e83fe2201715..0aac108102e447d7831f1ee0a9f10d60fad5302a 100644 (file)
@@ -27,6 +27,7 @@ import GlobalNewCodeDefinitionDescription from '../../../components/new-code-def
 import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption';
 import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption';
 import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning';
+import { NewCodeDefinitionLevels } from '../../../components/new-code-definition/utils';
 import { Alert } from '../../../components/ui/Alert';
 import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
@@ -45,10 +46,12 @@ export interface ProjectBaselineSelectorProps {
   branchesEnabled?: boolean;
   canAdmin: boolean | undefined;
   component: string;
-  currentSetting?: NewCodeDefinitionType;
-  currentSettingValue?: string;
+  newCodeDefinitionType?: NewCodeDefinitionType;
+  newCodeDefinitionValue?: string;
+  previousNonCompliantValue?: string;
+  projectNcdUpdatedAt?: number;
   days: string;
-  generalSetting: NewCodeDefinition;
+  globalNewCodeDefinition: NewCodeDefinition;
   isChanged: boolean;
   onCancel: () => void;
   onSelectDays: (value: string) => void;
@@ -58,8 +61,8 @@ export interface ProjectBaselineSelectorProps {
   onToggleSpecificSetting: (selection: boolean) => void;
   referenceBranch?: string;
   saving: boolean;
-  selected?: NewCodeDefinitionType;
-  overrideGeneralSetting: boolean;
+  selectedNewCodeDefinitionType?: NewCodeDefinitionType;
+  overrideGlobalNewCodeDefinition: boolean;
 }
 
 function branchToOption(b: Branch) {
@@ -74,24 +77,26 @@ export default function ProjectNewCodeDefinitionSelector(props: ProjectBaselineS
     branchesEnabled,
     canAdmin,
     component,
-    currentSetting,
-    currentSettingValue,
+    newCodeDefinitionType,
+    newCodeDefinitionValue,
+    previousNonCompliantValue,
+    projectNcdUpdatedAt,
     days,
-    generalSetting,
+    globalNewCodeDefinition,
     isChanged,
-    overrideGeneralSetting,
+    overrideGlobalNewCodeDefinition,
     referenceBranch,
     saving,
-    selected,
+    selectedNewCodeDefinitionType,
   } = props;
 
-  const isGlobalNcdCompliant = isNewCodeDefinitionCompliant(generalSetting);
+  const isGlobalNcdCompliant = isNewCodeDefinitionCompliant(globalNewCodeDefinition);
 
   const isValid = validateSetting({
-    days,
-    overrideGeneralSetting,
+    numberOfDays: days,
+    overrideGlobalNewCodeDefinition,
     referenceBranch,
-    selected,
+    selectedNewCodeDefinitionType,
   });
 
   if (branch === undefined) {
@@ -102,7 +107,7 @@ export default function ProjectNewCodeDefinitionSelector(props: ProjectBaselineS
     <form className="project-baseline-selector" onSubmit={props.onSubmit}>
       <div className="big-spacer-top spacer-bottom" role="radiogroup">
         <RadioButton
-          checked={!overrideGeneralSetting}
+          checked={!overrideGlobalNewCodeDefinition}
           className="big-spacer-bottom"
           disabled={!isGlobalNcdCompliant}
           onCheck={() => props.onToggleSpecificSetting(false)}
@@ -121,14 +126,14 @@ export default function ProjectNewCodeDefinitionSelector(props: ProjectBaselineS
 
         <div className="sw-ml-4">
           <GlobalNewCodeDefinitionDescription
-            globalNcd={generalSetting}
+            globalNcd={globalNewCodeDefinition}
             isGlobalNcdCompliant={isGlobalNcdCompliant}
             canAdmin={canAdmin}
           />
         </div>
 
         <RadioButton
-          checked={overrideGeneralSetting}
+          checked={overrideGlobalNewCodeDefinition}
           className="huge-spacer-top"
           onCheck={() => props.onToggleSpecificSetting(true)}
           value="specific"
@@ -139,51 +144,67 @@ export default function ProjectNewCodeDefinitionSelector(props: ProjectBaselineS
 
       <div className="big-spacer-left big-spacer-right project-baseline-setting">
         <NewCodeDefinitionWarning
-          newCodeDefinitionType={currentSetting}
-          newCodeDefinitionValue={currentSettingValue}
+          newCodeDefinitionType={newCodeDefinitionType}
+          newCodeDefinitionValue={newCodeDefinitionValue}
           isBranchSupportEnabled={branchesEnabled}
-          level="project"
+          level={NewCodeDefinitionLevels.Project}
         />
         <div className="display-flex-column big-spacer-bottom sw-gap-4" role="radiogroup">
           <NewCodeDefinitionPreviousVersionOption
-            disabled={!overrideGeneralSetting}
+            disabled={!overrideGlobalNewCodeDefinition}
             onSelect={props.onSelectSetting}
-            selected={overrideGeneralSetting && selected === NewCodeDefinitionType.PreviousVersion}
+            selected={
+              overrideGlobalNewCodeDefinition &&
+              selectedNewCodeDefinitionType === NewCodeDefinitionType.PreviousVersion
+            }
           />
           <NewCodeDefinitionDaysOption
             days={days}
-            disabled={!overrideGeneralSetting}
+            currentDaysValue={
+              newCodeDefinitionType === NewCodeDefinitionType.NumberOfDays
+                ? newCodeDefinitionValue
+                : undefined
+            }
+            previousNonCompliantValue={previousNonCompliantValue}
+            updatedAt={projectNcdUpdatedAt}
+            disabled={!overrideGlobalNewCodeDefinition}
             isChanged={isChanged}
             isValid={isValid}
             onChangeDays={props.onSelectDays}
             onSelect={props.onSelectSetting}
-            selected={overrideGeneralSetting && selected === NewCodeDefinitionType.NumberOfDays}
+            selected={
+              overrideGlobalNewCodeDefinition &&
+              selectedNewCodeDefinitionType === NewCodeDefinitionType.NumberOfDays
+            }
+            settingLevel={NewCodeDefinitionLevels.Project}
           />
           {branchesEnabled && (
             <NewCodeDefinitionSettingReferenceBranch
               branchList={branchList.map(branchToOption)}
-              disabled={!overrideGeneralSetting}
+              disabled={!overrideGlobalNewCodeDefinition}
               onChangeReferenceBranch={props.onSelectReferenceBranch}
               onSelect={props.onSelectSetting}
               referenceBranch={referenceBranch || ''}
               selected={
-                overrideGeneralSetting && selected === NewCodeDefinitionType.ReferenceBranch
+                overrideGlobalNewCodeDefinition &&
+                selectedNewCodeDefinitionType === NewCodeDefinitionType.ReferenceBranch
               }
-              settingLevel="project"
+              settingLevel={NewCodeDefinitionLevels.Project}
             />
           )}
-          {!branchesEnabled && currentSetting === NewCodeDefinitionType.SpecificAnalysis && (
+          {!branchesEnabled && newCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis && (
             <NewCodeDefinitionSettingAnalysis
               onSelect={noop}
               selected={
-                overrideGeneralSetting && selected === NewCodeDefinitionType.SpecificAnalysis
+                overrideGlobalNewCodeDefinition &&
+                selectedNewCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis
               }
             />
           )}
         </div>
         {!branchesEnabled &&
-          overrideGeneralSetting &&
-          selected === NewCodeDefinitionType.SpecificAnalysis && (
+          overrideGlobalNewCodeDefinition &&
+          selectedNewCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis && (
             <BranchAnalysisList
               analysis={analysis || ''}
               branch={branch.name}
index b0bd20f5c17582168361eff50614e3f1fbea3beb..ed8708a9702b693b489849f09a20907c740c8242 100644 (file)
@@ -23,13 +23,13 @@ import { getSettingValue, validateSetting } from '../../utils';
 describe('getSettingValue', () => {
   const state = {
     analysis: 'analysis',
-    days: '35',
+    numberOfDays: '35',
     referenceBranch: 'branch-4.2',
   };
 
   it('should work for Days', () => {
     expect(getSettingValue({ ...state, type: NewCodeDefinitionType.NumberOfDays })).toBe(
-      state.days
+      state.numberOfDays
     );
   });
 
@@ -54,62 +54,64 @@ describe('getSettingValue', () => {
 
 describe('validateSettings', () => {
   it('should validate at branch level', () => {
-    expect(validateSetting({ days: '' })).toEqual(false);
+    expect(validateSetting({ numberOfDays: '' })).toEqual(false);
     expect(
       validateSetting({
-        days: '12',
-        selected: NewCodeDefinitionType.NumberOfDays,
+        numberOfDays: '12',
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.NumberOfDays,
       })
     ).toEqual(true);
     expect(
       validateSetting({
-        days: 'nope',
-        selected: NewCodeDefinitionType.NumberOfDays,
+        numberOfDays: 'nope',
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.NumberOfDays,
       })
     ).toEqual(false);
     expect(
       validateSetting({
-        days: '',
-        selected: NewCodeDefinitionType.SpecificAnalysis,
+        numberOfDays: '',
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.SpecificAnalysis,
       })
     ).toEqual(false);
     expect(
       validateSetting({
-        days: '',
+        numberOfDays: '',
         referenceBranch: 'master',
-        selected: NewCodeDefinitionType.ReferenceBranch,
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.ReferenceBranch,
       })
     ).toEqual(true);
     expect(
       validateSetting({
-        days: '',
+        numberOfDays: '',
         referenceBranch: '',
-        selected: NewCodeDefinitionType.ReferenceBranch,
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.ReferenceBranch,
       })
     ).toEqual(false);
   });
 
   it('should validate at project level', () => {
-    expect(validateSetting({ days: '', overrideGeneralSetting: false })).toEqual(true);
+    expect(validateSetting({ numberOfDays: '', overrideGlobalNewCodeDefinition: false })).toEqual(
+      true
+    );
     expect(
       validateSetting({
-        selected: NewCodeDefinitionType.PreviousVersion,
-        days: '',
-        overrideGeneralSetting: true,
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.PreviousVersion,
+        numberOfDays: '',
+        overrideGlobalNewCodeDefinition: true,
       })
     ).toEqual(true);
     expect(
       validateSetting({
-        selected: NewCodeDefinitionType.NumberOfDays,
-        days: '',
-        overrideGeneralSetting: true,
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.NumberOfDays,
+        numberOfDays: '',
+        overrideGlobalNewCodeDefinition: true,
       })
     ).toEqual(false);
     expect(
       validateSetting({
-        selected: NewCodeDefinitionType.NumberOfDays,
-        days: '12',
-        overrideGeneralSetting: true,
+        selectedNewCodeDefinitionType: NewCodeDefinitionType.NumberOfDays,
+        numberOfDays: '12',
+        overrideGlobalNewCodeDefinition: true,
       })
     ).toEqual(true);
   });
index 45c5c8efd943ab92889e5432fc07662768883d1c..1f859034f38c53410b9dd07322aba4ed732af174 100644 (file)
@@ -22,18 +22,18 @@ import { NewCodeDefinitionType } from '../../types/new-code-definition';
 
 export function getSettingValue({
   analysis,
-  days,
+  numberOfDays,
   referenceBranch,
   type,
 }: {
   analysis?: string;
-  days?: string;
+  numberOfDays?: string;
   referenceBranch?: string;
   type?: NewCodeDefinitionType;
 }) {
   switch (type) {
     case NewCodeDefinitionType.NumberOfDays:
-      return days;
+      return numberOfDays;
     case NewCodeDefinitionType.ReferenceBranch:
       return referenceBranch;
     case NewCodeDefinitionType.SpecificAnalysis:
@@ -44,20 +44,26 @@ export function getSettingValue({
 }
 
 export function validateSetting(state: {
-  days: string;
-  overrideGeneralSetting?: boolean;
+  numberOfDays: string;
+  overrideGlobalNewCodeDefinition?: boolean;
   referenceBranch?: string;
-  selected?: NewCodeDefinitionType;
+  selectedNewCodeDefinitionType?: NewCodeDefinitionType;
 }) {
-  const { days, overrideGeneralSetting, referenceBranch = '', selected } = state;
+  const {
+    numberOfDays,
+    overrideGlobalNewCodeDefinition,
+    referenceBranch = '',
+    selectedNewCodeDefinitionType,
+  } = state;
 
   return (
-    overrideGeneralSetting === false ||
-    (!!selected &&
+    overrideGlobalNewCodeDefinition === false ||
+    (!!selectedNewCodeDefinitionType &&
       isNewCodeDefinitionCompliant({
-        type: selected,
-        value: days,
+        type: selectedNewCodeDefinitionType,
+        value: numberOfDays,
       }) &&
-      (selected !== NewCodeDefinitionType.ReferenceBranch || referenceBranch.length > 0))
+      (selectedNewCodeDefinitionType !== NewCodeDefinitionType.ReferenceBranch ||
+        referenceBranch.length > 0))
   );
 }
index de5639a1a39eb65dd33564b6ec155ddbe5c11129..a2144e436b7ce41ca632aa13e03c69a210323381 100644 (file)
@@ -26,6 +26,7 @@ import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon';
 import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption';
 import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption';
 import NewCodeDefinitionWarning from '../../../components/new-code-definition/NewCodeDefinitionWarning';
+import { NewCodeDefinitionLevels } from '../../../components/new-code-definition/utils';
 import Spinner from '../../../components/ui/Spinner';
 import { translate } from '../../../helpers/l10n';
 import {
@@ -42,6 +43,7 @@ interface State {
   loading: boolean;
   currentSettingValue?: string;
   isChanged: boolean;
+  projectKey?: string;
   saving: boolean;
   selected?: NewCodeDefinitionType;
   success: boolean;
@@ -68,7 +70,7 @@ export default class NewCodeDefinition extends React.PureComponent<{}, State> {
 
   fetchNewCodePeriodSetting() {
     getNewCodeDefinition()
-      .then(({ type, value, previousNonCompliantValue, updatedAt }) => {
+      .then(({ type, value, previousNonCompliantValue, projectKey, updatedAt }) => {
         this.setState(({ days }) => ({
           currentSetting: type,
           days: type === NewCodeDefinitionType.NumberOfDays ? String(value) : days,
@@ -76,6 +78,7 @@ export default class NewCodeDefinition extends React.PureComponent<{}, State> {
           currentSettingValue: value,
           selected: type,
           previousNonCompliantValue,
+          projectKey,
           ncdUpdatedAt: updatedAt,
         }));
       })
@@ -124,6 +127,8 @@ export default class NewCodeDefinition extends React.PureComponent<{}, State> {
             saving: false,
             currentSetting: type,
             currentSettingValue: value || undefined,
+            previousNonCompliantValue: undefined,
+            ncdUpdatedAt: Date.now(),
             isChanged: false,
             success: true,
           });
@@ -148,6 +153,7 @@ export default class NewCodeDefinition extends React.PureComponent<{}, State> {
       loading,
       isChanged,
       currentSettingValue,
+      projectKey,
       saving,
       selected,
       success,
@@ -214,19 +220,26 @@ export default class NewCodeDefinition extends React.PureComponent<{}, State> {
                         <NewCodeDefinitionDaysOption
                           className="spacer-top sw-mb-4"
                           days={days}
+                          currentDaysValue={
+                            currentSetting === NewCodeDefinitionType.NumberOfDays
+                              ? currentSettingValue
+                              : undefined
+                          }
                           previousNonCompliantValue={previousNonCompliantValue}
+                          projectKey={projectKey}
                           updatedAt={ncdUpdatedAt}
                           isChanged={isChanged}
                           isValid={isValid}
                           onChangeDays={this.onSelectDays}
                           onSelect={this.onSelectSetting}
                           selected={selected === NewCodeDefinitionType.NumberOfDays}
+                          settingLevel={NewCodeDefinitionLevels.Global}
                         />
                         <NewCodeDefinitionWarning
                           newCodeDefinitionType={currentSetting}
                           newCodeDefinitionValue={currentSettingValue}
                           isBranchSupportEnabled={undefined}
-                          level="global"
+                          level={NewCodeDefinitionLevels.Global}
                         />
                         {isChanged && (
                           <div className="big-spacer-top">
index 422a08419eddb8906fcbf1e4ef29a4bbf9b83ced..def197bba026fadc086b1f2333da913d3ef97d19 100644 (file)
@@ -50,7 +50,7 @@ const ui = {
   daysInput: byRole('spinbutton') /* spinbutton is the default role for a number input */,
   saveButton: byRole('button', { name: 'save' }),
   cancelButton: byRole('button', { name: 'cancel' }),
-  ncdAutoUpdateMessage: byText(/new_code_definition.auto_update.global.page.message/),
+  ncdAutoUpdateMessage: byText(/new_code_definition.auto_update.ncd_page.message/),
   ncdAutoUpdateMessageDismiss: byLabelText('alert.dismiss'),
 };
 
index e9386faeb7bfed3ad6af57228f1338dd5a6eebda..cb6d5c95c3fdcb5e9aff12b526c441f5f3a038ae 100644 (file)
@@ -122,7 +122,12 @@ function NCDAutoUpdateMessage(props: NCDAutoUpdateMessageProps) {
     : 'new_code_definition.auto_update.project.message';
 
   return (
-    <DismissableAlertComponent onDismiss={handleBannerDismiss} variant="info" display="banner">
+    <DismissableAlertComponent
+      onDismiss={handleBannerDismiss}
+      variant="info"
+      display="banner"
+      bannerClassName="sw-mb-0"
+    >
       <FormattedMessage
         id={bannerMessageId}
         values={{
index 665cf3562bcfdfba0af0317bca678ec40dd31047..de6016a23b949cbe72e2552c42b501ff3c1ccd98 100644 (file)
@@ -20,9 +20,9 @@
 import { FlagErrorIcon, InputField, Note, SelectionCard } from 'design-system';
 import { noop } from 'lodash';
 import * as React from 'react';
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
 import { FormattedMessage } from 'react-intl';
-import { checkMessageDismissed, MessageTypes, setMessageDismissed } from '../../api/messages';
+import { MessageTypes, checkMessageDismissed, setMessageDismissed } from '../../api/messages';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import {
   NUMBER_OF_DAYS_MAX_VALUE,
@@ -32,11 +32,14 @@ import { isDefined } from '../../helpers/types';
 import { NewCodeDefinitionType } from '../../types/new-code-definition';
 import DocLink from '../common/DocLink';
 import DismissableAlertComponent from '../ui/DismissableAlertComponent';
+import { NewCodeDefinitionLevels } from './utils';
 
 export interface Props {
   className?: string;
   days: string;
+  currentDaysValue?: string;
   previousNonCompliantValue?: string;
+  projectKey?: string;
   updatedAt?: number;
   disabled?: boolean;
   isChanged: boolean;
@@ -44,13 +47,16 @@ export interface Props {
   onChangeDays: (value: string) => void;
   onSelect: (selection: NewCodeDefinitionType) => void;
   selected: boolean;
+  settingLevel: NewCodeDefinitionLevels;
 }
 
 export default function NewCodeDefinitionDaysOption(props: Props) {
   const {
     className,
     days,
+    currentDaysValue,
     previousNonCompliantValue,
+    projectKey,
     updatedAt,
     disabled,
     isChanged,
@@ -58,22 +64,41 @@ export default function NewCodeDefinitionDaysOption(props: Props) {
     onChangeDays,
     onSelect,
     selected,
+    settingLevel,
   } = props;
 
   const [ncdAutoUpdateBannerDismissed, setNcdAutoUpdateBannerDismissed] = useState(true);
 
   useEffect(() => {
     async function fetchMessageDismissed() {
-      const messageStatus = await checkMessageDismissed({
-        messageType: MessageTypes.GlobalNcdPage90,
-      });
+      const messageStatus = await checkMessageDismissed(
+        projectKey
+          ? {
+              messageType: MessageTypes.ProjectNcdPage90,
+              projectKey,
+            }
+          : {
+              messageType: MessageTypes.GlobalNcdPage90,
+            }
+      );
       setNcdAutoUpdateBannerDismissed(messageStatus.dismissed);
     }
 
     if (isDefined(previousNonCompliantValue)) {
       fetchMessageDismissed().catch(noop);
     }
-  }, [previousNonCompliantValue]);
+  }, [previousNonCompliantValue, projectKey, settingLevel]);
+
+  const shouldShowAutoUpdateBanner = useMemo(() => {
+    return (
+      (settingLevel === NewCodeDefinitionLevels.Global ||
+        settingLevel === NewCodeDefinitionLevels.Project) &&
+      isDefined(previousNonCompliantValue) &&
+      isDefined(updatedAt) &&
+      !disabled &&
+      !ncdAutoUpdateBannerDismissed
+    );
+  }, [disabled, ncdAutoUpdateBannerDismissed, previousNonCompliantValue, settingLevel, updatedAt]);
 
   const handleBannerDismiss = useCallback(async () => {
     await setMessageDismissed({ messageType: MessageTypes.GlobalNcdPage90 });
@@ -118,32 +143,30 @@ export default function NewCodeDefinitionDaysOption(props: Props) {
               )}
             </Note>
 
-            {isDefined(previousNonCompliantValue) &&
-              isDefined(updatedAt) &&
-              !ncdAutoUpdateBannerDismissed && (
-                <DismissableAlertComponent
-                  variant="info"
-                  display="inline"
-                  className="sw-mt-4 sw-max-w-[800px]"
-                  onDismiss={handleBannerDismiss}
-                >
-                  <FormattedMessage
-                    defaultMessage="new_code_definition.auto_update.global.page.message"
-                    id="new_code_definition.auto_update.global.page.message"
-                    tagName="span"
-                    values={{
-                      previousDays: previousNonCompliantValue,
-                      days,
-                      date: new Date(updatedAt).toLocaleDateString(),
-                      link: (
-                        <DocLink to="/project-administration/clean-as-you-code-settings/defining-new-code/#new-code-definition-options">
-                          {translate('learn_more')}
-                        </DocLink>
-                      ),
-                    }}
-                  />
-                </DismissableAlertComponent>
-              )}
+            {shouldShowAutoUpdateBanner && (
+              <DismissableAlertComponent
+                variant="info"
+                display="inline"
+                className="sw-mt-4 sw-max-w-[800px]"
+                onDismiss={handleBannerDismiss}
+              >
+                <FormattedMessage
+                  defaultMessage="new_code_definition.auto_update.ncd_page.message"
+                  id="new_code_definition.auto_update.ncd_page.message"
+                  tagName="span"
+                  values={{
+                    previousDays: previousNonCompliantValue,
+                    days: currentDaysValue,
+                    date: isDefined(updatedAt) && new Date(updatedAt).toLocaleDateString(),
+                    link: (
+                      <DocLink to="/project-administration/clean-as-you-code-settings/defining-new-code/#new-code-definition-options">
+                        {translate('learn_more')}
+                      </DocLink>
+                    ),
+                  }}
+                />
+              </DismissableAlertComponent>
+            )}
           </div>
         )}
       </>
index 2b2b7587c0f84224af4ada078921bbaf53c8e920..59f0d4eee6cefaba558e5e9f02d4209960c9b60b 100644 (file)
@@ -42,6 +42,7 @@ import Tooltip from '../controls/Tooltip';
 import GlobalNewCodeDefinitionDescription from './GlobalNewCodeDefinitionDescription';
 import NewCodeDefinitionDaysOption from './NewCodeDefinitionDaysOption';
 import NewCodeDefinitionPreviousVersionOption from './NewCodeDefinitionPreviousVersionOption';
+import { NewCodeDefinitionLevels } from './utils';
 
 interface Props {
   canAdmin: boolean | undefined;
@@ -174,6 +175,7 @@ export default function NewCodeDefinitionSelector(props: Props) {
           onChangeDays={setDays}
           onSelect={handleNcdChanged}
           selected={selectedNcdType === NewCodeDefinitionType.NumberOfDays}
+          settingLevel={NewCodeDefinitionLevels.NewProject}
         />
 
         <SelectionCard
index 60e23dcecaee2cc32125c2c2fb30155ae0da22b2..1a978222ac743b82546fd12e6438184a7269c100 100644 (file)
@@ -24,12 +24,13 @@ import { isNewCodeDefinitionCompliant } from '../../helpers/new-code-definition'
 import { NewCodeDefinitionType } from '../../types/new-code-definition';
 import DocLink from '../common/DocLink';
 import { Alert } from '../ui/Alert';
+import { NewCodeDefinitionLevels } from './utils';
 
 export interface NewCodeDefinitionWarningProps {
   newCodeDefinitionType: NewCodeDefinitionType | undefined;
   newCodeDefinitionValue: string | undefined;
   isBranchSupportEnabled: boolean | undefined;
-  level: 'branch' | 'project' | 'global';
+  level: Exclude<NewCodeDefinitionLevels, NewCodeDefinitionLevels.NewProject>;
 }
 
 export default function NewCodeDefinitionWarning({
@@ -73,7 +74,9 @@ export default function NewCodeDefinitionWarning({
         <p className="sw-mb-2">
           {translate(
             `baseline.number_days.compliance_warning.content.${level}${
-              isBranchSupportEnabled && level === 'project' ? '.with_branch_support' : ''
+              isBranchSupportEnabled && level === NewCodeDefinitionLevels.Project
+                ? '.with_branch_support'
+                : ''
             }`
           )}
         </p>
index 7545834f3a112845a157160eba2a183f26b49878..95cf53fc6f463efb89eb4e450f284dec3e553c73 100644 (file)
@@ -24,6 +24,13 @@ import { Permissions } from '../../types/permissions';
 import { Component } from '../../types/types';
 import { CurrentUser, isLoggedIn } from '../../types/users';
 
+export enum NewCodeDefinitionLevels {
+  Global = 'GLOBAL',
+  Project = 'PROJECT',
+  Branch = 'BRANCH',
+  NewProject = 'NEW_PROJECT',
+}
+
 export type PreviouslyNonCompliantNCD = NewCodeDefinition &
   Required<Pick<NewCodeDefinition, 'previousNonCompliantValue' | 'updatedAt'>>;
 
index 202e91a8dd55054705bc1660abc9fbcd1a169c3e..20a4a595da2dc7b736b8961c8ac19814966598bf 100644 (file)
@@ -25,17 +25,22 @@ import ClearIcon from '../icons/ClearIcon';
 import { Alert, AlertProps } from './Alert';
 
 export interface DismissableAlertComponentProps extends AlertProps {
+  bannerClassName?: string;
   className?: string;
   children: React.ReactNode;
   onDismiss: () => void;
 }
 
 export default function DismissableAlertComponent(props: DismissableAlertComponentProps) {
-  const { className, display = 'banner', variant, children, onDismiss } = props;
+  const { bannerClassName, className, display = 'banner', variant, children, onDismiss } = props;
 
   return (
     <div className={classNames('dismissable-alert-wrapper', className)}>
-      <Alert className={`dismissable-alert-${display}`} display={display} variant={variant}>
+      <Alert
+        className={classNames(`dismissable-alert-${display}`, bannerClassName)}
+        display={display}
+        variant={variant}
+      >
         <div className="display-flex-center dismissable-alert-content">
           <div className="flex-1">{children}</div>
           <ButtonIcon aria-label={translate('alert.dismiss')} onClick={onDismiss}>
index c4c946108f340fd4e4923285e92ea03837f4dc84..cb846a32df7cd796c217d3dda4367cc7eed44b4b 100644 (file)
@@ -3962,7 +3962,7 @@ new_code_definition.reference_branch.usecase=Recommended for projects using feat
 new_code_definition.reference_branch.notice=The main branch will be set as the reference branch when the project is created. You will be able to choose another branch as the reference branch when your project will have more branches.
 
 new_code_definition.auto_update.global.message=The global new code definition was automatically changed from {previousDays} to {days} days on {date}, following a SonarQube upgrade, as it was exceeding the maximum value. {link}
-new_code_definition.auto_update.global.page.message=The number of days was automatically changed from {previousDays} to {days} on {date}, following a SonarQube upgrade, as it was exceeding the maximum value. {link}
+new_code_definition.auto_update.ncd_page.message=The number of days was automatically changed from {previousDays} to {days} on {date}, following a SonarQube upgrade, as it was exceeding the maximum value. {link}
 new_code_definition.auto_update.project.message=This project's new code definition was automatically changed from {previousDays} to {days} days on {date}, following a SonarQube upgrade, as it was exceeding the maximum value. {link}
 new_code_definition.auto_update.review_link=Review new code definition