aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/quality-profiles
diff options
context:
space:
mode:
authorDavid Cho-Lerat <david.cho-lerat@sonarsource.com>2024-09-18 14:52:23 +0200
committersonartech <sonartech@sonarsource.com>2024-09-24 20:03:04 +0000
commit9f80a169ea2c85db1cd8479c42ce609b80aeafaf (patch)
tree26dcfcc6d66a2e08e81b470fc69325d19b9e07a1 /server/sonar-web/src/main/js/apps/quality-profiles
parent7869ce9d4981e5ce428aef07bed9e99ca7506f47 (diff)
downloadsonarqube-9f80a169ea2c85db1cd8479c42ce609b80aeafaf.tar.gz
sonarqube-9f80a169ea2c85db1cd8479c42ce609b80aeafaf.zip
SONAR-23030 Use Heading component
Diffstat (limited to 'server/sonar-web/src/main/js/apps/quality-profiles')
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx60
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx13
4 files changed, 66 insertions, 34 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx
index 735bf38a28d..4b3080b4499 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { Heading } from '@sonarsource/echoes-react';
import { DiscreetLink, FlagMessage, Note } from 'design-system';
import { sortBy } from 'lodash';
import * as React from 'react';
import { useIntl } from 'react-intl';
+import { isDefined } from '../../../helpers/types';
import { getDeprecatedActiveRulesUrl } from '../../../helpers/urls';
import { Profile } from '../types';
import { getProfilePath } from '../utils';
@@ -49,9 +52,10 @@ export default function EvolutionDeprecated({ profiles }: Readonly<Props>) {
return (
<section aria-label={intl.formatMessage({ id: 'quality_profiles.deprecated_rules' })}>
- <h2 className="sw-heading-md sw-mb-6">
+ <Heading as="h2" hasMarginBottom>
{intl.formatMessage({ id: 'quality_profiles.deprecated_rules' })}
- </h2>
+ </Heading>
+
<FlagMessage variant="error" className="sw-mb-3">
{intl.formatMessage(
{ id: 'quality_profiles.deprecated_rules_are_still_activated' },
@@ -70,7 +74,9 @@ export default function EvolutionDeprecated({ profiles }: Readonly<Props>) {
<Note>
{profile.languageName}
+
{', '}
+
<DiscreetLink
className="link-no-underline"
to={getDeprecatedActiveRulesUrl({ qprofile: profile.key })}
@@ -85,6 +91,7 @@ export default function EvolutionDeprecated({ profiles }: Readonly<Props>) {
)}
</DiscreetLink>
</Note>
+
<EvolutionDeprecatedInherited
profile={profile}
profilesWithDeprecations={profilesWithDeprecations}
@@ -104,31 +111,34 @@ function EvolutionDeprecatedInherited(
) {
const { profile, profilesWithDeprecations } = props;
const intl = useIntl();
+
const rules = React.useMemo(
() => getDeprecatedRulesInheritanceChain(profile, profilesWithDeprecations),
[profile, profilesWithDeprecations],
);
- if (rules.length) {
- return (
- <>
- {rules.map((rule) => {
- if (rule.from.key === profile.key) {
- return null;
- }
-
- return (
- <Note key={rule.from.key}>
- {intl.formatMessage(
- { id: 'coding_rules.filters.inheritance.x_inherited_from_y' },
- { count: rule.count, name: rule.from.name },
- )}
- </Note>
- );
- })}
- </>
- );
+
+ if (rules.length === 0) {
+ return null;
}
- return null;
+
+ return (
+ <>
+ {rules.map((rule) => {
+ if (rule.from.key === profile.key) {
+ return null;
+ }
+
+ return (
+ <Note key={rule.from.key}>
+ {intl.formatMessage(
+ { id: 'coding_rules.filters.inheritance.x_inherited_from_y' },
+ { count: rule.count, name: rule.from.name },
+ )}
+ </Note>
+ );
+ })}
+ </>
+ );
}
function getDeprecatedRulesInheritanceChain(profile: Profile, profilesWithDeprecations: Profile[]) {
@@ -139,14 +149,16 @@ function getDeprecatedRulesInheritanceChain(profile: Profile, profilesWithDeprec
return rules;
}
- if (profile.parentKey) {
+ if (isDefined(profile.parentKey) && profile.parentKey !== '') {
const parentProfile = profilesWithDeprecations.find((p) => p.key === profile.parentKey);
+
if (parentProfile) {
const parentRules = getDeprecatedRulesInheritanceChain(
parentProfile,
profilesWithDeprecations,
);
- if (parentRules.length) {
+
+ if (parentRules.length !== 0) {
count -= parentRules.reduce((n, rule) => n + rule.count, 0);
rules = rules.concat(parentRules);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx
index 125155c9f42..0e680661200 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx
@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { Heading } from '@sonarsource/echoes-react';
import { DiscreetLink, Link, Note } from 'design-system';
import { noop, sortBy } from 'lodash';
import * as React from 'react';
@@ -26,6 +28,7 @@ import { MetricType } from '~sonar-aligned/types/metrics';
import { listRules } from '../../../api/rules';
import { toShortISO8601String } from '../../../helpers/dates';
import { translateWithParameters } from '../../../helpers/l10n';
+import { isDefined } from '../../../helpers/types';
import { getRulesUrl } from '../../../helpers/urls';
import { Rule, RuleActivation } from '../../../types/types';
@@ -39,6 +42,7 @@ export default function EvolutionRules() {
const intl = useIntl();
const [latestRules, setLatestRules] = React.useState<ExtendedRule[]>();
const [latestRulesTotal, setLatestRulesTotal] = React.useState<number>();
+
const periodStartDate = React.useMemo(() => {
const startDate = new Date();
startDate.setFullYear(startDate.getFullYear() - 1);
@@ -60,21 +64,23 @@ export default function EvolutionRules() {
}, noop);
}, [periodStartDate]);
- if (!latestRulesTotal || !latestRules) {
+ if (!(isDefined(latestRulesTotal) && latestRulesTotal !== 0) || !latestRules) {
return null;
}
return (
<section aria-label={intl.formatMessage({ id: 'quality_profiles.latest_new_rules' })}>
- <h2 className="sw-heading-md sw-mb-6">
+ <Heading as="h2" hasMarginBottom>
{intl.formatMessage({ id: 'quality_profiles.latest_new_rules' })}
- </h2>
+ </Heading>
+
<ul className="sw-flex sw-flex-col sw-gap-4 sw-body-sm">
{latestRules.map((rule) => (
<li className="sw-flex sw-flex-col sw-gap-1" key={rule.key}>
<div className="sw-truncate">
<DiscreetLink to={getRulesUrl({ rule_key: rule.key })}>{rule.name}</DiscreetLink>
</div>
+
<Note className="sw-truncate">
{rule.activations
? translateWithParameters(
@@ -90,6 +96,7 @@ export default function EvolutionRules() {
</li>
))}
</ul>
+
{latestRulesTotal > RULES_LIMIT && (
<div className="sw-mt-6 sw-body-sm-highlight">
<Link to={getRulesUrl({ available_since: periodStartDate })}>
@@ -107,6 +114,7 @@ export default function EvolutionRules() {
function parseRules(rules: Rule[], actives?: Record<string, RuleActivation[]>): ExtendedRule[] {
return rules.map((rule) => {
const activations = actives?.[rule.key]?.length ?? 0;
+
return { ...rule, activations };
});
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx
index e41b60b349d..d44fe4cab52 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { Heading } from '@sonarsource/echoes-react';
import { DiscreetLink, FlagMessage, Note } from 'design-system';
import * as React from 'react';
import { useIntl } from 'react-intl';
import DateFormatter from '../../../components/intl/DateFormatter';
+import { isDefined } from '../../../helpers/types';
import { Profile } from '../types';
import { getProfilePath, isStagnant } from '../utils';
@@ -28,7 +31,7 @@ interface Props {
profiles: Profile[];
}
-export default function EvolutionStagnant(props: Props) {
+export default function EvolutionStagnant(props: Readonly<Props>) {
const intl = useIntl();
const outdated = props.profiles.filter((profile) => !profile.isBuiltIn && isStagnant(profile));
@@ -38,13 +41,14 @@ export default function EvolutionStagnant(props: Props) {
return (
<section aria-label={intl.formatMessage({ id: 'quality_profiles.stagnant_profiles' })}>
- <h2 className="sw-heading-md sw-mb-6">
+ <Heading as="h2" hasMarginBottom>
{intl.formatMessage({ id: 'quality_profiles.stagnant_profiles' })}
- </h2>
+ </Heading>
<FlagMessage variant="warning" className="sw-mb-3">
{intl.formatMessage({ id: 'quality_profiles.not_updated_more_than_year' })}
</FlagMessage>
+
<ul className="sw-flex sw-flex-col sw-gap-4 sw-body-sm">
{outdated.map((profile) => (
<li className="sw-flex sw-flex-col sw-gap-1" key={profile.key}>
@@ -53,7 +57,8 @@ export default function EvolutionStagnant(props: Props) {
{profile.name}
</DiscreetLink>
</div>
- {profile.rulesUpdatedAt && (
+
+ {isDefined(profile.rulesUpdatedAt) && profile.rulesUpdatedAt !== '' && (
<Note>
<DateFormatter date={profile.rulesUpdatedAt} long>
{(formattedDate) =>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx
index 55043a09630..1fd5d6c299a 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx
@@ -17,8 +17,9 @@
* 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, ButtonGroup, ButtonVariety } from '@sonarsource/echoes-react';
-import { FlagMessage, Link } from 'design-system';
+
+import { Button, ButtonGroup, ButtonVariety, Heading, Link } from '@sonarsource/echoes-react';
+import { FlagMessage } from 'design-system';
import * as React from 'react';
import { useIntl } from 'react-intl';
import { useLocation, useRouter } from '~sonar-aligned/components/hoc/withRouter';
@@ -61,7 +62,10 @@ export default function PageHeader(props: Readonly<Props>) {
return (
<header className="sw-grid sw-grid-cols-3 sw-gap-12">
<div className="sw-col-span-2">
- <h1 className="sw-heading-lg sw-mb-4">{translate('quality_profiles.page')}</h1>
+ <Heading as="h1" hasMarginBottom>
+ {translate('quality_profiles.page')}
+ </Heading>
+
<div className="sw-body-sm">
{intl.formatMessage({ id: 'quality_profiles.intro' })}
@@ -70,6 +74,7 @@ export default function PageHeader(props: Readonly<Props>) {
</Link>
</div>
</div>
+
{actions.create && (
<div className="sw-flex sw-flex-col sw-items-end">
<ButtonGroup>
@@ -81,10 +86,12 @@ export default function PageHeader(props: Readonly<Props>) {
>
{intl.formatMessage({ id: 'create' })}
</Button>
+
<Button id="quality-profiles-restore" onClick={() => setModal('restoreProfile')}>
{intl.formatMessage({ id: 'restore' })}
</Button>
</ButtonGroup>
+
{languages.length === 0 && (
<FlagMessage className="sw-mt-2" variant="warning">
{intl.formatMessage({ id: 'quality_profiles.no_languages_available' })}