mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-12-24 01:09:48 +01:00
Feature/mirror (#1683)
Add mirror command and extension points. Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com> Co-authored-by: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Co-authored-by: Konstantin Schaper <konstantin.schaper@cloudogu.com>
This commit is contained in:
53
scm-ui/ui-components/src/Duration.stories.tsx
Normal file
53
scm-ui/ui-components/src/Duration.stories.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import Duration from "./Duration";
|
||||
import React from "react";
|
||||
|
||||
storiesOf("Duration", module).add("Duration", () => (
|
||||
<div className="m-5 p-5">
|
||||
<p>
|
||||
<Duration duration={500} />
|
||||
</p>
|
||||
<p>
|
||||
<Duration duration={2000} />
|
||||
</p>
|
||||
<p>
|
||||
<Duration duration={42 * 1000 * 60} />
|
||||
</p>
|
||||
<p>
|
||||
<Duration duration={21 * 1000 * 60 * 60} />
|
||||
</p>
|
||||
<p>
|
||||
<Duration duration={5 * 1000 * 60 * 60 * 24} />
|
||||
</p>
|
||||
<p>
|
||||
<Duration duration={3 * 1000 * 60 * 60 * 24 * 7} />
|
||||
</p>
|
||||
<p>
|
||||
<Duration duration={12 * 1000 * 60 * 60 * 24} />
|
||||
</p>
|
||||
</div>
|
||||
));
|
||||
73
scm-ui/ui-components/src/Duration.tsx
Normal file
73
scm-ui/ui-components/src/Duration.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Unit = "ms" | "s" | "m" | "h" | "d" | "w";
|
||||
|
||||
type Props = {
|
||||
duration: number;
|
||||
};
|
||||
|
||||
export const parse = (duration: number) => {
|
||||
let value = duration;
|
||||
let unit: Unit = "ms";
|
||||
if (value > 1000) {
|
||||
unit = "s";
|
||||
value /= 1000;
|
||||
if (value > 60) {
|
||||
unit = "m";
|
||||
value /= 60;
|
||||
if (value > 60) {
|
||||
unit = "h";
|
||||
value /= 60;
|
||||
if (value > 24) {
|
||||
unit = "d";
|
||||
value /= 24;
|
||||
if (value > 7) {
|
||||
unit = "w";
|
||||
value /= 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
duration: Math.round(value),
|
||||
unit,
|
||||
};
|
||||
};
|
||||
|
||||
const Duration: FC<Props> = ({ duration }) => {
|
||||
const [t] = useTranslation("commons");
|
||||
const parsed = parse(duration);
|
||||
return (
|
||||
<time dateTime={`${parsed.duration}${parsed.unit}`}>
|
||||
{t(`duration.${parsed.unit}`, { count: parsed.duration })}
|
||||
</time>
|
||||
);
|
||||
};
|
||||
|
||||
export default Duration;
|
||||
@@ -74,7 +74,7 @@ const OverviewPageActions: FC<Props> = ({
|
||||
if (showCreateButton) {
|
||||
return (
|
||||
<div className={classNames("input-button", "control", "column")}>
|
||||
<Button label={label} link={createLink || `${link}create`} color="primary" />
|
||||
<Button label={label} link={createLink || `${link}create/`} color="primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
|
||||
import styled from "styled-components";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import * as React from "react";
|
||||
import React, { ReactNode } from "react";
|
||||
import Tag from "./Tag";
|
||||
import { ReactNode } from "react";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { Color, colors, sizes } from "./styleConstants";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 2rem;
|
||||
@@ -35,26 +35,43 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const Spacing = styled.div`
|
||||
padding: 1em;
|
||||
padding: 0.5rem;
|
||||
`;
|
||||
|
||||
const colors = ["primary", "link", "info", "success", "warning", "danger"];
|
||||
|
||||
const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
|
||||
|
||||
storiesOf("Tag", module)
|
||||
.addDecorator(RoutingDecorator)
|
||||
.addDecorator(storyFn => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.addDecorator((storyFn) => <Wrapper>{storyFn()}</Wrapper>)
|
||||
.add("Default", () => <Tag label="Default tag" />)
|
||||
.add("Rounded", () => <Tag label="Rounded tag" color="dark" rounded={true} />)
|
||||
.add("With Icon", () => <Tag label="System" icon="bolt" />)
|
||||
.add("Colors", () => (
|
||||
<div>
|
||||
{colors.map(color => (
|
||||
<Spacing>
|
||||
{colors.map((color) => (
|
||||
<Spacing key={color}>
|
||||
<Tag color={color} label={color} />
|
||||
</Spacing>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
.add("With title", () => <Tag label="hover me" title="good job"/>)
|
||||
.add("Clickable", () => <Tag label="Click here" onClick={() => alert("Not so fast")}/>);
|
||||
.add("Outlined", () => (
|
||||
<div>
|
||||
{(["success", "black", "danger"] as Color[]).map((color) => (
|
||||
<Spacing key={color}>
|
||||
<Tag color={color} label={color} outlined={true} />
|
||||
</Spacing>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
.add("With title", () => <Tag label="hover me" title="good job" />)
|
||||
.add("Clickable", () => <Tag label="Click here" onClick={() => alert("Not so fast")} />)
|
||||
.add("Sizes", () => (
|
||||
<div>
|
||||
{sizes.map((size) => (
|
||||
<Spacing key={size}>
|
||||
<Tag size={size} label={size} />
|
||||
</Spacing>
|
||||
))}
|
||||
</div>
|
||||
));
|
||||
|
||||
@@ -21,50 +21,84 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import * as React from "react";
|
||||
import React, { FC, HTMLAttributes } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Color, Size } from "./styleConstants";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
color: string;
|
||||
color?: Color;
|
||||
outlined?: boolean;
|
||||
rounded?: boolean;
|
||||
icon?: string;
|
||||
label: string;
|
||||
label?: string;
|
||||
title?: string;
|
||||
size?: Size;
|
||||
onClick?: () => void;
|
||||
onRemove?: () => void;
|
||||
};
|
||||
|
||||
class Tag extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
color: "light"
|
||||
};
|
||||
type InnerTagProps = HTMLAttributes<HTMLSpanElement> & {
|
||||
small: boolean;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, color, icon, label, title, onClick, onRemove } = this.props;
|
||||
let showIcon = null;
|
||||
if (icon) {
|
||||
showIcon = (
|
||||
<>
|
||||
<i className={classNames("fas", `fa-${icon}`)} />
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
let showDelete = null;
|
||||
if (onRemove) {
|
||||
showDelete = <a className="tag is-delete" onClick={onRemove} />;
|
||||
}
|
||||
const smallMixin = css`
|
||||
font-size: 0.7rem !important;
|
||||
padding: 0.25rem !important;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
return (
|
||||
const InnerTag = styled.span<InnerTagProps>`
|
||||
${(props) => props.small && smallMixin};
|
||||
`;
|
||||
|
||||
const Tag: FC<Props> = ({
|
||||
className,
|
||||
color = "light",
|
||||
outlined,
|
||||
size = "normal",
|
||||
rounded,
|
||||
icon,
|
||||
label,
|
||||
title,
|
||||
onClick,
|
||||
onRemove,
|
||||
children,
|
||||
}) => {
|
||||
let showIcon = null;
|
||||
if (icon) {
|
||||
showIcon = (
|
||||
<>
|
||||
<span className={classNames("tag", `is-${color}`, className)} title={title} onClick={onClick}>
|
||||
{showIcon}
|
||||
{label}
|
||||
</span>
|
||||
{showDelete}
|
||||
<i className={classNames("fas", `fa-${icon}`)} />
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
let showDelete = null;
|
||||
if (onRemove) {
|
||||
showDelete = <a className="tag is-delete" onClick={onRemove} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<InnerTag
|
||||
className={classNames("tag", `is-${color}`, `is-${size}`, className, {
|
||||
"is-outlined": outlined,
|
||||
"is-rounded": rounded,
|
||||
"has-cursor-pointer": onClick,
|
||||
})}
|
||||
title={title}
|
||||
onClick={onClick}
|
||||
small={size === "small"}
|
||||
>
|
||||
{showIcon}
|
||||
{label}
|
||||
{children}
|
||||
</InnerTag>
|
||||
{showDelete}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
86
scm-ui/ui-components/src/forms/FileInput.tsx
Normal file
86
scm-ui/ui-components/src/forms/FileInput.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { ChangeEvent, FC, FocusEvent } from "react";
|
||||
import classNames from "classnames";
|
||||
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||
import { createAttributesForTesting } from "../devBuild";
|
||||
|
||||
type Props = {
|
||||
name?: string;
|
||||
className?: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
helpText?: string;
|
||||
disabled?: boolean;
|
||||
testId?: string;
|
||||
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
|
||||
ref?: React.Ref<HTMLInputElement>;
|
||||
};
|
||||
|
||||
const FileInput: FC<Props> = ({
|
||||
name,
|
||||
testId,
|
||||
helpText,
|
||||
placeholder,
|
||||
disabled,
|
||||
label,
|
||||
className,
|
||||
ref,
|
||||
onBlur,
|
||||
onChange
|
||||
}) => {
|
||||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (onChange && event.target.files) {
|
||||
onChange(event);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = (event: FocusEvent<HTMLInputElement>) => {
|
||||
if (onBlur && event.target.files) {
|
||||
onBlur(event);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames("field", className)}>
|
||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||
<div className="control">
|
||||
<input
|
||||
ref={ref}
|
||||
name={name}
|
||||
className={classNames("input", "p-1", className)}
|
||||
type="file"
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
{...createAttributesForTesting(testId)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileInput;
|
||||
@@ -56,7 +56,7 @@ export default class TagGroup extends React.Component<Props> {
|
||||
return (
|
||||
<div className="control" key={key}>
|
||||
<div className="tags has-addons">
|
||||
<Tag color="info is-outlined" label={item.displayName} onRemove={() => this.removeEntry(item)} />
|
||||
<Tag color="info" outlined={true} label={item.displayName} onRemove={() => this.removeEntry(item)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -66,7 +66,7 @@ export default class TagGroup extends React.Component<Props> {
|
||||
}
|
||||
|
||||
removeEntry = (item: DisplayedUser) => {
|
||||
const newItems = this.props.items.filter(name => name !== item);
|
||||
const newItems = this.props.items.filter((name) => name !== item);
|
||||
this.props.onRemove(newItems);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,3 +39,4 @@ export { default as PasswordConfirmation } from "./PasswordConfirmation";
|
||||
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon";
|
||||
export { default as DropDown } from "./DropDown";
|
||||
export { default as FileUpload } from "./FileUpload";
|
||||
export { default as FileInput } from "./FileInput";
|
||||
|
||||
@@ -44,6 +44,7 @@ export { validation, repositories };
|
||||
|
||||
export { default as DateFromNow } from "./DateFromNow";
|
||||
export { default as DateShort } from "./DateShort";
|
||||
export { default as Duration } from "./Duration";
|
||||
export { default as ErrorNotification } from "./ErrorNotification";
|
||||
export { default as ErrorPage } from "./ErrorPage";
|
||||
export { default as Icon } from "./Icon";
|
||||
|
||||
@@ -396,10 +396,17 @@ class DiffFile extends React.Component<Props, State> {
|
||||
if (key === value) {
|
||||
value = file.type;
|
||||
}
|
||||
const color =
|
||||
value === "added" ? "success is-outlined" : value === "deleted" ? "danger is-outlined" : "info is-outlined";
|
||||
|
||||
return <ChangeTypeTag className={classNames("is-rounded", "has-text-weight-normal")} color={color} label={value} />;
|
||||
const color = value === "added" ? "success" : value === "deleted" ? "danger" : "info";
|
||||
return (
|
||||
<ChangeTypeTag
|
||||
className={classNames("has-text-weight-normal")}
|
||||
rounded={true}
|
||||
outlined={true}
|
||||
color={color}
|
||||
label={value}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
hasContent = (file: FileDiff) => file && !file.isBinary && file.hunks && file.hunks.length > 0;
|
||||
|
||||
@@ -33,6 +33,8 @@ import { Repository } from "@scm-manager/ui-types";
|
||||
import Image from "../Image";
|
||||
import Icon from "../Icon";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { Color } from "../styleConstants";
|
||||
import RepositoryFlag from "./RepositoryFlag";
|
||||
|
||||
const baseDate = "2020-03-26T12:13:42+02:00";
|
||||
|
||||
@@ -48,6 +50,14 @@ const bindAvatar = (binder: Binder, avatar: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
const bindFlag = (binder: Binder, color: Color, label: string) => {
|
||||
binder.bind("repository.card.flags", () => (
|
||||
<RepositoryFlag title={label} color={color}>
|
||||
{label}
|
||||
</RepositoryFlag>
|
||||
));
|
||||
};
|
||||
|
||||
const bindBeforeTitle = (binder: Binder, extension: ReactNode) => {
|
||||
binder.bind("repository.card.beforeTitle", () => {
|
||||
return extension;
|
||||
@@ -76,6 +86,17 @@ const QuickLink = (
|
||||
|
||||
const archivedRepository = { ...repository, archived: true };
|
||||
const exportingRepository = { ...repository, exporting: true };
|
||||
const healthCheckFailedRepository = {
|
||||
...repository,
|
||||
healthCheckFailures: [
|
||||
{
|
||||
id: "4211",
|
||||
summary: "Something failed",
|
||||
description: "Something realy bad happend",
|
||||
url: "https://something-realy-bad.happend"
|
||||
}
|
||||
]
|
||||
};
|
||||
const archivedExportingRepository = { ...repository, archived: true, exporting: true };
|
||||
|
||||
storiesOf("RepositoryEntry", module)
|
||||
@@ -109,6 +130,18 @@ storiesOf("RepositoryEntry", module)
|
||||
bindAvatar(binder, Git);
|
||||
return withBinder(binder, exportingRepository);
|
||||
})
|
||||
.add("HealthCheck Failure", () => {
|
||||
const binder = new Binder("title");
|
||||
bindAvatar(binder, Git);
|
||||
return withBinder(binder, healthCheckFailedRepository);
|
||||
})
|
||||
.add("RepositoryFlag EP", () => {
|
||||
const binder = new Binder("title");
|
||||
bindAvatar(binder, Git);
|
||||
bindFlag(binder, "success", "awesome");
|
||||
bindFlag(binder, "warning", "ouhhh...");
|
||||
return withBinder(binder, healthCheckFailedRepository);
|
||||
})
|
||||
.add("MultiRepositoryTags", () => {
|
||||
const binder = new Binder("title");
|
||||
bindAvatar(binder, Git);
|
||||
|
||||
@@ -30,6 +30,7 @@ import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { withTranslation, WithTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import HealthCheckFailureDetail from "./HealthCheckFailureDetail";
|
||||
import RepositoryFlag from "./RepositoryFlag";
|
||||
|
||||
type DateProp = Date | string;
|
||||
|
||||
@@ -44,37 +45,23 @@ type State = {
|
||||
showHealthCheck: boolean;
|
||||
};
|
||||
|
||||
const RepositoryTag = styled.span`
|
||||
margin-left: 0.2rem;
|
||||
background-color: #9a9a9a;
|
||||
padding: 0.25rem;
|
||||
border-radius: 5px;
|
||||
color: white;
|
||||
overflow: visible;
|
||||
pointer-events: all;
|
||||
font-weight: bold;
|
||||
font-size: 0.7rem;
|
||||
`;
|
||||
const RepositoryWarnTag = styled.span`
|
||||
margin-left: 0.2rem;
|
||||
background-color: #f14668;
|
||||
padding: 0.25rem;
|
||||
border-radius: 5px;
|
||||
color: white;
|
||||
overflow: visible;
|
||||
pointer-events: all;
|
||||
font-weight: bold;
|
||||
font-size: 0.7rem;
|
||||
cursor: help;
|
||||
const Title = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > * {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
`;
|
||||
|
||||
class RepositoryEntry extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showHealthCheck: false
|
||||
showHealthCheck: false,
|
||||
};
|
||||
}
|
||||
|
||||
createLink = (repository: Repository) => {
|
||||
return `/repo/${repository.namespace}/${repository.name}`;
|
||||
};
|
||||
@@ -170,31 +157,33 @@ class RepositoryEntry extends React.Component<Props, State> {
|
||||
const { repository, t } = this.props;
|
||||
const repositoryFlags = [];
|
||||
if (repository.archived) {
|
||||
repositoryFlags.push(<RepositoryTag title={t("archive.tooltip")}>{t("repository.archived")}</RepositoryTag>);
|
||||
repositoryFlags.push(<RepositoryFlag title={t("archive.tooltip")}>{t("repository.archived")}</RepositoryFlag>);
|
||||
}
|
||||
|
||||
if (repository.exporting) {
|
||||
repositoryFlags.push(<RepositoryTag title={t("exporting.tooltip")}>{t("repository.exporting")}</RepositoryTag>);
|
||||
repositoryFlags.push(<RepositoryFlag title={t("exporting.tooltip")}>{t("repository.exporting")}</RepositoryFlag>);
|
||||
}
|
||||
|
||||
if (repository.healthCheckFailures && repository.healthCheckFailures.length > 0) {
|
||||
repositoryFlags.push(
|
||||
<RepositoryWarnTag
|
||||
<RepositoryFlag
|
||||
color="danger"
|
||||
title={t("healthCheckFailure.tooltip")}
|
||||
onClick={() => {
|
||||
this.setState({ showHealthCheck: true });
|
||||
}}
|
||||
>
|
||||
{t("repository.healthCheckFailure")}
|
||||
</RepositoryWarnTag>
|
||||
</RepositoryFlag>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>
|
||||
<ExtensionPoint name="repository.card.beforeTitle" props={{ repository }} />
|
||||
<strong>{repository.name}</strong> {repositoryFlags.map(flag => flag)}
|
||||
</>
|
||||
<strong>{repository.name}</strong> {repositoryFlags}
|
||||
<ExtensionPoint name="repository.flags" props={{ repository }} renderAll={true} />
|
||||
</Title>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
42
scm-ui/ui-components/src/repos/RepositoryFlag.tsx
Normal file
42
scm-ui/ui-components/src/repos/RepositoryFlag.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React, { FC } from "react";
|
||||
import Tag from "../Tag";
|
||||
import { Color, Size } from "../styleConstants";
|
||||
|
||||
type Props = {
|
||||
color?: Color;
|
||||
title?: string;
|
||||
onClick?: () => void;
|
||||
size?: Size;
|
||||
};
|
||||
|
||||
const RepositoryFlag: FC<Props> = ({ children, size = "small", ...props }) => (
|
||||
<Tag size={size} {...props}>
|
||||
{children}
|
||||
</Tag>
|
||||
);
|
||||
|
||||
export default RepositoryFlag;
|
||||
@@ -29,10 +29,11 @@ import {
|
||||
AnnotationFactory,
|
||||
AnnotationFactoryContext,
|
||||
DiffEventHandler,
|
||||
DiffEventContext
|
||||
DiffEventContext,
|
||||
} from "./DiffTypes";
|
||||
|
||||
import { FileDiff as File, FileChangeType, Hunk, Change, ChangeType } from "@scm-manager/ui-types";
|
||||
|
||||
export { diffs };
|
||||
|
||||
export * from "./annotate";
|
||||
@@ -46,6 +47,7 @@ export { default as LoadingDiff } from "./LoadingDiff";
|
||||
export { DefaultCollapsed, DefaultCollapsedFunction } from "./defaultCollapsed";
|
||||
export { default as RepositoryAvatar } from "./RepositoryAvatar";
|
||||
export { default as RepositoryEntry } from "./RepositoryEntry";
|
||||
export { default as RepositoryFlag } from "./RepositoryFlag";
|
||||
export { default as RepositoryEntryLink } from "./RepositoryEntryLink";
|
||||
export { default as JumpToFileButton } from "./JumpToFileButton";
|
||||
export { default as CommitAuthor } from "./CommitAuthor";
|
||||
@@ -61,5 +63,5 @@ export {
|
||||
AnnotationFactory,
|
||||
AnnotationFactoryContext,
|
||||
DiffEventHandler,
|
||||
DiffEventContext
|
||||
DiffEventContext,
|
||||
};
|
||||
|
||||
40
scm-ui/ui-components/src/styleConstants.ts
Normal file
40
scm-ui/ui-components/src/styleConstants.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
export const colors = [
|
||||
"black",
|
||||
"dark",
|
||||
"light",
|
||||
"white",
|
||||
"primary",
|
||||
"link",
|
||||
"info",
|
||||
"success",
|
||||
"warning",
|
||||
"danger",
|
||||
] as const;
|
||||
export type Color = typeof colors[number];
|
||||
|
||||
export const sizes = ["small", "normal", "medium", "large"] as const;
|
||||
export type Size = typeof sizes[number];
|
||||
Reference in New Issue
Block a user