'/admin/extension/license/support',
'/admin/audit',
'/admin/projects_management',
+ '/account/projects',
];
export default function GlobalContainer() {
border-top: 1px solid var(--barBorderColor);
}
-.account-projects-list > li {
- padding: 15px 20px;
-}
-
-.account-projects-list > li + li {
- border-top: 1px solid var(--barBorderColor);
-}
-
-.account-project-side {
- float: right;
- margin-left: 10px;
- text-align: right;
-}
-
-.account-project-analysis {
- line-height: var(--controlHeight);
- color: var(--secondFontColor);
- font-size: var(--smallFontSize);
-}
-
-.account-project-card {
- position: relative;
- display: block;
-}
-
-.account-project-name {
- display: inline-block;
- vertical-align: top;
- max-width: 300px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.account-project-name > a {
- border-bottom-color: #d0d0d0;
- color: var(--baseFontColor);
-}
-
-.account-project-name > a:hover {
- border-bottom-color: var(--lightBlue);
- color: var(--blue);
-}
-
-.account-project-quality-gate {
- display: inline-block;
- vertical-align: top;
- line-height: var(--controlHeight);
- margin-left: 8px;
-}
-
-.account-project-description {
- margin-top: 6px;
- line-height: 1.5;
-}
-
-.account-project-links {
- margin-top: 6px;
-}
-
-.account-project-key {
- margin-top: 6px;
- color: var(--secondFontColor);
- font-size: var(--smallFontSize);
-}
-
-.my-activity-issues {
- position: relative;
- display: flex;
- justify-content: center;
- margin-bottom: 70px;
-}
-
-.my-activity-issues:after {
- position: absolute;
- z-index: 5;
- top: -15px;
- left: 50%;
- width: 1px;
- height: 100px;
- background-color: var(--barBorderColor);
- transform: rotate(30deg);
- content: '';
-}
-
-.my-activity-issues > a {
- display: block;
- padding: 15px 20px;
- border: none;
- border-radius: 2px;
- color: var(--baseFontColor);
-}
-
-.my-activity-issues > a:hover {
- background-color: var(--barBackgroundColor);
-}
-
-.my-activity-recent-issues {
- margin-right: 50px;
- text-align: right;
-}
-
-.my-activity-recent-issues .my-activity-issues-note {
- text-align: left;
-}
-
-.my-activity-all-issues {
- margin-left: 50px;
-}
-
-.my-activity-issues-number {
- display: inline-block;
- vertical-align: middle;
- line-height: 36px;
- font-size: 36px;
- font-weight: 300;
-}
-
-.my-activity-issues-note {
- display: inline-block;
- vertical-align: middle;
- padding-left: 10px;
- padding-top: 2px;
- line-height: 16px;
- font-size: var(--smallFontSize);
-}
-
-.my-activity-projects {
- width: 360px;
- margin: 0 auto;
- padding: 40px 0;
-}
-
-.my-activity-projects-header {
- line-height: var(--controlHeight);
- margin-bottom: 15px;
- padding: 0 10px;
-}
-
-.my-activity-projects > ul > li + li {
- border-top: 1px solid var(--barBorderColor);
-}
-
-.my-activity-projects > ul > li > a {
- display: block;
- padding: 15px 10px;
- border: none;
-}
-
-.my-activity-projects > ul > li > a:hover {
- background-color: var(--barBackgroundColor);
-}
-
-.my-activity-projects .level {
- width: 60px;
-}
-
-.my-activity-projects .more {
- margin-top: 30px;
- text-align: center;
-}
-
.notifications-table {
margin-top: calc(-2 * var(--gridSize));
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import {
+ Card,
+ DiscreetLink,
+ LightPrimary,
+ Note,
+ QualityGateIndicator,
+ SubHeading,
+ UnorderedList,
+} from 'design-system';
import * as React from 'react';
-import Link from '../../../components/common/Link';
import MetaLink from '../../../components/common/MetaLink';
-import HelpTooltip from '../../../components/controls/HelpTooltip';
+import Tooltip from '../../../components/controls/Tooltip';
import DateFromNow from '../../../components/intl/DateFromNow';
-import Level from '../../../components/ui/Level';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { formatMeasure } from '../../../helpers/measures';
import { orderLinks } from '../../../helpers/projectLinks';
import { getProjectUrl } from '../../../helpers/urls';
-import { MyProject, ProjectLink } from '../../../types/types';
+import { MetricType } from '../../../types/metrics';
+import { MyProject, ProjectLink, Status } from '../../../types/types';
interface Props {
project: MyProject;
}
-export default function ProjectCard({ project }: Props) {
+export default function ProjectCard({ project }: Readonly<Props>) {
const { links } = project;
const orderedLinks: ProjectLink[] = orderLinks(
const { lastAnalysisDate } = project;
+ const formatted = formatMeasure(project.qualityGate, MetricType.Level);
+ const qualityGateLabel = translateWithParameters('overview.quality_gate_x', formatted);
+
return (
- <div className="account-project-card clearfix">
- <aside className="account-project-side">
+ <Card>
+ <aside className="sw-float-right sw-flex sw-flex-col sw-items-end sw-gap-2">
{lastAnalysisDate !== undefined ? (
- <div className="account-project-analysis">
+ <Note>
<DateFromNow date={lastAnalysisDate}>
{(fromNow) => translateWithParameters('my_account.projects.analyzed_x', fromNow)}
</DateFromNow>
- </div>
+ </Note>
) : (
- <div className="account-project-analysis">
- {translate('my_account.projects.never_analyzed')}
- </div>
+ <Note>{translate('my_account.projects.never_analyzed')}</Note>
)}
{project.qualityGate !== undefined && (
- <div className="account-project-quality-gate">
- {project.qualityGate === 'WARN' && (
- <HelpTooltip
- className="little-spacer-right"
- overlay={translate('quality_gates.conditions.warning.tooltip')}
- />
- )}
- <Level aria-label={translate('quality_gates.status')} level={project.qualityGate} />
+ <div>
+ <Tooltip overlay={qualityGateLabel}>
+ <span className="sw-flex sw-items-center">
+ <QualityGateIndicator
+ status={(project.qualityGate as Status) ?? 'NONE'}
+ ariaLabel={qualityGateLabel}
+ />
+ <LightPrimary className="sw-ml-2 sw-body-sm-highlight">{formatted}</LightPrimary>
+ </span>
+ </Tooltip>
</div>
)}
</aside>
- <h3 className="account-project-name">
- <Link to={getProjectUrl(project.key)}>{project.name}</Link>
- </h3>
+ <SubHeading as="h3">
+ <DiscreetLink to={getProjectUrl(project.key)}>{project.name}</DiscreetLink>
+ </SubHeading>
+
+ <Note>{project.key}</Note>
+
+ {!!project.description && <div className="sw-mt-2">{project.description}</div>}
{orderedLinks.length > 0 && (
- <div className="account-project-links">
- <ul className="list-inline">
+ <div className="sw-mt-2">
+ <UnorderedList className="sw-flex sw-gap-4">
{orderedLinks.map((link) => (
- <MetaLink iconOnly key={link.id} link={link} />
+ <MetaLink key={link.id} link={link} />
))}
- </ul>
+ </UnorderedList>
</div>
)}
-
- <div className="account-project-key">{project.key}</div>
-
- {!!project.description && (
- <div className="account-project-description">{project.description}</div>
- )}
- </div>
+ </Card>
);
}
total?: number;
}
-export default function Projects(props: Props) {
+export default function Projects(props: Readonly<Props>) {
const { projects } = props;
return (
<div id="account-projects">
- {projects.length === 0 ? (
- <div className="js-no-results">{translate('my_account.projects.no_results')}</div>
- ) : (
- <p>{translate('my_account.projects.description')}</p>
- )}
+ <div className="sw-mt-8">
+ {projects.length === 0
+ ? translate('my_account.projects.no_results')
+ : translate('my_account.projects.description')}
+ </div>
{projects.length > 0 && (
- <ul className="account-projects-list">
- {projects.map((project) => (
- <li key={project.key}>
- <ProjectCard project={project} />
- </li>
- ))}
- </ul>
- )}
+ <>
+ <ul className="sw-mt-4 sw-flex sw-flex-col sw-gap-4">
+ {projects.map((project) => (
+ <li key={project.key}>
+ <ProjectCard project={project} />
+ </li>
+ ))}
+ </ul>
- {projects.length > 0 && (
- <ListFooter
- count={projects.length}
- loadMore={props.loadMore}
- loading={props.loading}
- ready={!props.loading}
- total={props.total || 0}
- />
+ <ListFooter
+ count={projects.length}
+ loadMore={props.loadMore}
+ loading={props.loading}
+ ready={!props.loading}
+ total={props.total ?? 0}
+ useMIUIButtons
+ />
+ </>
)}
</div>
);
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { LargeCenteredLayout, PageContentFontWrapper, Spinner } from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { getMyProjects } from '../../../api/components';
};
render() {
- const helmet = <Helmet title={translate('my_account.projects')} />;
-
- if (this.state.projects == null) {
- return (
- <div className="text-center">
- {helmet}
- <i className="spinner spacer" />
- </div>
- );
- }
+ const { loading, projects = [], total } = this.state;
return (
- <div className="account-body account-container">
- {helmet}
- <Projects
- loadMore={this.loadMore}
- loading={this.state.loading}
- projects={this.state.projects}
- total={this.state.total}
- />
- </div>
+ <LargeCenteredLayout as="main">
+ <PageContentFontWrapper className="sw-body-sm sw-py-8">
+ <Helmet title={translate('my_account.projects')} />
+ <Spinner loading={loading && projects.length === 0}>
+ <Projects
+ loadMore={this.loadMore}
+ loading={loading}
+ projects={projects}
+ total={total}
+ />
+ </Spinner>
+ </PageContentFontWrapper>
+ </LargeCenteredLayout>
);
}
}
links: ProjectLink[];
}
-export default function MetaLinks({ links }: Props) {
+export default function MetaLinks({ links }: Readonly<Props>) {
const orderedLinks = orderLinks(links);
return (
<h3 id="external-links">{translate('overview.external_links')}</h3>
<ul className="project-info-list" aria-labelledby="external-links">
{orderedLinks.map((link) => (
- <MetaLink miui key={link.id} link={link} />
+ <MetaLink key={link.id} link={link} />
))}
</ul>
</>
* 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 { CloseIcon, InputField, InteractiveIcon, Link } from 'design-system';
import React, { useState } from 'react';
import isValidUri from '../../app/utils/isValidUri';
import { translate } from '../../helpers/l10n';
import { getLinkName } from '../../helpers/projectLinks';
import { ProjectLink } from '../../types/types';
-import { ClearButton } from '../controls/buttons';
import ProjectLinkIcon from '../icons/ProjectLinkIcon';
interface Props {
iconOnly?: boolean;
link: ProjectLink;
- // TODO Remove this prop when all links are migrated to the new design
- miui?: boolean;
}
-export default function MetaLink({ iconOnly, link, miui }: Props) {
+export default function MetaLink({ iconOnly, link }: Readonly<Props>) {
const [expanded, setExpanded] = useState(false);
const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
const linkTitle = getLinkName(link);
const isValid = isValidUri(link.url);
- return miui ? (
+ return (
<li>
- <Link
- isExternal
+ <StyledLink
to={link.url}
preventDefault={!isValid}
onClick={isValid ? undefined : handleClick}
- rel="nofollow noreferrer noopener"
- target="_blank"
- icon={<ProjectLinkIcon miui className="little-spacer-right" type={link.type} />}
+ icon={<ProjectLinkIcon miui type={link.type} />}
>
{!iconOnly && linkTitle}
- </Link>
+ </StyledLink>
{expanded && (
- <div className="little-spacer-top display-flex-center">
- <InputField
- className="overview-key width-80"
- onClick={handleSelect}
- readOnly
- type="text"
- value={link.url}
- />
+ <div className="sw-mt-1 sw-flex sw-items-center">
+ <InputField onClick={handleSelect} readOnly type="text" value={link.url} size="large" />
<InteractiveIcon
Icon={CloseIcon}
aria-label={translate('hide')}
</div>
)}
</li>
- ) : (
- <li>
- <a
- className="link-no-underline"
- href={isValid ? link.url : undefined}
- onClick={isValid ? undefined : handleClick}
- rel="nofollow noreferrer noopener"
- target="_blank"
- title={linkTitle}
- >
- <ProjectLinkIcon className="little-spacer-right" type={link.type} />
- {!iconOnly && linkTitle}
- </a>
- {expanded && (
- <div className="little-spacer-top display-flex-center">
- <input
- className="overview-key width-80"
- onClick={handleSelect}
- readOnly
- type="text"
- value={link.url}
- />
- <ClearButton className="little-spacer-left" onClick={handleCollapse} />
- </div>
- )}
- </li>
);
}
+
+/*
+ * Override the spacing to make it smaller
+ * 1rem = 16px for the icon width
+ * +
+ * 0.5 rem = margin '2'
+ */
+const StyledLink = styled(Link)`
+ margin-left: 1.5rem;
+
+ & > svg,
+ & > img {
+ margin-right: 0.5rem;
+ margin-left: -1.5rem;
+ }
+`;
quality_gates.conditions.overall_code_1=1 condition failed on overall code
quality_gates.conditions.overall_code_x={0} conditions failed on overall code
quality_gates.conditions.operator=Operator
-quality_gates.conditions.warning=Warning
-quality_gates.conditions.warning.tooltip=Warning status is deprecated and will disappear with the next update of the Quality Gate.
quality_gates.conditions.value=Value
quality_gates.conditions.where=Where?
quality_gates.duplicated_conditions=This quality gate has duplicated conditions:
my_account.tokens.expiration=Expiration
my_account.tokens.expired=Token is expired
my_account.projects=Projects
-my_account.projects.description=Those projects are the ones you are administering.
-my_account.projects.no_results=You are not administering any project yet.
+my_account.projects.description=You have admin privileges over the following projects.
+my_account.projects.no_results=You have no project admin privileges.
my_account.projects.analyzed_x=Analyzed {0}
my_account.projects.never_analyzed=Never analyzed
my_account.search_project=Search Project