/* * 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 styled from '@emotion/styled'; import { Link, LinkStandalone } from '@sonarsource/echoes-react'; import classNames from 'classnames'; import { Badge, Card, LightLabel, LightPrimary, Note, QualityGateIndicator, SeparatorCircleIcon, SubnavigationFlowSeparator, Tags, themeBorder, themeColor, } from 'design-system'; import { isEmpty } from 'lodash'; import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import Favorite from '../../../../components/controls/Favorite'; import Tooltip from '../../../../components/controls/Tooltip'; import DateFromNow from '../../../../components/intl/DateFromNow'; import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter'; import Measure from '../../../../components/measure/Measure'; import { translate, translateWithParameters } from '../../../../helpers/l10n'; import { formatMeasure } from '../../../../helpers/measures'; import { isDefined } from '../../../../helpers/types'; import { getProjectUrl } from '../../../../helpers/urls'; import { ComponentQualifier } from '../../../../types/component'; import { MetricKey, MetricType } from '../../../../types/metrics'; import { Status } from '../../../../types/types'; import { CurrentUser, isLoggedIn } from '../../../../types/users'; import { Project } from '../../types'; import ProjectCardLanguages from './ProjectCardLanguages'; import ProjectCardMeasures from './ProjectCardMeasures'; interface Props { currentUser: CurrentUser; handleFavorite: (component: string, isFavorite: boolean) => void; project: Project; type?: string; } function renderFirstLine( project: Props['project'], handleFavorite: Props['handleFavorite'], isNewCode: boolean, ) { const { analysisDate, isFavorite, key, measures, name, qualifier, tags, visibility } = project; const awaitingScan = [ MetricKey.reliability_issues, MetricKey.maintainability_issues, MetricKey.security_issues, ].every((key) => measures[key] === undefined) && !isNewCode && !isEmpty(analysisDate) && measures.ncloc !== undefined; const formatted = formatMeasure(measures[MetricKey.alert_status], MetricType.Level); const qualityGateLabel = translateWithParameters('overview.quality_gate_x', formatted); return ( <>
{isDefined(isFavorite) && ( )} {name} {qualifier === ComponentQualifier.Application && ( {translate('qualifier.APP')} {measures.projects !== '' && ( {' ‒ '} {translateWithParameters('x_projects_', measures.projects)} )} } > {translate('qualifier.APP')} )} {translate('visibility', visibility)} {awaitingScan && !isNewCode && !isEmpty(analysisDate) && measures.ncloc !== undefined && ( {translate('projects.awaiting_scan')} )}
{isDefined(analysisDate) && analysisDate !== '' && ( {formatted} )}
{isDefined(analysisDate) && analysisDate !== '' && ( {(formattedAnalysisDate) => ( , }} /> )} )} {isNewCode ? measures[MetricKey.new_lines] != null && ( <>
{translate('metric.new_lines.name')}
) : measures[MetricKey.ncloc] != null && ( <>
{translate('metric.ncloc.name')}
)} {tags.length > 0 && ( <> )}
); } function renderSecondLine( currentUser: Props['currentUser'], project: Props['project'], isNewCode: boolean, ) { const { analysisDate, key, leakPeriodDate, measures, qualifier, isScannable } = project; if (!isEmpty(analysisDate) && (!isNewCode || !isEmpty(leakPeriodDate))) { return ( ); } return (
{isNewCode && analysisDate ? translate('projects.no_new_code_period', qualifier) : translate('projects.not_analyzed', qualifier)} {qualifier !== ComponentQualifier.Application && isEmpty(analysisDate) && isLoggedIn(currentUser) && isScannable && ( {translate('projects.configure_analysis')} )}
); } export default function ProjectCard(props: Readonly) { const { currentUser, type, project } = props; const isNewCode = type === 'leak'; return ( {renderFirstLine(project, props.handleFavorite, isNewCode)} {renderSecondLine(currentUser, project, isNewCode)} ); } const ProjectCardWrapper = styled(Card)` background-color: ${themeColor('projectCardBackground')}; border: ${themeBorder('default', 'projectCardBorder')}; &.project-card-disabled *:not(g):not(path) { color: ${themeColor('projectCardDisabled')} !important; } `;