mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-16 18:26:16 +01:00
merge brachn 2.0.0-m3
This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
// @flow
|
||||
import { contextPath } from "./urls";
|
||||
|
||||
export const NOT_FOUND_ERROR = Error("not found");
|
||||
export const UNAUTHORIZED_ERROR = Error("unauthorized");
|
||||
|
||||
const fetchOptions: RequestOptions = {
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
Cache: "no-cache"
|
||||
}
|
||||
};
|
||||
|
||||
function handleStatusCode(response: Response) {
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw UNAUTHORIZED_ERROR;
|
||||
}
|
||||
if (response.status === 404) {
|
||||
throw NOT_FOUND_ERROR;
|
||||
}
|
||||
throw new Error("server returned status code " + response.status);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function createUrl(url: string) {
|
||||
if (url.includes("://")) {
|
||||
return url;
|
||||
}
|
||||
let urlWithStartingSlash = url;
|
||||
if (url.indexOf("/") !== 0) {
|
||||
urlWithStartingSlash = "/" + urlWithStartingSlash;
|
||||
}
|
||||
return `${contextPath}/api/rest/v2${urlWithStartingSlash}`;
|
||||
}
|
||||
|
||||
class ApiClient {
|
||||
get(url: string): Promise<Response> {
|
||||
return fetch(createUrl(url), fetchOptions).then(handleStatusCode);
|
||||
}
|
||||
|
||||
post(url: string, payload: any, contentType: string = "application/json") {
|
||||
return this.httpRequestWithJSONBody("POST", url, contentType, payload);
|
||||
}
|
||||
|
||||
put(url: string, payload: any, contentType: string = "application/json") {
|
||||
return this.httpRequestWithJSONBody("PUT", url, contentType, payload);
|
||||
}
|
||||
|
||||
delete(url: string): Promise<Response> {
|
||||
let options: RequestOptions = {
|
||||
method: "DELETE"
|
||||
};
|
||||
options = Object.assign(options, fetchOptions);
|
||||
return fetch(createUrl(url), options).then(handleStatusCode);
|
||||
}
|
||||
|
||||
httpRequestWithJSONBody(
|
||||
method: string,
|
||||
url: string,
|
||||
contentType: string,
|
||||
payload: any
|
||||
): Promise<Response> {
|
||||
let options: RequestOptions = {
|
||||
method: method,
|
||||
body: JSON.stringify(payload)
|
||||
};
|
||||
options = Object.assign(options, fetchOptions);
|
||||
// $FlowFixMe
|
||||
options.headers["Content-Type"] = contentType;
|
||||
|
||||
return fetch(createUrl(url), options).then(handleStatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
export let apiClient = new ApiClient();
|
||||
@@ -1,15 +0,0 @@
|
||||
// @flow
|
||||
import { createUrl } from "./apiclient";
|
||||
|
||||
describe("create url", () => {
|
||||
it("should not change absolute urls", () => {
|
||||
expect(createUrl("https://www.scm-manager.org")).toBe(
|
||||
"https://www.scm-manager.org"
|
||||
);
|
||||
});
|
||||
|
||||
it("should add prefix for api", () => {
|
||||
expect(createUrl("/users")).toBe("/api/rest/v2/users");
|
||||
expect(createUrl("users")).toBe("/api/rest/v2/users");
|
||||
});
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import moment from "moment";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
date?: string,
|
||||
|
||||
// context props
|
||||
i18n: any
|
||||
};
|
||||
|
||||
class DateFromNow extends React.Component<Props> {
|
||||
static format(locale: string, date?: string) {
|
||||
let fromNow = "";
|
||||
if (date) {
|
||||
fromNow = moment(date)
|
||||
.locale(locale)
|
||||
.fromNow();
|
||||
}
|
||||
return fromNow;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { i18n, date } = this.props;
|
||||
|
||||
const fromNow = DateFromNow.format(i18n.language, date);
|
||||
return <span>{fromNow}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate()(DateFromNow);
|
||||
@@ -1,25 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import Notification from "./Notification";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
error?: Error
|
||||
};
|
||||
|
||||
class ErrorNotification extends React.Component<Props> {
|
||||
render() {
|
||||
const { t, error } = this.props;
|
||||
if (error) {
|
||||
return (
|
||||
<Notification type="danger">
|
||||
<strong>{t("error-notification.prefix")}:</strong> {error.message}
|
||||
</Notification>
|
||||
);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(ErrorNotification);
|
||||
@@ -1,27 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import ErrorNotification from "./ErrorNotification";
|
||||
|
||||
type Props = {
|
||||
error: Error,
|
||||
title: string,
|
||||
subtitle: string
|
||||
};
|
||||
|
||||
class ErrorPage extends React.Component<Props> {
|
||||
render() {
|
||||
const { title, subtitle, error } = this.props;
|
||||
|
||||
return (
|
||||
<section className="section">
|
||||
<div className="box column is-4 is-offset-4 container">
|
||||
<h1 className="title">{title}</h1>
|
||||
<p className="subtitle">{subtitle}</p>
|
||||
<ErrorNotification error={error} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorPage;
|
||||
@@ -1,18 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { withContextPath } from "../urls";
|
||||
|
||||
type Props = {
|
||||
src: string,
|
||||
alt: string,
|
||||
className?: any
|
||||
};
|
||||
|
||||
class Image extends React.Component<Props> {
|
||||
render() {
|
||||
const { src, alt, className } = this.props;
|
||||
return <img className={className} src={withContextPath(src)} alt={alt} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default Image;
|
||||
@@ -1,51 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import injectSheet from "react-jss";
|
||||
import Image from "./Image";
|
||||
|
||||
const styles = {
|
||||
wrapper: {
|
||||
position: "relative"
|
||||
},
|
||||
loading: {
|
||||
width: "128px",
|
||||
height: "128px",
|
||||
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
|
||||
margin: "64px 0 0 -64px"
|
||||
},
|
||||
image: {
|
||||
width: "128px",
|
||||
height: "128px"
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
message?: string,
|
||||
classes: any
|
||||
};
|
||||
|
||||
class Loading extends React.Component<Props> {
|
||||
render() {
|
||||
const { message, t, classes } = this.props;
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.loading}>
|
||||
<Image
|
||||
className={classes.image}
|
||||
src="/images/loading.svg"
|
||||
alt={t("loading.alt")}
|
||||
/>
|
||||
<p className="has-text-centered">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(translate("commons")(Loading));
|
||||
@@ -1,17 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import Image from "./Image";
|
||||
|
||||
type Props = {
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class Logo extends React.Component<Props> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return <Image src="/images/logo.png" alt={t("logo.alt")} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(Logo);
|
||||
@@ -1,18 +0,0 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
address?: string
|
||||
};
|
||||
|
||||
class MailLink extends React.Component<Props> {
|
||||
render() {
|
||||
const { address } = this.props;
|
||||
if (!address) {
|
||||
return null;
|
||||
}
|
||||
return <a href={"mailto: " + address}>{address}</a>;
|
||||
}
|
||||
}
|
||||
|
||||
export default MailLink;
|
||||
@@ -1,37 +0,0 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type NotificationType = "primary" | "info" | "success" | "warning" | "danger";
|
||||
|
||||
type Props = {
|
||||
type: NotificationType,
|
||||
onClose?: () => void,
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Notification extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
type: "info"
|
||||
};
|
||||
|
||||
renderCloseButton() {
|
||||
const { onClose } = this.props;
|
||||
if (onClose) {
|
||||
return <button className="delete" onClick={onClose} />;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
render() {
|
||||
const { type, children } = this.props;
|
||||
return (
|
||||
<div className={classNames("notification", "is-" + type)}>
|
||||
{this.renderCloseButton()}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Notification;
|
||||
@@ -1,121 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { PagedCollection } from "../types/Collection";
|
||||
import { Button } from "./buttons";
|
||||
|
||||
type Props = {
|
||||
collection: PagedCollection,
|
||||
onPageChange: string => void,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class Paginator extends React.Component<Props> {
|
||||
isLinkUnavailable(linkType: string) {
|
||||
return !this.props.collection || !this.props.collection._links[linkType];
|
||||
}
|
||||
|
||||
createAction = (linkType: string) => () => {
|
||||
const { collection, onPageChange } = this.props;
|
||||
const link = collection._links[linkType].href;
|
||||
onPageChange(link);
|
||||
};
|
||||
|
||||
renderFirstButton() {
|
||||
return this.renderPageButton(1, "first");
|
||||
}
|
||||
|
||||
renderPreviousButton() {
|
||||
const { t } = this.props;
|
||||
return this.renderButton(
|
||||
"pagination-previous",
|
||||
t("paginator.previous"),
|
||||
"prev"
|
||||
);
|
||||
}
|
||||
|
||||
renderNextButton() {
|
||||
const { t } = this.props;
|
||||
return this.renderButton("pagination-next", t("paginator.next"), "next");
|
||||
}
|
||||
|
||||
renderLastButton() {
|
||||
const { collection } = this.props;
|
||||
return this.renderPageButton(collection.pageTotal, "last");
|
||||
}
|
||||
|
||||
renderPageButton(page: number, linkType: string) {
|
||||
return this.renderButton("pagination-link", page.toString(), linkType);
|
||||
}
|
||||
|
||||
renderButton(className: string, label: string, linkType: string) {
|
||||
return (
|
||||
<Button
|
||||
className={className}
|
||||
label={label}
|
||||
disabled={this.isLinkUnavailable(linkType)}
|
||||
action={this.createAction(linkType)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
seperator() {
|
||||
return <span className="pagination-ellipsis">…</span>;
|
||||
}
|
||||
|
||||
currentPage(page: number) {
|
||||
return (
|
||||
<Button
|
||||
className="pagination-link is-current"
|
||||
label={page}
|
||||
disabled={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
pageLinks() {
|
||||
const { collection } = this.props;
|
||||
|
||||
const links = [];
|
||||
const page = collection.page + 1;
|
||||
const pageTotal = collection.pageTotal;
|
||||
if (page > 1) {
|
||||
links.push(this.renderFirstButton());
|
||||
}
|
||||
if (page > 3) {
|
||||
links.push(this.seperator());
|
||||
}
|
||||
if (page > 2) {
|
||||
links.push(this.renderPageButton(page - 1, "prev"));
|
||||
}
|
||||
|
||||
links.push(this.currentPage(page));
|
||||
|
||||
if (page + 1 < pageTotal) {
|
||||
links.push(this.renderPageButton(page + 1, "next"));
|
||||
}
|
||||
if(page+2 < pageTotal) //if there exists pages between next and last
|
||||
links.push(this.seperator());
|
||||
if (page < pageTotal) {
|
||||
links.push(this.renderLastButton());
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<nav className="pagination is-centered" aria-label="pagination">
|
||||
{this.renderPreviousButton()}
|
||||
{this.renderNextButton()}
|
||||
<ul className="pagination-list">
|
||||
{this.pageLinks().map((link, index) => {
|
||||
return <li key={index}>{link}</li>;
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(Paginator);
|
||||
@@ -1,253 +0,0 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import "../tests/enzyme";
|
||||
import "../tests/i18n";
|
||||
|
||||
import Paginator from "./Paginator";
|
||||
|
||||
describe("paginator rendering tests", () => {
|
||||
const dummyLink = {
|
||||
href: "https://dummy"
|
||||
};
|
||||
|
||||
it("should render all buttons but disabled, without links", () => {
|
||||
const collection = {
|
||||
page: 10,
|
||||
pageTotal: 20,
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(7);
|
||||
for (let button of buttons) {
|
||||
expect(button.props.disabled).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("should render buttons for first page", () => {
|
||||
const collection = {
|
||||
page: 0,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(5);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeTruthy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeTruthy();
|
||||
expect(firstButton.label).toBe(1);
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(3).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("2");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(4).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should render buttons for second page", () => {
|
||||
const collection = {
|
||||
page: 1,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(6);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
// current button
|
||||
const currentButton = buttons.get(3).props;
|
||||
expect(currentButton.disabled).toBeTruthy();
|
||||
expect(currentButton.label).toBe(2);
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(4).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("3");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(5).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should render buttons for last page", () => {
|
||||
const collection = {
|
||||
page: 147,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(5);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeTruthy();
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(3).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("147");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(4).props;
|
||||
expect(lastButton.disabled).toBeTruthy();
|
||||
expect(lastButton.label).toBe(148);
|
||||
});
|
||||
|
||||
it("should render buttons for penultimate page", () => {
|
||||
const collection = {
|
||||
page: 146,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(6);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
const currentButton = buttons.get(3).props;
|
||||
expect(currentButton.disabled).toBeFalsy();
|
||||
expect(currentButton.label).toBe("146");
|
||||
|
||||
// current button
|
||||
const nextButton = buttons.get(4).props;
|
||||
expect(nextButton.disabled).toBeTruthy();
|
||||
expect(nextButton.label).toBe(147);
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(5).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should render buttons for a page in the middle", () => {
|
||||
const collection = {
|
||||
page: 41,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(7);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// next button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
// previous Button
|
||||
const previousButton = buttons.get(3).props;
|
||||
expect(previousButton.disabled).toBeFalsy();
|
||||
expect(previousButton.label).toBe("41");
|
||||
|
||||
// current button
|
||||
const currentButton = buttons.get(4).props;
|
||||
expect(currentButton.disabled).toBeTruthy();
|
||||
expect(currentButton.label).toBe(42);
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(5).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("43");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(6).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should call the function with the last previous url", () => {
|
||||
const collection = {
|
||||
page: 41,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: {
|
||||
href: "https://www.scm-manager.org"
|
||||
},
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
let urlToOpen;
|
||||
const callMe = (url: string) => {
|
||||
urlToOpen = url;
|
||||
};
|
||||
|
||||
const paginator = mount(
|
||||
<Paginator collection={collection} onPageChange={callMe} />
|
||||
);
|
||||
paginator.find("Button.pagination-previous").simulate("click");
|
||||
|
||||
expect(urlToOpen).toBe("https://www.scm-manager.org");
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
//@flow
|
||||
import React, { Component } from "react";
|
||||
import { Route, Redirect, withRouter } from "react-router-dom";
|
||||
|
||||
type Props = {
|
||||
authenticated?: boolean,
|
||||
component: Component<any, any>
|
||||
};
|
||||
|
||||
class ProtectedRoute extends React.Component<Props> {
|
||||
renderRoute = (Component: any, authenticated?: boolean) => {
|
||||
return (routeProps: any) => {
|
||||
if (authenticated) {
|
||||
return <Component {...routeProps} />;
|
||||
} else {
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/login",
|
||||
state: { from: routeProps.location }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
render() {
|
||||
const { component, authenticated, ...routeProps } = this.props;
|
||||
return (
|
||||
<Route
|
||||
{...routeProps}
|
||||
render={this.renderRoute(component, authenticated)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(ProtectedRoute);
|
||||
@@ -1,11 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class AddButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="default" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default AddButton;
|
||||
@@ -1,69 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export type ButtonProps = {
|
||||
label: string,
|
||||
loading?: boolean,
|
||||
disabled?: boolean,
|
||||
action?: (event: Event) => void,
|
||||
link?: string,
|
||||
fullWidth?: boolean,
|
||||
className?: string,
|
||||
classes: any
|
||||
};
|
||||
|
||||
type Props = ButtonProps & {
|
||||
type: string,
|
||||
color: string
|
||||
};
|
||||
|
||||
class Button extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
type: "button",
|
||||
color: "default"
|
||||
};
|
||||
|
||||
renderButton = () => {
|
||||
const {
|
||||
label,
|
||||
loading,
|
||||
disabled,
|
||||
type,
|
||||
color,
|
||||
action,
|
||||
fullWidth,
|
||||
className
|
||||
} = this.props;
|
||||
const loadingClass = loading ? "is-loading" : "";
|
||||
const fullWidthClass = fullWidth ? "is-fullwidth" : "";
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
onClick={action ? action : (event: Event) => {}}
|
||||
className={classNames(
|
||||
"button",
|
||||
"is-" + color,
|
||||
loadingClass,
|
||||
fullWidthClass,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { link } = this.props;
|
||||
if (link) {
|
||||
return <Link to={link}>{this.renderButton()}</Link>;
|
||||
} else {
|
||||
return this.renderButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Button;
|
||||
@@ -1,24 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import AddButton, { type ButtonProps } from "./Button";
|
||||
import classNames from "classnames";
|
||||
|
||||
const styles = {
|
||||
spacing: {
|
||||
margin: "1em 0 0 1em"
|
||||
}
|
||||
};
|
||||
|
||||
class CreateButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<div className={classNames("is-pulled-right", classes.spacing)}>
|
||||
<AddButton {...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(CreateButton);
|
||||
@@ -1,11 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class DeleteButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="warning" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default DeleteButton;
|
||||
@@ -1,11 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class EditButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="default" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default EditButton;
|
||||
@@ -1,33 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { DeleteButton } from ".";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
entryname: string,
|
||||
removeEntry: string => void,
|
||||
disabled: boolean,
|
||||
label: string
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
||||
class RemoveEntryOfTableButton extends React.Component<Props, State> {
|
||||
render() {
|
||||
const { label, entryname, removeEntry, disabled } = this.props;
|
||||
return (
|
||||
<div className={classNames("is-pulled-right")}>
|
||||
<DeleteButton
|
||||
label={label}
|
||||
action={(event: Event) => {
|
||||
event.preventDefault();
|
||||
removeEntry(entryname);
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RemoveEntryOfTableButton;
|
||||
@@ -1,11 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class SubmitButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button type="submit" color="primary" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default SubmitButton;
|
||||
@@ -1,7 +0,0 @@
|
||||
export { default as Button } from "./Button";
|
||||
export { default as AddButton } from "./AddButton";
|
||||
export { default as CreateButton } from "./CreateButton";
|
||||
export { default as DeleteButton } from "./DeleteButton";
|
||||
export { default as EditButton } from "./EditButton";
|
||||
export { default as SubmitButton } from "./SubmitButton";
|
||||
export {default as RemoveEntryOfTableButton} from "./RemoveEntryOfTableButton";
|
||||
@@ -1,68 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import { AddButton } from "../buttons";
|
||||
import InputField from "./InputField";
|
||||
|
||||
type Props = {
|
||||
addEntry: string => void,
|
||||
disabled: boolean,
|
||||
buttonLabel: string,
|
||||
fieldLabel: string,
|
||||
errorMessage: string
|
||||
};
|
||||
|
||||
type State = {
|
||||
entryToAdd: string
|
||||
};
|
||||
|
||||
class AddEntryToTableField extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
entryToAdd: ""
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { disabled, buttonLabel, fieldLabel, errorMessage } = this.props;
|
||||
return (
|
||||
<div className="field">
|
||||
<InputField
|
||||
label={fieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={this.handleAddEntryChange}
|
||||
validationError={false}
|
||||
value={this.state.entryToAdd}
|
||||
onReturnPressed={this.appendEntry}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<AddButton
|
||||
label={buttonLabel}
|
||||
action={this.addButtonClicked}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
addButtonClicked = (event: Event) => {
|
||||
event.preventDefault();
|
||||
this.appendEntry();
|
||||
};
|
||||
|
||||
appendEntry = () => {
|
||||
const { entryToAdd } = this.state;
|
||||
this.props.addEntry(entryToAdd);
|
||||
this.setState({ ...this.state, entryToAdd: "" });
|
||||
};
|
||||
|
||||
handleAddEntryChange = (entryname: string) => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
entryToAdd: entryname
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default AddEntryToTableField;
|
||||
@@ -1,36 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
checked: boolean,
|
||||
onChange?: boolean => void,
|
||||
disabled?: boolean
|
||||
};
|
||||
class Checkbox extends React.Component<Props> {
|
||||
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(event.target.checked);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<label className="checkbox" disabled={this.props.disabled}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.props.checked}
|
||||
onChange={this.onCheckboxChange}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
{this.props.label}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Checkbox;
|
||||
@@ -1,94 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
placeholder?: string,
|
||||
value?: string,
|
||||
type?: string,
|
||||
autofocus?: boolean,
|
||||
onChange: string => void,
|
||||
onReturnPressed?: () => void,
|
||||
validationError: boolean,
|
||||
errorMessage: string,
|
||||
disabled?: boolean
|
||||
};
|
||||
|
||||
class InputField extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
type: "text",
|
||||
placeholder: ""
|
||||
};
|
||||
|
||||
field: ?HTMLInputElement;
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autofocus && this.field) {
|
||||
this.field.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
this.props.onChange(event.target.value);
|
||||
};
|
||||
|
||||
renderLabel = () => {
|
||||
const label = this.props.label;
|
||||
if (label) {
|
||||
return <label className="label">{label}</label>;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
|
||||
handleKeyPress = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
||||
const onReturnPressed = this.props.onReturnPressed;
|
||||
if (!onReturnPressed) {
|
||||
return;
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
onReturnPressed();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
type,
|
||||
placeholder,
|
||||
value,
|
||||
validationError,
|
||||
errorMessage,
|
||||
disabled
|
||||
} = this.props;
|
||||
const errorView = validationError ? "is-danger" : "";
|
||||
const helper = validationError ? (
|
||||
<p className="help is-danger">{errorMessage}</p>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
return (
|
||||
<div className="field">
|
||||
{this.renderLabel()}
|
||||
<div className="control">
|
||||
<input
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
className={classNames("input", errorView)}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={this.handleInput}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
{helper}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default InputField;
|
||||
@@ -1,72 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
export type SelectItem = {
|
||||
value: string,
|
||||
label: string
|
||||
};
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
options: SelectItem[],
|
||||
value?: SelectItem,
|
||||
onChange: string => void,
|
||||
loading?: boolean
|
||||
};
|
||||
|
||||
class Select extends React.Component<Props> {
|
||||
field: ?HTMLSelectElement;
|
||||
|
||||
componentDidMount() {
|
||||
// trigger change after render, if value is null to set it to the first value
|
||||
// of the given options.
|
||||
if (!this.props.value && this.field && this.field.value) {
|
||||
this.props.onChange(this.field.value);
|
||||
}
|
||||
}
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLSelectElement>) => {
|
||||
this.props.onChange(event.target.value);
|
||||
};
|
||||
|
||||
renderLabel = () => {
|
||||
const label = this.props.label;
|
||||
if (label) {
|
||||
return <label className="label">{label}</label>;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options, value, loading } = this.props;
|
||||
const loadingClass = loading ? "is-loading" : "";
|
||||
return (
|
||||
<div className="field">
|
||||
{this.renderLabel()}
|
||||
<div className={classNames(
|
||||
"control select",
|
||||
loadingClass
|
||||
)}>
|
||||
<select
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
value={value}
|
||||
onChange={this.handleInput}
|
||||
>
|
||||
{options.map(opt => {
|
||||
return (
|
||||
<option value={opt.value} key={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Select;
|
||||
@@ -1,53 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
export type SelectItem = {
|
||||
value: string,
|
||||
label: string
|
||||
};
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
placeholder?: SelectItem[],
|
||||
value?: string,
|
||||
onChange: string => void
|
||||
};
|
||||
|
||||
class Textarea extends React.Component<Props> {
|
||||
field: ?HTMLTextAreaElement;
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLTextAreaElement>) => {
|
||||
this.props.onChange(event.target.value);
|
||||
};
|
||||
|
||||
renderLabel = () => {
|
||||
const label = this.props.label;
|
||||
if (label) {
|
||||
return <label className="label">{label}</label>;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
render() {
|
||||
const { placeholder, value } = this.props;
|
||||
|
||||
return (
|
||||
<div className="field">
|
||||
{this.renderLabel()}
|
||||
<div className="control">
|
||||
<textarea
|
||||
className="textarea"
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
onChange={this.handleInput}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Textarea;
|
||||
@@ -1,3 +0,0 @@
|
||||
export { default as Checkbox } from "./Checkbox";
|
||||
export { default as InputField } from "./InputField";
|
||||
export { default as Select } from "./Select";
|
||||
@@ -1,25 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Me } from "../../types/Me";
|
||||
|
||||
type Props = {
|
||||
me?: Me
|
||||
};
|
||||
|
||||
class Footer extends React.Component<Props> {
|
||||
render() {
|
||||
const { me } = this.props;
|
||||
if (!me) {
|
||||
return "";
|
||||
}
|
||||
return (
|
||||
<footer className="footer">
|
||||
<div className="container is-centered">
|
||||
<p className="has-text-centered">{me.displayName}</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
@@ -1,31 +0,0 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import Logo from "./../Logo";
|
||||
|
||||
type Props = {
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Header extends React.Component<Props> {
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return (
|
||||
<section className="hero is-dark is-small">
|
||||
<div className="hero-body">
|
||||
<div className="container">
|
||||
<div className="columns is-vcentered">
|
||||
<div className="column">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hero-foot">
|
||||
<div className="container">{children}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@@ -1,44 +0,0 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import Loading from "./../Loading";
|
||||
import ErrorNotification from "./../ErrorNotification";
|
||||
import Title from "./Title";
|
||||
import Subtitle from "./Subtitle";
|
||||
|
||||
type Props = {
|
||||
title?: string,
|
||||
subtitle?: string,
|
||||
loading?: boolean,
|
||||
error?: Error,
|
||||
showContentOnError?: boolean,
|
||||
children: React.Node
|
||||
};
|
||||
|
||||
class Page extends React.Component<Props> {
|
||||
render() {
|
||||
const { title, error, subtitle } = this.props;
|
||||
return (
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<Title title={title} />
|
||||
<Subtitle subtitle={subtitle} />
|
||||
<ErrorNotification error={error} />
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
const { loading, children, showContentOnError, error } = this.props;
|
||||
if (error && !showContentOnError) {
|
||||
return null;
|
||||
}
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -1,18 +0,0 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
subtitle?: string
|
||||
};
|
||||
|
||||
class Subtitle extends React.Component<Props> {
|
||||
render() {
|
||||
const { subtitle } = this.props;
|
||||
if (subtitle) {
|
||||
return <h1 className="subtitle">{subtitle}</h1>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Subtitle;
|
||||
@@ -1,18 +0,0 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
title?: string
|
||||
};
|
||||
|
||||
class Title extends React.Component<Props> {
|
||||
render() {
|
||||
const { title } = this.props;
|
||||
if (title) {
|
||||
return <h1 className="title">{title}</h1>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Title;
|
||||
@@ -1,3 +0,0 @@
|
||||
export { default as Footer } from "./Footer";
|
||||
export { default as Header } from "./Header";
|
||||
export { default as Page } from "./Page";
|
||||
@@ -1,102 +0,0 @@
|
||||
/*modified from https://github.com/GA-MO/react-confirm-alert*/
|
||||
.react-confirm-alert-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
display: -webkit-flex;
|
||||
display: -moz-flex;
|
||||
display: -ms-flex;
|
||||
display: -o-flex;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
-ms-align-items: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
-webkit-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
-moz-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
-o-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
}
|
||||
|
||||
.react-confirm-alert-body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
width: 400px;
|
||||
padding: 30px;
|
||||
text-align: left;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 20px 75px rgba(0, 0, 0, 0.13);
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.react-confirm-alert-body > h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.react-confirm-alert-body > h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.react-confirm-alert-button-group {
|
||||
display: -webkit-flex;
|
||||
display: -moz-flex;
|
||||
display: -ms-flex;
|
||||
display: -o-flex;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.react-confirm-alert-button-group > button {
|
||||
outline: none;
|
||||
background: #333;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
padding: 6px 18px;
|
||||
color: #eee;
|
||||
margin-right: 10px;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@-webkit-keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-o-keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// @flow
|
||||
//modified from https://github.com/GA-MO/react-confirm-alert
|
||||
|
||||
import * as React from "react";
|
||||
import { render, unmountComponentAtNode } from "react-dom";
|
||||
import "./ConfirmAlert.css";
|
||||
|
||||
type Button = {
|
||||
label: string,
|
||||
onClick: () => void | null
|
||||
};
|
||||
|
||||
type Props = {
|
||||
title: string,
|
||||
message: string,
|
||||
buttons: Button[]
|
||||
};
|
||||
|
||||
class ConfirmAlert extends React.Component<Props> {
|
||||
handleClickButton = (button: Button) => {
|
||||
if (button.onClick) {
|
||||
button.onClick();
|
||||
}
|
||||
this.close();
|
||||
};
|
||||
|
||||
close = () => {
|
||||
removeElementReconfirm();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { title, message, buttons } = this.props;
|
||||
|
||||
return (
|
||||
<div className="react-confirm-alert-overlay">
|
||||
<div className="react-confirm-alert">
|
||||
{
|
||||
<div className="react-confirm-alert-body">
|
||||
{title && <h1>{title}</h1>}
|
||||
{message}
|
||||
<div className="react-confirm-alert-button-group">
|
||||
{buttons.map((button, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => this.handleClickButton(button)}
|
||||
>
|
||||
{button.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function createElementReconfirm(properties: Props) {
|
||||
const divTarget = document.createElement("div");
|
||||
divTarget.id = "react-confirm-alert";
|
||||
if (document.body) {
|
||||
document.body.appendChild(divTarget);
|
||||
render(<ConfirmAlert {...properties} />, divTarget);
|
||||
}
|
||||
}
|
||||
|
||||
function removeElementReconfirm() {
|
||||
const target = document.getElementById("react-confirm-alert");
|
||||
if (target) {
|
||||
unmountComponentAtNode(target);
|
||||
if (target.parentNode) {
|
||||
target.parentNode.removeChild(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function confirmAlert(properties: Props) {
|
||||
createElementReconfirm(properties);
|
||||
}
|
||||
|
||||
export default ConfirmAlert;
|
||||
@@ -1 +0,0 @@
|
||||
export { default as ConfirmAlert } from "./ConfirmAlert";
|
||||
@@ -1,20 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
label: string,
|
||||
action: () => void
|
||||
};
|
||||
|
||||
class NavAction extends React.Component<Props> {
|
||||
render() {
|
||||
const { label, action } = this.props;
|
||||
return (
|
||||
<li>
|
||||
<a onClick={action}>{label}</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NavAction;
|
||||
@@ -1,37 +0,0 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import { Route, Link } from "react-router-dom";
|
||||
|
||||
// TODO mostly copy of PrimaryNavigationLink
|
||||
|
||||
type Props = {
|
||||
to: string,
|
||||
label: string,
|
||||
activeOnlyWhenExact?: boolean
|
||||
};
|
||||
|
||||
class NavLink extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
activeOnlyWhenExact: true
|
||||
};
|
||||
|
||||
renderLink = (route: any) => {
|
||||
const { to, label } = this.props;
|
||||
return (
|
||||
<li>
|
||||
<Link className={route.match ? "is-active" : ""} to={to}>
|
||||
{label}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { to, activeOnlyWhenExact } = this.props;
|
||||
return (
|
||||
<Route path={to} exact={activeOnlyWhenExact} children={this.renderLink} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NavLink;
|
||||
@@ -1,14 +0,0 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Navigation extends React.Component<Props> {
|
||||
render() {
|
||||
return <aside className="menu">{this.props.children}</aside>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Navigation;
|
||||
@@ -1,45 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import PrimaryNavigationLink from "./PrimaryNavigationLink";
|
||||
|
||||
type Props = {
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class PrimaryNavigation extends React.Component<Props> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<nav className="tabs is-boxed">
|
||||
<ul>
|
||||
<PrimaryNavigationLink
|
||||
to="/repos"
|
||||
match="/(repo|repos)"
|
||||
label={t("primary-navigation.repositories")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/users"
|
||||
match="/(user|users)"
|
||||
label={t("primary-navigation.users")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/groups"
|
||||
match="/(group|groups)"
|
||||
label={t("primary-navigation.groups")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/config"
|
||||
label={t("primary-navigation.config")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/logout"
|
||||
label={t("primary-navigation.logout")}
|
||||
/>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(PrimaryNavigation);
|
||||
@@ -1,35 +0,0 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import { Route, Link } from "react-router-dom";
|
||||
|
||||
type Props = {
|
||||
to: string,
|
||||
label: string,
|
||||
match?: string,
|
||||
activeOnlyWhenExact?: boolean
|
||||
};
|
||||
|
||||
class PrimaryNavigationLink extends React.Component<Props> {
|
||||
renderLink = (route: any) => {
|
||||
const { to, label } = this.props;
|
||||
return (
|
||||
<li className={route.match ? "is-active" : ""}>
|
||||
<Link to={to}>{label}</Link>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { to, match, activeOnlyWhenExact } = this.props;
|
||||
const path = match ? match : to;
|
||||
return (
|
||||
<Route
|
||||
path={path}
|
||||
exact={activeOnlyWhenExact}
|
||||
children={this.renderLink}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PrimaryNavigationLink;
|
||||
@@ -1,21 +0,0 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
label: string,
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Section extends React.Component<Props> {
|
||||
render() {
|
||||
const { label, children } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<p className="menu-label">{label}</p>
|
||||
<ul className="menu-list">{children}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Section;
|
||||
@@ -1,8 +0,0 @@
|
||||
//primary Navigation
|
||||
export { default as PrimaryNavigation } from "./PrimaryNavigation";
|
||||
export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink";
|
||||
//secondary Navigation
|
||||
export { default as Navigation } from "./Navigation";
|
||||
export { default as Section } from "./Section";
|
||||
export { default as NavLink } from "./NavLink";
|
||||
export { default as NavAction } from "./NavAction";
|
||||
@@ -1,16 +0,0 @@
|
||||
// @flow
|
||||
const nameRegex = /^([A-z0-9.\-_@]|[^ ]([A-z0-9.\-_@ ]*[A-z0-9.\-_@]|[^\s])?)$/;
|
||||
|
||||
export const isNameValid = (name: string) => {
|
||||
return nameRegex.test(name);
|
||||
};
|
||||
|
||||
const mailRegex = /^[A-z0-9][\w.-]*@[A-z0-9][\w\-.]*\.[A-z0-9][A-z0-9-]+$/;
|
||||
|
||||
export const isMailValid = (mail: string) => {
|
||||
return mailRegex.test(mail);
|
||||
};
|
||||
|
||||
export const isNumberValid = (number: string) => {
|
||||
return !isNaN(number);
|
||||
};
|
||||
@@ -1,102 +0,0 @@
|
||||
// @flow
|
||||
import * as validator from "./validation";
|
||||
|
||||
describe("test name validation", () => {
|
||||
it("should return false", () => {
|
||||
// invalid names taken from ValidationUtilTest.java
|
||||
const invalidNames = [
|
||||
" test 123",
|
||||
" test 123 ",
|
||||
"test 123 ",
|
||||
"test/123",
|
||||
"test%123",
|
||||
"test:123",
|
||||
"t ",
|
||||
" t",
|
||||
" t ",
|
||||
"",
|
||||
|
||||
" invalid_name",
|
||||
"another%one",
|
||||
"!!!",
|
||||
"!_!"
|
||||
];
|
||||
for (let name of invalidNames) {
|
||||
expect(validator.isNameValid(name)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return true", () => {
|
||||
// valid names taken from ValidationUtilTest.java
|
||||
const validNames = [
|
||||
"test",
|
||||
"test.git",
|
||||
"Test123.git",
|
||||
"Test123-git",
|
||||
"Test_user-123.git",
|
||||
"test@scm-manager.de",
|
||||
"test 123",
|
||||
"tt",
|
||||
"t",
|
||||
|
||||
"valid_name",
|
||||
"another1",
|
||||
"stillValid",
|
||||
"this.one_as-well",
|
||||
"and@this"
|
||||
];
|
||||
for (let name of validNames) {
|
||||
expect(validator.isNameValid(name)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("test mail validation", () => {
|
||||
it("should return false", () => {
|
||||
// invalid taken from ValidationUtilTest.java
|
||||
const invalid = [
|
||||
"ostfalia.de",
|
||||
"@ostfalia.de",
|
||||
"s.sdorra@",
|
||||
"s.sdorra@ostfalia",
|
||||
"s.sdorra@@ostfalia.de",
|
||||
"s.sdorra@ ostfalia.de",
|
||||
"s.sdorra @ostfalia.de"
|
||||
];
|
||||
for (let mail of invalid) {
|
||||
expect(validator.isMailValid(mail)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return true", () => {
|
||||
// valid taken from ValidationUtilTest.java
|
||||
const valid = [
|
||||
"s.sdorra@ostfalia.de",
|
||||
"sdorra@ostfalia.de",
|
||||
"s.sdorra@hbk-bs.de",
|
||||
"s.sdorra@gmail.com",
|
||||
"s.sdorra@t.co",
|
||||
"s.sdorra@ucla.college",
|
||||
"s.sdorra@example.xn--p1ai",
|
||||
"s.sdorra@scm.solutions"
|
||||
];
|
||||
for (let mail of valid) {
|
||||
expect(validator.isMailValid(mail)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("test number validation", () => {
|
||||
it("should return false", () => {
|
||||
const invalid = ["1a", "35gu", "dj6", "45,5", "test"];
|
||||
for (let number of invalid) {
|
||||
expect(validator.isNumberValid(number)).toBe(false);
|
||||
}
|
||||
});
|
||||
it("should return true", () => {
|
||||
const valid = ["1", "35", "2", "235", "34.4"];
|
||||
for (let number of valid) {
|
||||
expect(validator.isNumberValid(number)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,9 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import Subtitle from "../../../components/layout/Subtitle";
|
||||
import { Subtitle, AddEntryToTableField } from "@scm-manager/ui-components";
|
||||
import AdminGroupTable from "../table/AdminGroupTable";
|
||||
import AdminUserTable from "../table/AdminUserTable";
|
||||
import AddEntryToTableField from "../../../components/forms/AddEntryToTableField";
|
||||
|
||||
type Props = {
|
||||
adminGroups: string[],
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Checkbox, InputField } from "../../../components/forms/index";
|
||||
import Subtitle from "../../../components/layout/Subtitle";
|
||||
import { Checkbox, InputField, Subtitle } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
baseUrl: string,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { SubmitButton } from "../../../components/buttons/index";
|
||||
import type { Config } from "../../types/Config";
|
||||
import { SubmitButton, Notification } from "@scm-manager/ui-components";
|
||||
import type { Config } from "@scm-manager/ui-types";
|
||||
import ProxySettings from "./ProxySettings";
|
||||
import GeneralSettings from "./GeneralSettings";
|
||||
import BaseUrlSettings from "./BaseUrlSettings";
|
||||
import AdminSettings from "./AdminSettings";
|
||||
import Notification from "../../../components/Notification";
|
||||
import LoginAttempt from "./LoginAttempt";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Checkbox, InputField } from "../../../components/forms/index";
|
||||
import { Checkbox, InputField } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
realmDescription: string,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { InputField } from "../../../components/forms/index";
|
||||
import Subtitle from "../../../components/layout/Subtitle";
|
||||
import * as validator from "../../../components/validation";
|
||||
import {
|
||||
InputField,
|
||||
Subtitle,
|
||||
validation as validator
|
||||
} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
loginAttemptLimit: number,
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Checkbox, InputField } from "../../../components/forms/index";
|
||||
import Subtitle from "../../../components/layout/Subtitle";
|
||||
import {
|
||||
Checkbox,
|
||||
InputField,
|
||||
Subtitle,
|
||||
AddEntryToTableField
|
||||
} from "@scm-manager/ui-components";
|
||||
import ProxyExcludesTable from "../table/ProxyExcludesTable";
|
||||
import AddEntryToTableField from "../../../components/forms/AddEntryToTableField";
|
||||
|
||||
type Props = {
|
||||
proxyPassword: string,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { RemoveEntryOfTableButton } from "../../../components/buttons";
|
||||
import { RemoveEntryOfTableButton } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
items: string[],
|
||||
|
||||
@@ -3,8 +3,7 @@ import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Route } from "react-router";
|
||||
|
||||
import { Page } from "../../components/layout";
|
||||
import { Navigation, NavLink, Section } from "../../components/navigation";
|
||||
import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components";
|
||||
import GlobalConfig from "./GlobalConfig";
|
||||
import type { History } from "history";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import Title from "../../components/layout/Title";
|
||||
import { Title, ErrorPage, Loading } from "@scm-manager/ui-components";
|
||||
import {
|
||||
fetchConfig,
|
||||
getFetchConfigFailure,
|
||||
@@ -14,10 +14,8 @@ import {
|
||||
modifyConfigReset
|
||||
} from "../modules/config";
|
||||
import { connect } from "react-redux";
|
||||
import ErrorPage from "../../components/ErrorPage";
|
||||
import type { Config } from "../types/Config";
|
||||
import type { Config } from "@scm-manager/ui-types";
|
||||
import ConfigForm from "../components/form/ConfigForm";
|
||||
import Loading from "../../components/Loading";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// @flow
|
||||
import { apiClient } from "../../apiclient";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import * as types from "../../modules/types";
|
||||
import type { Action } from "../../types/Action";
|
||||
import type { Action } from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../modules/pending";
|
||||
import { getFailure } from "../../modules/failure";
|
||||
import { Dispatch } from "redux";
|
||||
import type { Config } from "../types/Config";
|
||||
import type { Config } from "@scm-manager/ui-types";
|
||||
|
||||
export const FETCH_CONFIG = "scm/config/FETCH_CONFIG";
|
||||
export const FETCH_CONFIG_PENDING = `${FETCH_CONFIG}_${types.PENDING_SUFFIX}`;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
//@flow
|
||||
import type { Links } from "../../types/hal";
|
||||
|
||||
export type Config = {
|
||||
proxyPassword: string | null,
|
||||
proxyPort: number,
|
||||
proxyServer: string,
|
||||
proxyUser: string | null,
|
||||
enableProxy: boolean,
|
||||
realmDescription: string,
|
||||
enableRepositoryArchive: boolean,
|
||||
disableGroupingGrid: boolean,
|
||||
dateFormat: string,
|
||||
anonymousAccessEnabled: boolean,
|
||||
adminGroups: string[],
|
||||
adminUsers: string[],
|
||||
baseUrl: string,
|
||||
forceBaseUrl: boolean,
|
||||
loginAttemptLimit: number,
|
||||
proxyExcludes: string[],
|
||||
skipFailedAuthenticators: boolean,
|
||||
pluginUrl: string,
|
||||
loginAttemptLimitTimeout: number,
|
||||
enabledXsrfProtection: boolean,
|
||||
defaultNamespaceStrategy: string,
|
||||
_links: Links
|
||||
};
|
||||
@@ -12,11 +12,14 @@ import {
|
||||
getFetchMeFailure
|
||||
} from "../modules/auth";
|
||||
|
||||
import { PrimaryNavigation } from "../components/navigation";
|
||||
import Loading from "../components/Loading";
|
||||
import ErrorPage from "../components/ErrorPage";
|
||||
import { Footer, Header } from "../components/layout";
|
||||
import type { Me } from "../types/Me";
|
||||
import {
|
||||
PrimaryNavigation,
|
||||
Loading,
|
||||
ErrorPage,
|
||||
Footer,
|
||||
Header
|
||||
} from "@scm-manager/ui-components";
|
||||
import type { Me } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
me: Me,
|
||||
|
||||
@@ -11,12 +11,13 @@ import {
|
||||
} from "../modules/auth";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { InputField } from "../components/forms";
|
||||
import { SubmitButton } from "../components/buttons";
|
||||
|
||||
import {
|
||||
InputField,
|
||||
SubmitButton,
|
||||
ErrorNotification,
|
||||
Image
|
||||
} from "@scm-manager/ui-components";
|
||||
import classNames from "classnames";
|
||||
import ErrorNotification from "../components/ErrorNotification";
|
||||
import Image from "../components/Image";
|
||||
|
||||
const styles = {
|
||||
avatar: {
|
||||
|
||||
@@ -10,8 +10,7 @@ import {
|
||||
isLogoutPending,
|
||||
getLogoutFailure
|
||||
} from "../modules/auth";
|
||||
import ErrorPage from "../components/ErrorPage";
|
||||
import Loading from "../components/Loading";
|
||||
import { Loading, ErrorPage } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
authenticated: boolean,
|
||||
|
||||
@@ -9,7 +9,7 @@ import Login from "../containers/Login";
|
||||
import Logout from "../containers/Logout";
|
||||
|
||||
import { Switch } from "react-router-dom";
|
||||
import ProtectedRoute from "../components/ProtectedRoute";
|
||||
import { ProtectedRoute } from "@scm-manager/ui-components";
|
||||
import AddUser from "../users/containers/AddUser";
|
||||
import SingleUser from "../users/containers/SingleUser";
|
||||
import RepositoryRoot from "../repos/containers/RepositoryRoot";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
import * as React from "react";
|
||||
import Loading from "./Loading";
|
||||
import { apiClient } from "../apiclient";
|
||||
import { apiClient, Loading } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
children: React.Node
|
||||
@@ -1,14 +1,16 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import InputField from "../../components/forms/InputField";
|
||||
import { SubmitButton } from "../../components/buttons";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Group } from "../types/Group";
|
||||
import {
|
||||
InputField,
|
||||
SubmitButton,
|
||||
Textarea,
|
||||
AddEntryToTableField
|
||||
} from "@scm-manager/ui-components";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
|
||||
import * as validator from "./groupValidation";
|
||||
import MemberNameTable from "./MemberNameTable";
|
||||
import Textarea from "../../components/forms/Textarea";
|
||||
import AddEntryToTableField from "../../components/forms/AddEntryToTableField";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { RemoveEntryOfTableButton } from "../../components/buttons";
|
||||
import { RemoveEntryOfTableButton } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
members: string[],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { CreateButton } from "../../../components/buttons";
|
||||
import { CreateButton } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
t: string => string
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
import { isNameValid } from "../../components/validation";
|
||||
import { validation } from "@scm-manager/ui-components";
|
||||
|
||||
const isNameValid = validation.isNameValid;
|
||||
|
||||
export { isNameValid };
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Group } from "../../types/Group";
|
||||
import { confirmAlert } from "../../../components/modals/ConfirmAlert";
|
||||
import { NavAction } from "../../../components/navigation";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import { NavAction, confirmAlert } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
group: Group,
|
||||
|
||||
@@ -4,8 +4,11 @@ import "../../../tests/enzyme";
|
||||
import "../../../tests/i18n";
|
||||
import DeleteGroupNavLink from "./DeleteGroupNavLink";
|
||||
|
||||
import { confirmAlert } from "../../../components/modals/ConfirmAlert";
|
||||
jest.mock("../../../components/modals/ConfirmAlert");
|
||||
import { confirmAlert } from "@scm-manager/ui-components";
|
||||
jest.mock("@scm-manager/ui-components", () => ({
|
||||
confirmAlert: jest.fn(),
|
||||
NavAction: require.requireActual("@scm-manager/ui-components").NavAction
|
||||
}));
|
||||
|
||||
describe("DeleteGroupNavLink", () => {
|
||||
it("should render nothing, if the delete link is missing", () => {
|
||||
@@ -67,7 +70,7 @@ describe("DeleteGroupNavLink", () => {
|
||||
|
||||
const navLink = mount(
|
||||
<DeleteGroupNavLink
|
||||
group={group}
|
||||
group={group}
|
||||
confirmDialog={false}
|
||||
deleteGroup={capture}
|
||||
/>
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import NavLink from "../../../components/navigation/NavLink";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Group } from "../../types/Group";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
editUrl: string,
|
||||
group: Group
|
||||
}
|
||||
};
|
||||
|
||||
type State = {
|
||||
}
|
||||
type State = {};
|
||||
|
||||
class EditGroupNavLink extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const { t, editUrl } = this.props;
|
||||
if (!this.isEditable()) {
|
||||
@@ -25,7 +23,7 @@ class EditGroupNavLink extends React.Component<Props, State> {
|
||||
|
||||
isEditable = () => {
|
||||
return this.props.group._links.update;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("groups")(EditGroupNavLink);
|
||||
export default translate("groups")(EditGroupNavLink);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Group } from "../../types/Group";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import GroupMember from "./GroupMember";
|
||||
import DateFromNow from "../../../components/DateFromNow";
|
||||
import { DateFromNow } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
group: Group,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { Member } from "../../types/Group";
|
||||
import type { Member } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
member: Member
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { Group } from "../../types/Group";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
group: Group
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import GroupRow from "./GroupRow";
|
||||
import type { Group } from "../../types/Group";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import Page from "../../components/layout/Page";
|
||||
import { Page } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import GroupForm from "../components/GroupForm";
|
||||
import { connect } from "react-redux";
|
||||
import { createGroup, isCreateGroupPending, getCreateGroupFailure, createGroupReset } from "../modules/groups";
|
||||
import type { Group } from "../types/Group";
|
||||
import {
|
||||
createGroup,
|
||||
isCreateGroupPending,
|
||||
getCreateGroupFailure,
|
||||
createGroupReset
|
||||
} from "../modules/groups";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import type { History } from "history";
|
||||
|
||||
type Props = {
|
||||
@@ -15,22 +20,28 @@ type Props = {
|
||||
history: History,
|
||||
loading?: boolean,
|
||||
error?: Error,
|
||||
resetForm: () => void,
|
||||
resetForm: () => void
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
||||
class AddGroup extends React.Component<Props, State> {
|
||||
|
||||
componentDidMount() {
|
||||
this.props.resetForm();
|
||||
}
|
||||
render() {
|
||||
const { t, loading, error } = this.props;
|
||||
return (
|
||||
<Page title={t("add-group.title")} subtitle={t("add-group.subtitle")} error={error}>
|
||||
<Page
|
||||
title={t("add-group.title")}
|
||||
subtitle={t("add-group.subtitle")}
|
||||
error={error}
|
||||
>
|
||||
<div>
|
||||
<GroupForm submitForm={group => this.createGroup(group)} loading={loading}/>
|
||||
<GroupForm
|
||||
submitForm={group => this.createGroup(group)}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
@@ -48,9 +59,9 @@ const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
createGroup: (group: Group, callback?: () => void) =>
|
||||
dispatch(createGroup(group, callback)),
|
||||
resetForm: () => {
|
||||
dispatch(createGroupReset());
|
||||
}
|
||||
resetForm: () => {
|
||||
dispatch(createGroupReset());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import GroupForm from "../components/GroupForm";
|
||||
import { modifyGroup, fetchGroup } from "../modules/groups";
|
||||
import type { History } from "history";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type { Group } from "../types/Group";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import { isModifyGroupPending, getModifyGroupFailure } from "../modules/groups";
|
||||
import ErrorNotification from "../../components/ErrorNotification";
|
||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
group: Group,
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Group } from "../types/Group.js";
|
||||
import type { PagedCollection } from "../../types/Collection";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import type { PagedCollection } from "@scm-manager/ui-types";
|
||||
import type { History } from "history";
|
||||
import { Page } from "../../components/layout";
|
||||
import { Page, Paginator } from "@scm-manager/ui-components";
|
||||
import { GroupTable } from "./../components/table";
|
||||
import Paginator from "../../components/Paginator";
|
||||
import CreateGroupButton from "../components/buttons/CreateGroupButton";
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Page } from "../../components/layout";
|
||||
import {
|
||||
Page,
|
||||
ErrorPage,
|
||||
Loading,
|
||||
Navigation,
|
||||
Section,
|
||||
NavLink
|
||||
} from "@scm-manager/ui-components";
|
||||
import { Route } from "react-router";
|
||||
import { Details } from "./../components/table";
|
||||
import { DeleteGroupNavLink, EditGroupNavLink } from "./../components/navLinks";
|
||||
import type { Group } from "../types/Group";
|
||||
import type { Group } from "@scm-manager/ui-types";
|
||||
import type { History } from "history";
|
||||
import {
|
||||
deleteGroup,
|
||||
@@ -14,12 +21,9 @@ import {
|
||||
isFetchGroupPending,
|
||||
getFetchGroupFailure,
|
||||
getDeleteGroupFailure,
|
||||
isDeleteGroupPending,
|
||||
isDeleteGroupPending
|
||||
} from "../modules/groups";
|
||||
import Loading from "../../components/Loading";
|
||||
|
||||
import { Navigation, Section, NavLink } from "../../components/navigation";
|
||||
import ErrorPage from "../../components/ErrorPage";
|
||||
import { translate } from "react-i18next";
|
||||
import EditGroup from "./EditGroup";
|
||||
|
||||
@@ -86,8 +90,16 @@ class SingleGroup extends React.Component<Props> {
|
||||
<Page title={group.name}>
|
||||
<div className="columns">
|
||||
<div className="column is-three-quarters">
|
||||
<Route path={url} exact component={() => <Details group={group} />} />
|
||||
<Route path={`${url}/edit`} exact component={() => <EditGroup group={group} />} />
|
||||
<Route
|
||||
path={url}
|
||||
exact
|
||||
component={() => <Details group={group} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/edit`}
|
||||
exact
|
||||
component={() => <EditGroup group={group} />}
|
||||
/>
|
||||
</div>
|
||||
<div className="column">
|
||||
<Navigation>
|
||||
@@ -98,8 +110,11 @@ class SingleGroup extends React.Component<Props> {
|
||||
/>
|
||||
</Section>
|
||||
<Section label={t("single-group.actions-label")}>
|
||||
<DeleteGroupNavLink group={group} deleteGroup={this.deleteGroup} />
|
||||
<EditGroupNavLink group={group} editUrl={`${url}/edit`}/>
|
||||
<DeleteGroupNavLink
|
||||
group={group}
|
||||
deleteGroup={this.deleteGroup}
|
||||
/>
|
||||
<EditGroupNavLink group={group} editUrl={`${url}/edit`} />
|
||||
<NavLink to="/groups" label={t("single-group.back-label")} />
|
||||
</Section>
|
||||
</Navigation>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// @flow
|
||||
import { apiClient } from "../../apiclient";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { isPending } from "../../modules/pending";
|
||||
import { getFailure } from "../../modules/failure";
|
||||
import * as types from "../../modules/types";
|
||||
import { combineReducers, Dispatch } from "redux";
|
||||
import type { Action } from "../../types/Action";
|
||||
import type { PagedCollection } from "../../types/Collection";
|
||||
import type { Group } from "../types/Group";
|
||||
import type { Action, PagedCollection, Group } from "@scm-manager/ui-types";
|
||||
|
||||
export const FETCH_GROUPS = "scm/groups/FETCH_GROUPS";
|
||||
export const FETCH_GROUPS_PENDING = `${FETCH_GROUPS}_${types.PENDING_SUFFIX}`;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
//@flow
|
||||
import type { Collection } from "../../types/Collection";
|
||||
import type { Links } from "../../types/hal";
|
||||
|
||||
export type Member = {
|
||||
name: string,
|
||||
_links: Links
|
||||
};
|
||||
|
||||
export type Group = Collection & {
|
||||
name: string,
|
||||
description: string,
|
||||
type: string,
|
||||
members: string[],
|
||||
_embedded: {
|
||||
members: Member[]
|
||||
},
|
||||
creationDate?: string,
|
||||
lastModified?: string
|
||||
};
|
||||
@@ -2,9 +2,9 @@ import i18n from "i18next";
|
||||
import Backend from "i18next-fetch-backend";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import { reactI18nextModule } from "react-i18next";
|
||||
import { withContextPath } from "./urls";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
const loadPath = withContextPath("/locales/{{lng}}/{{ns}}.json");
|
||||
const loadPath = urls.withContextPath("/locales/{{lng}}/{{ns}}.json");
|
||||
|
||||
// TODO load locales for moment
|
||||
|
||||
|
||||
@@ -14,13 +14,13 @@ import type { BrowserHistory } from "history/createBrowserHistory";
|
||||
|
||||
import createReduxStore from "./createReduxStore";
|
||||
import { ConnectedRouter } from "react-router-redux";
|
||||
import PluginLoader from "./components/PluginLoader";
|
||||
import PluginLoader from "./containers/PluginLoader";
|
||||
|
||||
import { contextPath } from "./urls";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
// Create a history of your choosing (we're using a browser history in this case)
|
||||
const history: BrowserHistory = createHistory({
|
||||
basename: contextPath
|
||||
basename: urls.contextPath
|
||||
});
|
||||
|
||||
// Add the reducer to your store on the `router` key
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// @flow
|
||||
import type { Me } from "../types/Me";
|
||||
import type { Me } from "@scm-manager/ui-components";
|
||||
import * as types from "./types";
|
||||
|
||||
import { apiClient, UNAUTHORIZED_ERROR } from "../apiclient";
|
||||
import { apiClient, UNAUTHORIZED_ERROR } from "@scm-manager/ui-components";
|
||||
import { isPending } from "./pending";
|
||||
import { getFailure } from "./failure";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @flow
|
||||
import type { Action } from "../types/Action";
|
||||
import type { Action } from "@scm-manager/ui-types";
|
||||
|
||||
const FAILURE_SUFFIX = "_FAILURE";
|
||||
const RESET_PATTERN = /^(.*)_(SUCCESS|RESET)$/;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @flow
|
||||
import type { Action } from "../types/Action";
|
||||
import type { Action } from "@scm-manager/ui-types";
|
||||
import * as types from "./types";
|
||||
|
||||
const PENDING_SUFFIX = "_" + types.PENDING_SUFFIX;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Checkbox, InputField } from "../../components/forms";
|
||||
import {
|
||||
Checkbox,
|
||||
InputField,
|
||||
SubmitButton
|
||||
} from "@scm-manager/ui-components";
|
||||
import TypeSelector from "./TypeSelector";
|
||||
import type {
|
||||
PermissionCollection,
|
||||
PermissionEntry
|
||||
} from "../types/Permissions";
|
||||
import { SubmitButton } from "../../components/buttons";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { Select } from "../../components/forms";
|
||||
import {
|
||||
Select
|
||||
} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Permission } from "../../types/Permissions";
|
||||
import { confirmAlert } from "../../../components/modals/ConfirmAlert";
|
||||
import { DeleteButton } from "../../../components/buttons/index";
|
||||
import {
|
||||
confirmAlert,
|
||||
DeleteButton
|
||||
} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
permission: Permission,
|
||||
|
||||
@@ -14,8 +14,10 @@ import {
|
||||
createPermissionReset,
|
||||
getDeletePermissionsFailure
|
||||
} from "../modules/permissions";
|
||||
import Loading from "../../components/Loading";
|
||||
import ErrorPage from "../../components/ErrorPage";
|
||||
import {
|
||||
Loading,
|
||||
ErrorPage
|
||||
} from "@scm-manager/ui-components";
|
||||
import type {
|
||||
Permission,
|
||||
PermissionCollection,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import type { Permission } from "../types/Permissions";
|
||||
import { Checkbox } from "../../components/forms/index";
|
||||
import { translate } from "react-i18next";
|
||||
import {
|
||||
modifyPermission,
|
||||
@@ -14,9 +13,11 @@ import {
|
||||
deletePermissionReset
|
||||
} from "../modules/permissions";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type { History } from "history";
|
||||
import ErrorNotification from "../../components/ErrorNotification";
|
||||
import {
|
||||
ErrorNotification,
|
||||
Checkbox
|
||||
} from "@scm-manager/ui-components";
|
||||
import DeletePermissionButton from "../components/buttons/DeletePermissionButton";
|
||||
import TypeSelector from "../components/TypeSelector";
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// @flow
|
||||
import { apiClient } from "../../apiclient";
|
||||
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import * as types from "../../modules/types";
|
||||
import type { Action } from "../../types/Action";
|
||||
import type {
|
||||
Action
|
||||
} from "@scm-manager/ui-types";
|
||||
import type {
|
||||
PermissionCollection,
|
||||
Permission,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { confirmAlert } from "../../components/modals/ConfirmAlert";
|
||||
import { NavAction } from "../../components/navigation";
|
||||
import type { Repository } from "../types/Repositories";
|
||||
import { NavAction, confirmAlert } from "@scm-manager/ui-components";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
|
||||
@@ -4,8 +4,11 @@ import "../../tests/enzyme";
|
||||
import "../../tests/i18n";
|
||||
import DeleteNavAction from "./DeleteNavAction";
|
||||
|
||||
import { confirmAlert } from "../../components/modals/ConfirmAlert";
|
||||
jest.mock("../../components/modals/ConfirmAlert");
|
||||
import { confirmAlert } from "@scm-manager/ui-components";
|
||||
jest.mock("@scm-manager/ui-components", () => ({
|
||||
confirmAlert: jest.fn(),
|
||||
NavAction: require.requireActual("@scm-manager/ui-components").NavAction
|
||||
}));
|
||||
|
||||
describe("DeleteNavAction", () => {
|
||||
it("should render nothing, if the delete link is missing", () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { NavLink } from "../../components/navigation";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Repository } from "../types/Repositories";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = { editUrl: string, t: string => string, repository: Repository };
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import { shallow, mount } from "enzyme";
|
||||
import "../../tests/enzyme";
|
||||
import "../../tests/i18n";
|
||||
import ReactRouterEnzymeContext from "react-router-enzyme-context";
|
||||
import EditNavLink from "./EditNavLink";
|
||||
|
||||
jest.mock("../../components/modals/ConfirmAlert");
|
||||
jest.mock("../../components/navigation/NavLink", () => () => <div>foo</div>);
|
||||
|
||||
describe("EditNavLink", () => {
|
||||
const options = new ReactRouterEnzymeContext();
|
||||
|
||||
it("should render nothing, if the modify link is missing", () => {
|
||||
const repository = {
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const navLink = shallow(<EditNavLink repository={repository} editUrl="" />);
|
||||
const navLink = shallow(
|
||||
<EditNavLink repository={repository} editUrl="" />,
|
||||
options.get()
|
||||
);
|
||||
expect(navLink.text()).toBe("");
|
||||
});
|
||||
|
||||
@@ -26,7 +29,10 @@ describe("EditNavLink", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const navLink = mount(<EditNavLink repository={repository} editUrl="" />);
|
||||
expect(navLink.text()).toBe("foo");
|
||||
const navLink = mount(
|
||||
<EditNavLink repository={repository} editUrl="" />,
|
||||
options.get()
|
||||
);
|
||||
expect(navLink.text()).toBe("edit-nav-link.label");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { NavLink } from "../../components/navigation";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Repository } from "../types/Repositories";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = { permissionUrl: string, t: string => string, repository: Repository };
|
||||
type Props = {
|
||||
permissionUrl: string,
|
||||
t: string => string,
|
||||
repository: Repository
|
||||
};
|
||||
|
||||
class PermissionsNavLink extends React.Component<Props> {
|
||||
hasPermissionsLink = () => {
|
||||
@@ -15,8 +19,9 @@ class PermissionsNavLink extends React.Component<Props> {
|
||||
return null;
|
||||
}
|
||||
const { permissionUrl, t } = this.props;
|
||||
return <NavLink to={permissionUrl} label={t("repository-root.permissions")}
|
||||
/>;
|
||||
return (
|
||||
<NavLink to={permissionUrl} label={t("repository-root.permissions")} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "../types/Repositories";
|
||||
import MailLink from "../../components/MailLink";
|
||||
import DateFromNow from "../../components/DateFromNow";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { MailLink, DateFromNow } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "../types/Repositories";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import RepositoryDetailTable from "./RepositoryDetailTable";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { InputField, Select } from "../../../components/forms/index";
|
||||
import { SubmitButton } from "../../../components/buttons/index";
|
||||
import type { Repository } from "../../types/Repositories";
|
||||
import {
|
||||
InputField,
|
||||
Select,
|
||||
SubmitButton,
|
||||
Textarea
|
||||
} from "@scm-manager/ui-components";
|
||||
import type { Repository, RepositoryType } from "@scm-manager/ui-types";
|
||||
import * as validator from "./repositoryValidation";
|
||||
import type { RepositoryType } from "../../types/RepositoryTypes";
|
||||
import Textarea from "../../../components/forms/Textarea";
|
||||
|
||||
type Props = {
|
||||
submitForm: Repository => void,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// @flow
|
||||
import * as generalValidator from "../../../components/validation";
|
||||
import { validation } from "@scm-manager/ui-components";
|
||||
|
||||
export const isNameValid = (name: string) => {
|
||||
return generalValidator.isNameValid(name);
|
||||
return validation.isNameValid(name);
|
||||
};
|
||||
|
||||
export function isContactValid(mail: string) {
|
||||
return "" === mail || generalValidator.isMailValid(mail);
|
||||
return "" === mail || validation.isMailValid(mail);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { Repository } from "../../types/Repositories";
|
||||
import Image from "../../../components/Image";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { Image } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import injectSheet from "react-jss";
|
||||
import type { Repository } from "../../types/Repositories";
|
||||
import DateFromNow from "../../../components/DateFromNow";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { DateFromNow } from "@scm-manager/ui-components";
|
||||
import RepositoryEntryLink from "./RepositoryEntryLink";
|
||||
import classNames from "classnames";
|
||||
import RepositoryAvatar from "./RepositoryAvatar";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user