You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EvolutionRules.tsx 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import { DiscreetLink, Link, Note } from 'design-system';
  21. import { noop, sortBy } from 'lodash';
  22. import * as React from 'react';
  23. import { useIntl } from 'react-intl';
  24. import { listRules } from '../../../api/rules';
  25. import { toShortISO8601String } from '../../../helpers/dates';
  26. import { translateWithParameters } from '../../../helpers/l10n';
  27. import { formatMeasure } from '../../../helpers/measures';
  28. import { getRulesUrl } from '../../../helpers/urls';
  29. import { MetricType } from '../../../types/metrics';
  30. import { Rule, RuleActivation } from '../../../types/types';
  31. const RULES_LIMIT = 10;
  32. interface ExtendedRule extends Rule {
  33. activations: number;
  34. }
  35. export default function EvolutionRules() {
  36. const intl = useIntl();
  37. const [latestRules, setLatestRules] = React.useState<ExtendedRule[]>();
  38. const [latestRulesTotal, setLatestRulesTotal] = React.useState<number>();
  39. const periodStartDate = React.useMemo(() => {
  40. const startDate = new Date();
  41. startDate.setFullYear(startDate.getFullYear() - 1);
  42. return toShortISO8601String(startDate);
  43. }, []);
  44. React.useEffect(() => {
  45. const data = {
  46. asc: false,
  47. available_since: periodStartDate,
  48. f: 'name,langName,actives',
  49. ps: RULES_LIMIT,
  50. s: 'createdAt',
  51. };
  52. listRules(data).then(({ actives, rules, paging: { total } }) => {
  53. setLatestRules(sortBy(parseRules(rules, actives), 'langName'));
  54. setLatestRulesTotal(total);
  55. }, noop);
  56. }, [periodStartDate]);
  57. if (!latestRulesTotal || !latestRules) {
  58. return null;
  59. }
  60. return (
  61. <section aria-label={intl.formatMessage({ id: 'quality_profiles.latest_new_rules' })}>
  62. <h2 className="sw-heading-md sw-mb-6">
  63. {intl.formatMessage({ id: 'quality_profiles.latest_new_rules' })}
  64. </h2>
  65. <ul className="sw-flex sw-flex-col sw-gap-4 sw-body-sm">
  66. {latestRules.map((rule) => (
  67. <li className="sw-flex sw-flex-col sw-gap-1" key={rule.key}>
  68. <div className="sw-truncate">
  69. <DiscreetLink to={getRulesUrl({ rule_key: rule.key })}>{rule.name}</DiscreetLink>
  70. </div>
  71. <Note className="sw-truncate">
  72. {rule.activations
  73. ? translateWithParameters(
  74. 'quality_profiles.latest_new_rules.activated',
  75. rule.langName!,
  76. rule.activations,
  77. )
  78. : translateWithParameters(
  79. 'quality_profiles.latest_new_rules.not_activated',
  80. rule.langName!,
  81. )}
  82. </Note>
  83. </li>
  84. ))}
  85. </ul>
  86. {latestRulesTotal > RULES_LIMIT && (
  87. <div className="sw-mt-6 sw-body-sm-highlight">
  88. <Link to={getRulesUrl({ available_since: periodStartDate })}>
  89. {intl.formatMessage(
  90. { id: 'quality_profiles.latest_new_rules.see_all_x' },
  91. { count: formatMeasure(latestRulesTotal, MetricType.ShortInteger) },
  92. )}
  93. </Link>
  94. </div>
  95. )}
  96. </section>
  97. );
  98. }
  99. function parseRules(rules: Rule[], actives?: Record<string, RuleActivation[]>): ExtendedRule[] {
  100. return rules.map((rule) => {
  101. const activations = actives?.[rule.key]?.length ?? 0;
  102. return { ...rule, activations };
  103. });
  104. }