Merged in feature/ux (pull request #139)

Feature/ux
This commit is contained in:
Maren Süwer
2018-12-20 10:32:46 +00:00
35 changed files with 1863 additions and 11644 deletions

View File

@@ -12,6 +12,11 @@ const styles = {
}, },
minWidthOfLabel: { minWidthOfLabel: {
minWidth: "4.5rem" minWidth: "4.5rem"
},
wrapper: {
padding: "1rem 1.5rem 0.25rem 1.5rem",
border: "1px solid #eee",
borderRadius: "5px 5px 0 0"
} }
}; };
@@ -43,7 +48,13 @@ class BranchSelector extends React.Component<Props, State> {
if (branches) { if (branches) {
return ( return (
<div className="box field is-horizontal"> <div
className={classNames(
"has-background-light field",
"is-horizontal",
classes.wrapper
)}
>
<div <div
className={classNames( className={classNames(
"field-label", "field-label",

View File

@@ -1,14 +1,23 @@
//@flow //@flow
import React from "react"; import React from "react";
import injectSheet from "react-jss";
import classNames from "classnames"; import classNames from "classnames";
type Props = { type Props = {
classes: any
};
const styles = {
textinfo: {
color: "#98d8f3 !important"
}
}; };
class HelpIcon extends React.Component<Props> { class HelpIcon extends React.Component<Props> {
render() { render() {
return <i className={classNames("fa fa-question has-text-info")} /> const { classes } = this.props;
return <i className={classNames("fa fa-question-circle has-text-info", classes.textinfo)}></i>;
} }
} }
export default HelpIcon; export default injectSheet(styles)(HelpIcon);

View File

@@ -1,11 +1,11 @@
//@flow //@flow
import React from "react"; import React from "react";
import Button, { type ButtonProps } from "./Button"; import Button, { type ButtonProps } from "./Button";
class AddButton extends React.Component<ButtonProps> { class AddButton extends React.Component<ButtonProps> {
render() { render() {
return <Button color="default" {...this.props} />; return <Button color="default" {...this.props} />;
} }
} }
export default AddButton; export default AddButton;

View File

@@ -1,24 +1,27 @@
//@flow //@flow
import React from "react"; import React from "react";
import injectSheet from "react-jss"; import injectSheet from "react-jss";
import AddButton, { type ButtonProps } from "./Button"; import SubmitButton, { type ButtonProps } from "./SubmitButton";
import classNames from "classnames"; import classNames from "classnames";
const styles = { const styles = {
spacing: { spacing: {
margin: "1em 0 0 1em" marginTop: "2em",
} border: "2px solid #e9f7fd",
}; padding: "1em 1em"
}
class CreateButton extends React.Component<ButtonProps> {
render() { };
const { classes } = this.props;
return ( class CreateButton extends React.Component<ButtonProps> {
<div className={classNames("is-pulled-right", classes.spacing)}> render() {
<AddButton {...this.props} /> const { classes } = this.props;
</div> return (
); <div className={classNames("has-text-centered", classes.spacing)}>
} <SubmitButton {...this.props} />
} </div>
);
export default injectSheet(styles)(CreateButton); }
}
export default injectSheet(styles)(CreateButton);

View File

@@ -1,31 +1,31 @@
//@flow //@flow
import * as React from "react"; import * as React from "react";
import Logo from "./../Logo"; import Logo from "./../Logo";
type Props = { type Props = {
children?: React.Node children?: React.Node
}; };
class Header extends React.Component<Props> { class Header extends React.Component<Props> {
render() { render() {
const { children } = this.props; const { children } = this.props;
return ( return (
<section className="hero is-dark is-small"> <section className="hero is-dark is-small">
<div className="hero-body"> <div className="hero-body">
<div className="container"> <div className="container">
<div className="columns is-vcentered"> <div className="columns is-vcentered">
<div className="column"> <div className="column">
<Logo /> <Logo />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="hero-foot"> <div className="hero-foot">
<div className="container">{children}</div> <div className="container">{children}</div>
</div> </div>
</section> </section>
); );
} }
} }
export default Header; export default Header;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@@ -20,34 +20,41 @@ class AdminSettings extends React.Component<Props> {
return ( return (
<div> <div>
<Subtitle subtitle={t("admin-settings.name")} /> <Subtitle subtitle={t("admin-settings.name")} />
<AdminGroupTable <div className="columns">
adminGroups={adminGroups} <div className="column is-half">
onChange={(isValid, changedValue, name) => <AdminGroupTable
this.props.onChange(isValid, changedValue, name) adminGroups={adminGroups}
} onChange={(isValid, changedValue, name) =>
disabled={!hasUpdatePermission} this.props.onChange(isValid, changedValue, name)
/> }
<AddEntryToTableField disabled={!hasUpdatePermission}
addEntry={this.addGroup} />
disabled={!hasUpdatePermission}
buttonLabel={t("admin-settings.add-group-button")} <AddEntryToTableField
fieldLabel={t("admin-settings.add-group-textfield")} addEntry={this.addGroup}
errorMessage={t("admin-settings.add-group-error")} disabled={!hasUpdatePermission}
/> buttonLabel={t("admin-settings.add-group-button")}
<AdminUserTable fieldLabel={t("admin-settings.add-group-textfield")}
adminUsers={adminUsers} errorMessage={t("admin-settings.add-group-error")}
onChange={(isValid, changedValue, name) => />
this.props.onChange(isValid, changedValue, name) </div>
} <div className="column is-half">
disabled={!hasUpdatePermission} <AdminUserTable
/> adminUsers={adminUsers}
<AddEntryToTableField onChange={(isValid, changedValue, name) =>
addEntry={this.addUser} this.props.onChange(isValid, changedValue, name)
disabled={!hasUpdatePermission} }
buttonLabel={t("admin-settings.add-user-button")} disabled={!hasUpdatePermission}
fieldLabel={t("admin-settings.add-user-textfield")} />
errorMessage={t("admin-settings.add-user-error")} <AddEntryToTableField
/> addEntry={this.addUser}
disabled={!hasUpdatePermission}
buttonLabel={t("admin-settings.add-user-button")}
fieldLabel={t("admin-settings.add-user-textfield")}
errorMessage={t("admin-settings.add-user-error")}
/>
</div>
</div>
</div> </div>
); );
} }

View File

@@ -18,20 +18,26 @@ class BaseUrlSettings extends React.Component<Props> {
return ( return (
<div> <div>
<Subtitle subtitle={t("base-url-settings.name")} /> <Subtitle subtitle={t("base-url-settings.name")} />
<Checkbox <div className="columns">
checked={forceBaseUrl} <div className="column is-half">
label={t("base-url-settings.force-base-url")} <Checkbox
onChange={this.handleForceBaseUrlChange} checked={forceBaseUrl}
disabled={!hasUpdatePermission} label={t("base-url-settings.force-base-url")}
helpText={t("help.forceBaseUrlHelpText")} onChange={this.handleForceBaseUrlChange}
/> disabled={!hasUpdatePermission}
<InputField helpText={t("help.forceBaseUrlHelpText")}
label={t("base-url-settings.base-url")} />
onChange={this.handleBaseUrlChange} </div>
value={baseUrl} <div className="column is-half">
disabled={!hasUpdatePermission} <InputField
helpText={t("help.baseUrlHelpText")} label={t("base-url-settings.base-url")}
/> onChange={this.handleBaseUrlChange}
value={baseUrl}
disabled={!hasUpdatePermission}
helpText={t("help.baseUrlHelpText")}
/>
</div>
</div>
</div> </div>
); );
} }

View File

@@ -36,70 +36,98 @@ class GeneralSettings extends React.Component<Props> {
return ( return (
<div> <div>
<InputField <div className="columns">
label={t("general-settings.realm-description")} <div className="column is-half">
onChange={this.handleRealmDescriptionChange} <InputField
value={realmDescription} label={t("general-settings.realm-description")}
disabled={!hasUpdatePermission} onChange={this.handleRealmDescriptionChange}
helpText={t("help.realmDescriptionHelpText")} value={realmDescription}
/> disabled={!hasUpdatePermission}
<InputField helpText={t("help.realmDescriptionHelpText")}
label={t("general-settings.date-format")} />
onChange={this.handleDateFormatChange} </div>
value={dateFormat} <div className="column is-half">
disabled={!hasUpdatePermission} <InputField
helpText={t("help.dateFormatHelpText")} label={t("general-settings.date-format")}
/> onChange={this.handleDateFormatChange}
<InputField value={dateFormat}
label={t("general-settings.plugin-url")} disabled={!hasUpdatePermission}
onChange={this.handlePluginUrlChange} helpText={t("help.dateFormatHelpText")}
value={pluginUrl} />
disabled={!hasUpdatePermission} </div>
helpText={t("help.pluginRepositoryHelpText")} </div>
/> <div className="columns">
<InputField <div className="column is-half">
label={t("general-settings.default-namespace-strategy")} <InputField
onChange={this.handleDefaultNamespaceStrategyChange} label={t("general-settings.plugin-url")}
value={defaultNamespaceStrategy} onChange={this.handlePluginUrlChange}
disabled={!hasUpdatePermission} value={pluginUrl}
helpText={t("help.defaultNameSpaceStrategyHelpText")} disabled={!hasUpdatePermission}
/> helpText={t("help.pluginRepositoryHelpText")}
<Checkbox />
checked={enabledXsrfProtection} </div>
label={t("general-settings.enabled-xsrf-protection")} <div className="column is-half">
onChange={this.handleEnabledXsrfProtectionChange} <InputField
disabled={!hasUpdatePermission} label={t("general-settings.default-namespace-strategy")}
helpText={t("help.enableXsrfProtectionHelpText")} onChange={this.handleDefaultNamespaceStrategyChange}
/> value={defaultNamespaceStrategy}
<Checkbox disabled={!hasUpdatePermission}
checked={enableRepositoryArchive} helpText={t("help.defaultNameSpaceStrategyHelpText")}
label={t("general-settings.enable-repository-archive")} />
onChange={this.handleEnableRepositoryArchiveChange} </div>
disabled={!hasUpdatePermission} </div>
helpText={t("help.enableRepositoryArchiveHelpText")} <div className="columns">
/> <div className="column is-half">
<Checkbox <Checkbox
checked={disableGroupingGrid} checked={enabledXsrfProtection}
label={t("general-settings.disable-grouping-grid")} label={t("general-settings.enabled-xsrf-protection")}
onChange={this.handleDisableGroupingGridChange} onChange={this.handleEnabledXsrfProtectionChange}
disabled={!hasUpdatePermission} disabled={!hasUpdatePermission}
helpText={t("help.disableGroupingGridHelpText")} helpText={t("help.enableXsrfProtectionHelpText")}
/> />
<Checkbox </div>
checked={anonymousAccessEnabled} <div className="column is-half">
label={t("general-settings.anonymous-access-enabled")} <Checkbox
onChange={this.handleAnonymousAccessEnabledChange} checked={enableRepositoryArchive}
disabled={!hasUpdatePermission} label={t("general-settings.enable-repository-archive")}
helpText={t("help.allowAnonymousAccessHelpText")} onChange={this.handleEnableRepositoryArchiveChange}
/> disabled={!hasUpdatePermission}
<Checkbox helpText={t("help.enableRepositoryArchiveHelpText")}
checked={skipFailedAuthenticators} />
label={t("general-settings.skip-failed-authenticators")} </div>
onChange={this.handleSkipFailedAuthenticatorsChange} </div>
disabled={!hasUpdatePermission} <div className="columns">
helpText={t("help.skipFailedAuthenticatorsHelpText")} <div className="column is-half">
/> <Checkbox
</div> checked={disableGroupingGrid}
label={t("general-settings.disable-grouping-grid")}
onChange={this.handleDisableGroupingGridChange}
disabled={!hasUpdatePermission}
helpText={t("help.disableGroupingGridHelpText")}
/>
</div>
<div className="column is-half">
<Checkbox
checked={anonymousAccessEnabled}
label={t("general-settings.anonymous-access-enabled")}
onChange={this.handleAnonymousAccessEnabledChange}
disabled={!hasUpdatePermission}
helpText={t("help.allowAnonymousAccessHelpText")}
/>
</div>
</div>
<div className="columns">
<div className="column is-half">
<Checkbox
checked={skipFailedAuthenticators}
label={t("general-settings.skip-failed-authenticators")}
onChange={this.handleSkipFailedAuthenticatorsChange}
disabled={!hasUpdatePermission}
helpText={t("help.skipFailedAuthenticatorsHelpText")}
/>
</div>
</div>
</div>
); );
} }

View File

@@ -40,24 +40,30 @@ class LoginAttempt extends React.Component<Props, State> {
return ( return (
<div> <div>
<Subtitle subtitle={t("login-attempt.name")} /> <Subtitle subtitle={t("login-attempt.name")} />
<InputField <div className="columns">
label={t("login-attempt.login-attempt-limit")} <div className="column is-half">
onChange={this.handleLoginAttemptLimitChange} <InputField
value={loginAttemptLimit} label={t("login-attempt.login-attempt-limit")}
disabled={!hasUpdatePermission} onChange={this.handleLoginAttemptLimitChange}
validationError={this.state.loginAttemptLimitError} value={loginAttemptLimit}
errorMessage={t("validation.login-attempt-limit-invalid")} disabled={!hasUpdatePermission}
helpText={t("help.loginAttemptLimitHelpText")} validationError={this.state.loginAttemptLimitError}
/> errorMessage={t("validation.login-attempt-limit-invalid")}
<InputField helpText={t("help.loginAttemptLimitHelpText")}
label={t("login-attempt.login-attempt-limit-timeout")} />
onChange={this.handleLoginAttemptLimitTimeoutChange} </div>
value={loginAttemptLimitTimeout} <div className="column is-half">
disabled={!hasUpdatePermission} <InputField
validationError={this.state.loginAttemptLimitTimeoutError} label={t("login-attempt.login-attempt-limit-timeout")}
errorMessage={t("validation.login-attempt-limit-timeout-invalid")} onChange={this.handleLoginAttemptLimitTimeoutChange}
helpText={t("help.loginAttemptLimitTimeoutHelpText")} value={loginAttemptLimitTimeout}
/> disabled={!hasUpdatePermission}
validationError={this.state.loginAttemptLimitTimeoutError}
errorMessage={t("validation.login-attempt-limit-timeout-invalid")}
helpText={t("help.loginAttemptLimitTimeoutHelpText")}
/>
</div>
</div>
</div> </div>
); );
} }

View File

@@ -37,56 +37,76 @@ class ProxySettings extends React.Component<Props> {
return ( return (
<div> <div>
<Subtitle subtitle={t("proxy-settings.name")} /> <Subtitle subtitle={t("proxy-settings.name")} />
<Checkbox <div className="columns">
checked={enableProxy} <div className="column is-full">
label={t("proxy-settings.enable-proxy")} <Checkbox
onChange={this.handleEnableProxyChange} checked={enableProxy}
disabled={!hasUpdatePermission} label={t("proxy-settings.enable-proxy")}
helpText={t("help.enableProxyHelpText")} onChange={this.handleEnableProxyChange}
/> disabled={!hasUpdatePermission}
<InputField helpText={t("help.enableProxyHelpText")}
label={t("proxy-settings.proxy-password")} />
onChange={this.handleProxyPasswordChange} </div>
value={proxyPassword} </div>
type="password" <div className="columns">
disabled={!enableProxy || !hasUpdatePermission} <div className="column is-half">
helpText={t("help.proxyPasswordHelpText")} <InputField
/> label={t("proxy-settings.proxy-password")}
<InputField onChange={this.handleProxyPasswordChange}
label={t("proxy-settings.proxy-port")} value={proxyPassword}
value={proxyPort} type="password"
onChange={this.handleProxyPortChange} disabled={!enableProxy || !hasUpdatePermission}
disabled={!enableProxy || !hasUpdatePermission} helpText={t("help.proxyPasswordHelpText")}
helpText={t("help.proxyPortHelpText")} />
/> </div>
<InputField <div className="column is-half">
label={t("proxy-settings.proxy-server")} <InputField
value={proxyServer} label={t("proxy-settings.proxy-port")}
onChange={this.handleProxyServerChange} value={proxyPort}
disabled={!enableProxy || !hasUpdatePermission} onChange={this.handleProxyPortChange}
helpText={t("help.proxyServerHelpText")} disabled={!enableProxy || !hasUpdatePermission}
/> helpText={t("help.proxyPortHelpText")}
<InputField />
label={t("proxy-settings.proxy-user")} </div>
value={proxyUser} </div>
onChange={this.handleProxyUserChange} <div className="columns">
disabled={!enableProxy || !hasUpdatePermission} <div className="column is-half">
helpText={t("help.proxyUserHelpText")} <InputField
/> label={t("proxy-settings.proxy-server")}
<ProxyExcludesTable value={proxyServer}
proxyExcludes={proxyExcludes} onChange={this.handleProxyServerChange}
onChange={(isValid, changedValue, name) => disabled={!enableProxy || !hasUpdatePermission}
this.props.onChange(isValid, changedValue, name) helpText={t("help.proxyServerHelpText")}
} />
disabled={!enableProxy || !hasUpdatePermission} </div>
/> <div className="column is-half">
<AddEntryToTableField <InputField
addEntry={this.addProxyExclude} label={t("proxy-settings.proxy-user")}
disabled={!enableProxy || !hasUpdatePermission} value={proxyUser}
buttonLabel={t("proxy-settings.add-proxy-exclude-button")} onChange={this.handleProxyUserChange}
fieldLabel={t("proxy-settings.add-proxy-exclude-textfield")} disabled={!enableProxy || !hasUpdatePermission}
errorMessage={t("proxy-settings.add-proxy-exclude-error")} helpText={t("help.proxyUserHelpText")}
/> />
</div>
</div>
<div className="columns">
<div className="column is-full">
<ProxyExcludesTable
proxyExcludes={proxyExcludes}
onChange={(isValid, changedValue, name) =>
this.props.onChange(isValid, changedValue, name)
}
disabled={!enableProxy || !hasUpdatePermission}
/>
<AddEntryToTableField
addEntry={this.addProxyExclude}
disabled={!enableProxy || !hasUpdatePermission}
buttonLabel={t("proxy-settings.add-proxy-exclude-button")}
fieldLabel={t("proxy-settings.add-proxy-exclude-textfield")}
errorMessage={t("proxy-settings.add-proxy-exclude-error")}
/>
</div>
</div>
</div> </div>
); );
} }

View File

@@ -1,86 +1,86 @@
// @flow // @flow
import React from "react"; import React from "react";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { Route } from "react-router"; import { Route } from "react-router";
import { ExtensionPoint } from "@scm-manager/ui-extensions"; import { ExtensionPoint } from "@scm-manager/ui-extensions";
import type { Links } from "@scm-manager/ui-types"; import type { Links } from "@scm-manager/ui-types";
import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components"; import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components";
import GlobalConfig from "./GlobalConfig"; import GlobalConfig from "./GlobalConfig";
import type { History } from "history"; import type { History } from "history";
import {connect} from "react-redux"; import {connect} from "react-redux";
import {compose} from "redux"; import {compose} from "redux";
import { getLinks } from "../../modules/indexResource"; import { getLinks } from "../../modules/indexResource";
type Props = { type Props = {
links: Links, links: Links,
// context objects // context objects
t: string => string, t: string => string,
match: any, match: any,
history: History history: History
}; };
class Config extends React.Component<Props> { class Config extends React.Component<Props> {
stripEndingSlash = (url: string) => { stripEndingSlash = (url: string) => {
if (url.endsWith("/")) { if (url.endsWith("/")) {
return url.substring(0, url.length - 2); return url.substring(0, url.length - 2);
} }
return url; return url;
}; };
matchedUrl = () => { matchedUrl = () => {
return this.stripEndingSlash(this.props.match.url); return this.stripEndingSlash(this.props.match.url);
}; };
render() { render() {
const { links, t } = this.props; const { links, t } = this.props;
const url = this.matchedUrl(); const url = this.matchedUrl();
const extensionProps = { const extensionProps = {
links, links,
url url
}; };
return ( return (
<Page> <Page>
<div className="columns"> <div className="columns">
<div className="column is-three-quarters"> <div className="column is-three-quarters">
<Route path={url} exact component={GlobalConfig} /> <Route path={url} exact component={GlobalConfig} />
<ExtensionPoint name="config.route" <ExtensionPoint name="config.route"
props={extensionProps} props={extensionProps}
renderAll={true} renderAll={true}
/> />
</div> </div>
<div className="column"> <div className="column is-one-quarter">
<Navigation> <Navigation>
<Section label={t("config.navigation-title")}> <Section label={t("config.navigation-title")}>
<NavLink <NavLink
to={`${url}`} to={`${url}`}
label={t("global-config.navigation-label")} label={t("global-config.navigation-label")}
/> />
<ExtensionPoint name="config.navigation" <ExtensionPoint name="config.navigation"
props={extensionProps} props={extensionProps}
renderAll={true} renderAll={true}
/> />
</Section> </Section>
</Navigation> </Navigation>
</div> </div>
</div> </div>
</Page> </Page>
); );
} }
} }
const mapStateToProps = (state: any) => { const mapStateToProps = (state: any) => {
const links = getLinks(state); const links = getLinks(state);
return { return {
links links
}; };
}; };
export default compose( export default compose(
connect(mapStateToProps), connect(mapStateToProps),
translate("config") translate("config")
)(Config); )(Config);

View File

@@ -1,53 +1,53 @@
// @flow // @flow
import React from "react"; import React from "react";
import type { Me } from "@scm-manager/ui-types"; import type { Me } from "@scm-manager/ui-types";
import { MailLink, AvatarWrapper, AvatarImage } from "@scm-manager/ui-components"; import { MailLink, AvatarWrapper, AvatarImage } from "@scm-manager/ui-components";
import { compose } from "redux"; import { compose } from "redux";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
type Props = { type Props = {
me: Me, me: Me,
// Context props // Context props
t: string => string t: string => string
}; };
type State = {}; type State = {};
class ProfileInfo extends React.Component<Props, State> { class ProfileInfo extends React.Component<Props, State> {
render() { render() {
const { me, t } = this.props; const { me, t } = this.props;
return ( return (
<div className="media"> <div className="media">
<AvatarWrapper> <AvatarWrapper>
<figure className="media-left"> <figure className="media-left">
<p className="image is-64x64"> <p className="image is-64x64">
<AvatarImage person={ me }/> <AvatarImage person={ me }/>
</p> </p>
</figure> </figure>
</AvatarWrapper> </AvatarWrapper>
<div className="media-content"> <div className="media-content">
<table className="table"> <table className="table">
<tbody> <tbody>
<tr> <tr>
<td>{t("profile.username")}</td> <td className="has-text-weight-semibold">{t("profile.username")}</td>
<td>{me.name}</td> <td>{me.name}</td>
</tr> </tr>
<tr> <tr>
<td>{t("profile.displayName")}</td> <td className="has-text-weight-semibold">{t("profile.displayName")}</td>
<td>{me.displayName}</td> <td>{me.displayName}</td>
</tr> </tr>
<tr> <tr>
<td>{t("profile.mail")}</td> <td className="has-text-weight-semibold">{t("profile.mail")}</td>
<td> <td>
<MailLink address={me.mail} /> <MailLink address={me.mail} />
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
); );
} }
} }
export default compose(translate("commons"))(ProfileInfo); export default compose(translate("commons"))(ProfileInfo);

View File

@@ -1,69 +1,69 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { Group } from "@scm-manager/ui-types"; import type { Group } from "@scm-manager/ui-types";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import GroupMember from "./GroupMember"; import GroupMember from "./GroupMember";
import { DateFromNow } from "@scm-manager/ui-components"; import { DateFromNow } from "@scm-manager/ui-components";
type Props = { type Props = {
group: Group, group: Group,
t: string => string t: string => string
}; };
class Details extends React.Component<Props> { class Details extends React.Component<Props> {
render() { render() {
const { group, t } = this.props; const { group, t } = this.props;
return ( return (
<table className="table content"> <table className="table content">
<tbody> <tbody>
<tr> <tr>
<td>{t("group.name")}</td> <td className="has-text-weight-semibold">{t("group.name")}</td>
<td>{group.name}</td> <td>{group.name}</td>
</tr> </tr>
<tr> <tr>
<td>{t("group.description")}</td> <td className="has-text-weight-semibold">{t("group.description")}</td>
<td>{group.description}</td> <td>{group.description}</td>
</tr> </tr>
<tr> <tr>
<td>{t("group.type")}</td> <td className="has-text-weight-semibold">{t("group.type")}</td>
<td>{group.type}</td> <td>{group.type}</td>
</tr> </tr>
<tr> <tr>
<td>{t("group.creationDate")}</td> <td className="has-text-weight-semibold">{t("group.creationDate")}</td>
<td> <td>
<DateFromNow date={group.creationDate} /> <DateFromNow date={group.creationDate} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{t("group.lastModified")}</td> <td className="has-text-weight-semibold">{t("group.lastModified")}</td>
<td> <td>
<DateFromNow date={group.lastModified} /> <DateFromNow date={group.lastModified} />
</td> </td>
</tr> </tr>
{this.renderMembers()} {this.renderMembers()}
</tbody> </tbody>
</table> </table>
); );
} }
renderMembers() { renderMembers() {
if (this.props.group.members.length > 0) { if (this.props.group.members.length > 0) {
return ( return (
<tr> <tr>
<td> <td>
{this.props.t("group.members")} {this.props.t("group.members")}
<ul> <ul>
{this.props.group._embedded.members.map((member, index) => { {this.props.group._embedded.members.map((member, index) => {
return <GroupMember key={index} member={member} />; return <GroupMember key={index} member={member} />;
})} })}
</ul> </ul>
</td> </td>
</tr> </tr>
); );
} else { } else {
return; return;
} }
} }
} }
export default translate("groups")(Details); export default translate("groups")(Details);

View File

@@ -1,25 +1,25 @@
// @flow // @flow
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import type { Group } from "@scm-manager/ui-types"; import type { Group } from "@scm-manager/ui-types";
type Props = { type Props = {
group: Group group: Group
}; };
export default class GroupRow extends React.Component<Props> { export default class GroupRow extends React.Component<Props> {
renderLink(to: string, label: string) { renderLink(to: string, label: string) {
return <Link to={to}>{label}</Link>; return <Link to={to}>{label}</Link>;
} }
render() { render() {
const { group } = this.props; const { group } = this.props;
const to = `/group/${group.name}`; const to = `/group/${group.name}`;
return ( return (
<tr> <tr>
<td>{this.renderLink(to, group.name)}</td> <td>{this.renderLink(to, group.name)}</td>
<td className="is-hidden-mobile">{group.description}</td> <td className="is-hidden-mobile">{group.description}</td>
</tr> </tr>
); );
} }
} }

View File

@@ -1,33 +1,33 @@
// @flow // @flow
import React from "react"; import React from "react";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import GroupRow from "./GroupRow"; import GroupRow from "./GroupRow";
import type { Group } from "@scm-manager/ui-types"; import type { Group } from "@scm-manager/ui-types";
type Props = { type Props = {
t: string => string, t: string => string,
groups: Group[] groups: Group[]
}; };
class GroupTable extends React.Component<Props> { class GroupTable extends React.Component<Props> {
render() { render() {
const { groups, t } = this.props; const { groups, t } = this.props;
return ( return (
<table className="table is-hoverable is-fullwidth"> <table className="card-table table is-hoverable is-fullwidth">
<thead> <thead>
<tr> <tr>
<th>{t("group.name")}</th> <th>{t("group.name")}</th>
<th className="is-hidden-mobile">{t("group.description")}</th> <th className="is-hidden-mobile">{t("group.description")}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{groups.map((group, index) => { {groups.map((group, index) => {
return <GroupRow key={index} group={group} />; return <GroupRow key={index} group={group} />;
})} })}
</tbody> </tbody>
</table> </table>
); );
} }
} }
export default translate("groups")(GroupTable); export default translate("groups")(GroupTable);

View File

@@ -1,55 +1,55 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { Repository } from "@scm-manager/ui-types"; import type { Repository } from "@scm-manager/ui-types";
import { MailLink, DateFromNow } from "@scm-manager/ui-components"; import { MailLink, DateFromNow } from "@scm-manager/ui-components";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
type Props = { type Props = {
repository: Repository, repository: Repository,
// context props // context props
t: string => string t: string => string
}; };
class RepositoryDetailTable extends React.Component<Props> { class RepositoryDetailTable extends React.Component<Props> {
render() { render() {
const { repository, t } = this.props; const { repository, t } = this.props;
return ( return (
<table className="table"> <table className="table">
<tbody> <tbody>
<tr> <tr>
<td>{t("repository.name")}</td> <td className="has-text-weight-semibold">{t("repository.name")}</td>
<td>{repository.name}</td> <td>{repository.name}</td>
</tr> </tr>
<tr> <tr>
<td>{t("repository.type")}</td> <td className="has-text-weight-semibold">{t("repository.type")}</td>
<td>{repository.type}</td> <td>{repository.type}</td>
</tr> </tr>
<tr> <tr>
<td>{t("repository.contact")}</td> <td className="has-text-weight-semibold">{t("repository.contact")}</td>
<td> <td>
<MailLink address={repository.contact} /> <MailLink address={repository.contact} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{t("repository.description")}</td> <td className="has-text-weight-semibold">{t("repository.description")}</td>
<td>{repository.description}</td> <td>{repository.description}</td>
</tr> </tr>
<tr> <tr>
<td>{t("repository.creationDate")}</td> <td className="has-text-weight-semibold">{t("repository.creationDate")}</td>
<td> <td>
<DateFromNow date={repository.creationDate} /> <DateFromNow date={repository.creationDate} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{t("repository.lastModified")}</td> <td className="has-text-weight-semibold">{t("repository.lastModified")}</td>
<td> <td>
<DateFromNow date={repository.lastModified} /> <DateFromNow date={repository.lastModified} />
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
); );
} }
} }
export default translate("repos")(RepositoryDetailTable); export default translate("repos")(RepositoryDetailTable);

View File

@@ -1,29 +1,30 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { Repository } from "@scm-manager/ui-types"; import type { Repository } from "@scm-manager/ui-types";
import RepositoryDetailTable from "./RepositoryDetailTable"; import RepositoryDetailTable from "./RepositoryDetailTable";
import { ExtensionPoint } from "@scm-manager/ui-extensions"; import { ExtensionPoint } from "@scm-manager/ui-extensions";
type Props = { type Props = {
repository: Repository repository: Repository
}; };
class RepositoryDetails extends React.Component<Props> { class RepositoryDetails extends React.Component<Props> {
render() { render() {
const { repository } = this.props; const { repository } = this.props;
return ( return (
<div> <div>
<RepositoryDetailTable repository={repository} /> <RepositoryDetailTable repository={repository} />
<div className="content"> <hr />
<ExtensionPoint <div className="content">
name="repos.repository-details.information" <ExtensionPoint
renderAll={true} name="repos.repository-details.information"
props={{ repository }} renderAll={true}
/> props={{ repository }}
</div> />
</div> </div>
); </div>
} );
} }
}
export default RepositoryDetails;
export default RepositoryDetails;

View File

@@ -9,15 +9,10 @@ import classNames from "classnames";
import RepositoryAvatar from "./RepositoryAvatar"; import RepositoryAvatar from "./RepositoryAvatar";
const styles = { const styles = {
outer: {
position: "relative"
},
overlay: { overlay: {
position: "absolute", position: "absolute",
left: 0, height: "calc(120px - 1.5rem)",
top: 0, width: "calc(50% - 3rem)"
bottom: 0,
right: 0
}, },
inner: { inner: {
position: "relative", position: "relative",
@@ -26,11 +21,16 @@ const styles = {
}, },
innerLink: { innerLink: {
pointerEvents: "all" pointerEvents: "all"
},
centerImage: {
marginTop: "0.8em",
marginLeft: "1em !important"
} }
}; };
type Props = { type Props = {
repository: Repository, repository: Repository,
fullColumnWidth?: boolean,
// context props // context props
classes: any classes: any
}; };
@@ -44,7 +44,7 @@ class RepositoryEntry extends React.Component<Props> {
if (repository._links["changesets"]) { if (repository._links["changesets"]) {
return ( return (
<RepositoryEntryLink <RepositoryEntryLink
iconClass="fa-code-branch" iconClass="fa-code-branch fa-lg"
to={repositoryLink + "/changesets"} to={repositoryLink + "/changesets"}
/> />
); );
@@ -56,7 +56,7 @@ class RepositoryEntry extends React.Component<Props> {
if (repository._links["sources"]) { if (repository._links["sources"]) {
return ( return (
<RepositoryEntryLink <RepositoryEntryLink
iconClass="fa-code" iconClass="fa-code fa-lg"
to={repositoryLink + "/sources"} to={repositoryLink + "/sources"}
/> />
); );
@@ -67,29 +67,40 @@ class RepositoryEntry extends React.Component<Props> {
renderModifyLink = (repository: Repository, repositoryLink: string) => { renderModifyLink = (repository: Repository, repositoryLink: string) => {
if (repository._links["update"]) { if (repository._links["update"]) {
return ( return (
<RepositoryEntryLink iconClass="fa-cog" to={repositoryLink + "/edit"} /> <RepositoryEntryLink
iconClass="fa-cog fa-lg"
to={repositoryLink + "/edit"}
/>
); );
} }
return null; return null;
}; };
render() { render() {
const { repository, classes } = this.props; const { repository, classes, fullColumnWidth } = this.props;
const repositoryLink = this.createLink(repository); const repositoryLink = this.createLink(repository);
const halfColumn = fullColumnWidth ? "is-full" : "is-half";
return ( return (
<div className={classNames("box", "box-link-shadow", classes.outer)}> <div
<Link className={classes.overlay} to={repositoryLink} /> className={classNames(
"box",
"box-link-shadow",
"column",
"is-clipped",
halfColumn
)}
>
<Link className={classNames(classes.overlay)} to={repositoryLink} />
<article className={classNames("media", classes.inner)}> <article className={classNames("media", classes.inner)}>
<figure className="media-left"> <figure className={classNames(classes.centerImage, "media-left")}>
<RepositoryAvatar repository={repository} /> <RepositoryAvatar repository={repository} />
</figure> </figure>
<div className="media-content"> <div className="media-content">
<div className="content"> <div className="content">
<p> <p className="is-marginless">
<strong>{repository.name}</strong> <strong>{repository.name}</strong>
<br />
{repository.description}
</p> </p>
<p className={"shorten-text"}>{repository.description}</p>
</div> </div>
<nav className="level is-mobile"> <nav className="level is-mobile">
<div className="level-left"> <div className="level-left">

View File

@@ -1,34 +1,35 @@
//@flow //@flow
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import injectSheet from "react-jss"; import injectSheet from "react-jss";
import classNames from "classnames"; import classNames from "classnames";
const styles = { const styles = {
link: { link: {
pointerEvents: "all" pointerEvents: "all",
} marginRight: "1.25rem !important"
}; }
};
type Props = {
to: string, type Props = {
iconClass: string, to: string,
iconClass: string,
// context props
classes: any // context props
}; classes: any
};
class RepositoryEntryLink extends React.Component<Props> {
render() { class RepositoryEntryLink extends React.Component<Props> {
const { to, iconClass, classes } = this.props; render() {
return ( const { to, iconClass, classes } = this.props;
<Link className={classNames("level-item", classes.link)} to={to}> return (
<span className="icon is-small"> <Link className={classNames("level-item", classes.link)} to={to}>
<i className={classNames("fa", iconClass)} /> <span className="icon is-small">
</span> <i className={classNames("fa", iconClass)} />
</Link> </span>
); </Link>
} );
} }
}
export default injectSheet(styles)(RepositoryEntryLink);
export default injectSheet(styles)(RepositoryEntryLink);

View File

@@ -1,16 +1,23 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { RepositoryGroup } from "@scm-manager/ui-types"; import type { RepositoryGroup, Repository } from "@scm-manager/ui-types";
import injectSheet from "react-jss"; import injectSheet from "react-jss";
import classNames from "classnames"; import classNames from "classnames";
import RepositoryEntry from "./RepositoryEntry"; import RepositoryEntry from "./RepositoryEntry";
const styles = { const styles = {
pointer: { pointer: {
cursor: "pointer" cursor: "pointer",
fontSize: "1.5rem"
}, },
repoGroup: { repoGroup: {
marginBottom: "1em" marginBottom: "1em"
},
wrapper: {
padding: "0 0.75rem"
},
clearfix: {
clear: "both"
} }
}; };
@@ -39,6 +46,18 @@ class RepositoryGroupEntry extends React.Component<Props, State> {
})); }));
}; };
isLastEntry = (array: Repository[], index: number) => {
return index === array.length - 1;
};
isLengthOdd = (array: Repository[]) => {
return array.length % 2 !== 0;
};
isFullSize = (array: Repository[], index: number) => {
return this.isLastEntry(array, index) && this.isLengthOdd(array);
};
render() { render() {
const { group, classes } = this.props; const { group, classes } = this.props;
const { collapsed } = this.state; const { collapsed } = this.state;
@@ -47,7 +66,10 @@ class RepositoryGroupEntry extends React.Component<Props, State> {
let content = null; let content = null;
if (!collapsed) { if (!collapsed) {
content = group.repositories.map((repository, index) => { content = group.repositories.map((repository, index) => {
return <RepositoryEntry repository={repository} key={index} />; const fullColumnWidth = this.isFullSize(group.repositories, index);
return (
<RepositoryEntry repository={repository} fullColumnWidth={fullColumnWidth} key={index} />
);
}); });
} }
return ( return (
@@ -58,7 +80,10 @@ class RepositoryGroupEntry extends React.Component<Props, State> {
</span> </span>
</h2> </h2>
<hr /> <hr />
{content} <div className={classNames("columns", "is-multiline", classes.wrapper)}>
{content}
</div>
<div className={classes.clearfix} />
</div> </div>
); );
} }

View File

@@ -127,6 +127,7 @@ class CreatePermissionForm extends React.Component<Props, State> {
return ( return (
<div> <div>
<hr />
<h2 className="subtitle"> <h2 className="subtitle">
{t("permission.add-permission.add-permission-heading")} {t("permission.add-permission.add-permission-heading")}
</h2> </h2>
@@ -153,19 +154,29 @@ class CreatePermissionForm extends React.Component<Props, State> {
{t("permission.group-permission")} {t("permission.group-permission")}
</label> </label>
</div> </div>
{this.renderAutocompletionField()}
<TypeSelector <div className="columns">
label={t("permission.type")} <div className="column is-three-quarters">
helpText={t("permission.help.typeHelpText")} {this.renderAutocompletionField()}
handleTypeChange={this.handleTypeChange} </div>
type={type ? type : "READ"} <div className="column is-one-quarter">
/> <TypeSelector
<SubmitButton label={t("permission.type")}
label={t("permission.add-permission.submit-button")} helpText={t("permission.help.typeHelpText")}
loading={loading} handleTypeChange={this.handleTypeChange}
disabled={!this.state.valid || this.state.name === ""} type={type ? type : "READ"}
/> />
</div>
</div>
<div className="columns">
<div className="column">
<SubmitButton
label={t("permission.add-permission.submit-button")}
loading={loading}
disabled={!this.state.valid || this.state.name === ""}
/>
</div>
</div>
</form> </form>
</div> </div>
); );

View File

@@ -1,42 +1,42 @@
// @flow // @flow
import React from "react"; import React from "react";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { Select } from "@scm-manager/ui-components"; import { Select } from "@scm-manager/ui-components";
type Props = { type Props = {
t: string => string, t: string => string,
handleTypeChange: string => void, handleTypeChange: string => void,
type: string, type: string,
label?: string, label?: string,
helpText?: string, helpText?: string,
loading?: boolean loading?: boolean
}; };
class TypeSelector extends React.Component<Props> { class TypeSelector extends React.Component<Props> {
render() { render() {
const { type, handleTypeChange, loading, label, helpText } = this.props; const { type, handleTypeChange, loading, label, helpText } = this.props;
const types = ["READ", "OWNER", "WRITE"]; const types = ["READ", "OWNER", "WRITE"];
return ( return (
<Select <Select
onChange={handleTypeChange} onChange={handleTypeChange}
value={type ? type : "READ"} value={type ? type : "READ"}
options={this.createSelectOptions(types)} options={this.createSelectOptions(types)}
loading={loading} loading={loading}
label={label} label={label}
helpText={helpText} helpText={helpText}
/> />
); );
} }
createSelectOptions(types: string[]) { createSelectOptions(types: string[]) {
return types.map(type => { return types.map(type => {
return { return {
label: type, label: type,
value: type value: type
}; };
}); });
} }
} }
export default translate("repos")(TypeSelector); export default translate("repos")(TypeSelector);

View File

@@ -1,225 +1,225 @@
//@flow //@flow
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { import {
fetchPermissions, fetchPermissions,
getFetchPermissionsFailure, getFetchPermissionsFailure,
isFetchPermissionsPending, isFetchPermissionsPending,
getPermissionsOfRepo, getPermissionsOfRepo,
hasCreatePermission, hasCreatePermission,
createPermission, createPermission,
isCreatePermissionPending, isCreatePermissionPending,
getCreatePermissionFailure, getCreatePermissionFailure,
createPermissionReset, createPermissionReset,
getDeletePermissionsFailure, getDeletePermissionsFailure,
getModifyPermissionsFailure, getModifyPermissionsFailure,
modifyPermissionReset, modifyPermissionReset,
deletePermissionReset deletePermissionReset
} from "../modules/permissions"; } from "../modules/permissions";
import { Loading, ErrorPage } from "@scm-manager/ui-components"; import { Loading, ErrorPage } from "@scm-manager/ui-components";
import type { import type {
Permission, Permission,
PermissionCollection, PermissionCollection,
PermissionCreateEntry PermissionCreateEntry
} from "@scm-manager/ui-types"; } from "@scm-manager/ui-types";
import SinglePermission from "./SinglePermission"; import SinglePermission from "./SinglePermission";
import CreatePermissionForm from "../components/CreatePermissionForm"; import CreatePermissionForm from "../components/CreatePermissionForm";
import type { History } from "history"; import type { History } from "history";
import { getPermissionsLink } from "../../modules/repos"; import { getPermissionsLink } from "../../modules/repos";
import { import {
getGroupAutoCompleteLink, getGroupAutoCompleteLink,
getUserAutoCompleteLink getUserAutoCompleteLink
} from "../../../modules/indexResource"; } from "../../../modules/indexResource";
type Props = { type Props = {
namespace: string, namespace: string,
repoName: string, repoName: string,
loading: boolean, loading: boolean,
error: Error, error: Error,
permissions: PermissionCollection, permissions: PermissionCollection,
hasPermissionToCreate: boolean, hasPermissionToCreate: boolean,
loadingCreatePermission: boolean, loadingCreatePermission: boolean,
permissionsLink: string, permissionsLink: string,
groupAutoCompleteLink: string, groupAutoCompleteLink: string,
userAutoCompleteLink: string, userAutoCompleteLink: string,
//dispatch functions //dispatch functions
fetchPermissions: (link: string, namespace: string, repoName: string) => void, fetchPermissions: (link: string, namespace: string, repoName: string) => void,
createPermission: ( createPermission: (
link: string, link: string,
permission: PermissionCreateEntry, permission: PermissionCreateEntry,
namespace: string, namespace: string,
repoName: string, repoName: string,
callback?: () => void callback?: () => void
) => void, ) => void,
createPermissionReset: (string, string) => void, createPermissionReset: (string, string) => void,
modifyPermissionReset: (string, string) => void, modifyPermissionReset: (string, string) => void,
deletePermissionReset: (string, string) => void, deletePermissionReset: (string, string) => void,
// context props // context props
t: string => string, t: string => string,
match: any, match: any,
history: History history: History
}; };
class Permissions extends React.Component<Props> { class Permissions extends React.Component<Props> {
componentDidMount() { componentDidMount() {
const { const {
fetchPermissions, fetchPermissions,
namespace, namespace,
repoName, repoName,
modifyPermissionReset, modifyPermissionReset,
createPermissionReset, createPermissionReset,
deletePermissionReset, deletePermissionReset,
permissionsLink permissionsLink
} = this.props; } = this.props;
createPermissionReset(namespace, repoName); createPermissionReset(namespace, repoName);
modifyPermissionReset(namespace, repoName); modifyPermissionReset(namespace, repoName);
deletePermissionReset(namespace, repoName); deletePermissionReset(namespace, repoName);
fetchPermissions(permissionsLink, namespace, repoName); fetchPermissions(permissionsLink, namespace, repoName);
} }
createPermission = (permission: Permission) => { createPermission = (permission: Permission) => {
this.props.createPermission( this.props.createPermission(
this.props.permissionsLink, this.props.permissionsLink,
permission, permission,
this.props.namespace, this.props.namespace,
this.props.repoName this.props.repoName
); );
}; };
render() { render() {
const { const {
loading, loading,
error, error,
permissions, permissions,
t, t,
namespace, namespace,
repoName, repoName,
loadingCreatePermission, loadingCreatePermission,
hasPermissionToCreate, hasPermissionToCreate,
userAutoCompleteLink, userAutoCompleteLink,
groupAutoCompleteLink groupAutoCompleteLink
} = this.props; } = this.props;
if (error) { if (error) {
return ( return (
<ErrorPage <ErrorPage
title={t("permission.error-title")} title={t("permission.error-title")}
subtitle={t("permission.error-subtitle")} subtitle={t("permission.error-subtitle")}
error={error} error={error}
/> />
); );
} }
if (loading || !permissions) { if (loading || !permissions) {
return <Loading />; return <Loading />;
} }
const createPermissionForm = hasPermissionToCreate ? ( const createPermissionForm = hasPermissionToCreate ? (
<CreatePermissionForm <CreatePermissionForm
createPermission={permission => this.createPermission(permission)} createPermission={permission => this.createPermission(permission)}
loading={loadingCreatePermission} loading={loadingCreatePermission}
currentPermissions={permissions} currentPermissions={permissions}
userAutoCompleteLink={userAutoCompleteLink} userAutoCompleteLink={userAutoCompleteLink}
groupAutoCompleteLink={groupAutoCompleteLink} groupAutoCompleteLink={groupAutoCompleteLink}
/> />
) : null; ) : null;
return ( return (
<div> <div>
<table className="table is-hoverable is-fullwidth"> <table className="has-background-light table is-hoverable is-fullwidth">
<thead> <thead>
<tr> <tr>
<th>{t("permission.name")}</th> <th>{t("permission.name")}</th>
<th className="is-hidden-mobile"> <th className="is-hidden-mobile">
{t("permission.group-permission")} {t("permission.group-permission")}
</th> </th>
<th>{t("permission.type")}</th> <th>{t("permission.type")}</th>
<th /> <th />
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{permissions.map(permission => { {permissions.map(permission => {
return ( return (
<SinglePermission <SinglePermission
key={permission.name + permission.groupPermission.toString()} key={permission.name + permission.groupPermission.toString()}
namespace={namespace} namespace={namespace}
repoName={repoName} repoName={repoName}
permission={permission} permission={permission}
/> />
); );
})} })}
</tbody> </tbody>
</table> </table>
{createPermissionForm} {createPermissionForm}
</div> </div>
); );
} }
} }
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const namespace = ownProps.namespace; const namespace = ownProps.namespace;
const repoName = ownProps.repoName; const repoName = ownProps.repoName;
const error = const error =
getFetchPermissionsFailure(state, namespace, repoName) || getFetchPermissionsFailure(state, namespace, repoName) ||
getCreatePermissionFailure(state, namespace, repoName) || getCreatePermissionFailure(state, namespace, repoName) ||
getDeletePermissionsFailure(state, namespace, repoName) || getDeletePermissionsFailure(state, namespace, repoName) ||
getModifyPermissionsFailure(state, namespace, repoName); getModifyPermissionsFailure(state, namespace, repoName);
const loading = isFetchPermissionsPending(state, namespace, repoName); const loading = isFetchPermissionsPending(state, namespace, repoName);
const permissions = getPermissionsOfRepo(state, namespace, repoName); const permissions = getPermissionsOfRepo(state, namespace, repoName);
const loadingCreatePermission = isCreatePermissionPending( const loadingCreatePermission = isCreatePermissionPending(
state, state,
namespace, namespace,
repoName repoName
); );
const hasPermissionToCreate = hasCreatePermission(state, namespace, repoName); const hasPermissionToCreate = hasCreatePermission(state, namespace, repoName);
const permissionsLink = getPermissionsLink(state, namespace, repoName); const permissionsLink = getPermissionsLink(state, namespace, repoName);
const groupAutoCompleteLink = getGroupAutoCompleteLink(state); const groupAutoCompleteLink = getGroupAutoCompleteLink(state);
const userAutoCompleteLink = getUserAutoCompleteLink(state); const userAutoCompleteLink = getUserAutoCompleteLink(state);
return { return {
namespace, namespace,
repoName, repoName,
error, error,
loading, loading,
permissions, permissions,
hasPermissionToCreate, hasPermissionToCreate,
loadingCreatePermission, loadingCreatePermission,
permissionsLink, permissionsLink,
groupAutoCompleteLink, groupAutoCompleteLink,
userAutoCompleteLink userAutoCompleteLink
}; };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
fetchPermissions: (link: string, namespace: string, repoName: string) => { fetchPermissions: (link: string, namespace: string, repoName: string) => {
dispatch(fetchPermissions(link, namespace, repoName)); dispatch(fetchPermissions(link, namespace, repoName));
}, },
createPermission: ( createPermission: (
link: string, link: string,
permission: PermissionCreateEntry, permission: PermissionCreateEntry,
namespace: string, namespace: string,
repoName: string, repoName: string,
callback?: () => void callback?: () => void
) => { ) => {
dispatch( dispatch(
createPermission(link, permission, namespace, repoName, callback) createPermission(link, permission, namespace, repoName, callback)
); );
}, },
createPermissionReset: (namespace: string, repoName: string) => { createPermissionReset: (namespace: string, repoName: string) => {
dispatch(createPermissionReset(namespace, repoName)); dispatch(createPermissionReset(namespace, repoName));
}, },
modifyPermissionReset: (namespace: string, repoName: string) => { modifyPermissionReset: (namespace: string, repoName: string) => {
dispatch(modifyPermissionReset(namespace, repoName)); dispatch(modifyPermissionReset(namespace, repoName));
}, },
deletePermissionReset: (namespace: string, repoName: string) => { deletePermissionReset: (namespace: string, repoName: string) => {
dispatch(deletePermissionReset(namespace, repoName)); dispatch(deletePermissionReset(namespace, repoName));
} }
}; };
}; };
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(translate("repos")(Permissions)); )(translate("repos")(Permissions));

View File

@@ -1,176 +1,176 @@
// @flow // @flow
import React from "react"; import React from "react";
import type { Permission } from "@scm-manager/ui-types"; import type { Permission } from "@scm-manager/ui-types";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { import {
modifyPermission, modifyPermission,
isModifyPermissionPending, isModifyPermissionPending,
deletePermission, deletePermission,
isDeletePermissionPending isDeletePermissionPending
} from "../modules/permissions"; } from "../modules/permissions";
import { connect } from "react-redux"; import { connect } from "react-redux";
import type { History } from "history"; import type { History } from "history";
import { Checkbox } from "@scm-manager/ui-components"; import { Checkbox } from "@scm-manager/ui-components";
import DeletePermissionButton from "../components/buttons/DeletePermissionButton"; import DeletePermissionButton from "../components/buttons/DeletePermissionButton";
import TypeSelector from "../components/TypeSelector"; import TypeSelector from "../components/TypeSelector";
type Props = { type Props = {
submitForm: Permission => void, submitForm: Permission => void,
modifyPermission: (Permission, string, string) => void, modifyPermission: (Permission, string, string) => void,
permission: Permission, permission: Permission,
t: string => string, t: string => string,
namespace: string, namespace: string,
repoName: string, repoName: string,
match: any, match: any,
history: History, history: History,
loading: boolean, loading: boolean,
deletePermission: (Permission, string, string) => void, deletePermission: (Permission, string, string) => void,
deleteLoading: boolean deleteLoading: boolean
}; };
type State = { type State = {
permission: Permission permission: Permission
}; };
class SinglePermission extends React.Component<Props, State> { class SinglePermission extends React.Component<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
permission: { permission: {
name: "", name: "",
type: "READ", type: "READ",
groupPermission: false, groupPermission: false,
_links: {} _links: {}
} }
}; };
} }
componentDidMount() { componentDidMount() {
const { permission } = this.props; const { permission } = this.props;
if (permission) { if (permission) {
this.setState({ this.setState({
permission: { permission: {
name: permission.name, name: permission.name,
type: permission.type, type: permission.type,
groupPermission: permission.groupPermission, groupPermission: permission.groupPermission,
_links: permission._links _links: permission._links
} }
}); });
} }
} }
deletePermission = () => { deletePermission = () => {
this.props.deletePermission( this.props.deletePermission(
this.props.permission, this.props.permission,
this.props.namespace, this.props.namespace,
this.props.repoName this.props.repoName
); );
}; };
render() { render() {
const { permission } = this.state; const { permission } = this.state;
const { loading, namespace, repoName } = this.props; const { loading, namespace, repoName } = this.props;
const typeSelector = const typeSelector =
this.props.permission._links && this.props.permission._links.update ? ( this.props.permission._links && this.props.permission._links.update ? (
<td> <td>
<TypeSelector <TypeSelector
handleTypeChange={this.handleTypeChange} handleTypeChange={this.handleTypeChange}
type={permission.type ? permission.type : "READ"} type={permission.type ? permission.type : "READ"}
loading={loading} loading={loading}
/> />
</td> </td>
) : ( ) : (
<td>{permission.type}</td> <td>{permission.type}</td>
); );
return ( return (
<tr> <tr>
<td>{permission.name}</td> <td>{permission.name}</td>
<td> <td>
<Checkbox checked={permission ? permission.groupPermission : false} /> <Checkbox checked={permission ? permission.groupPermission : false} />
</td> </td>
{typeSelector} {typeSelector}
<td> <td>
<DeletePermissionButton <DeletePermissionButton
permission={permission} permission={permission}
namespace={namespace} namespace={namespace}
repoName={repoName} repoName={repoName}
deletePermission={this.deletePermission} deletePermission={this.deletePermission}
loading={this.props.deleteLoading} loading={this.props.deleteLoading}
/> />
</td> </td>
</tr> </tr>
); );
} }
handleTypeChange = (type: string) => { handleTypeChange = (type: string) => {
this.setState({ this.setState({
permission: { permission: {
...this.state.permission, ...this.state.permission,
type: type type: type
} }
}); });
this.modifyPermission(type); this.modifyPermission(type);
}; };
modifyPermission = (type: string) => { modifyPermission = (type: string) => {
let permission = this.state.permission; let permission = this.state.permission;
permission.type = type; permission.type = type;
this.props.modifyPermission( this.props.modifyPermission(
permission, permission,
this.props.namespace, this.props.namespace,
this.props.repoName this.props.repoName
); );
}; };
createSelectOptions(types: string[]) { createSelectOptions(types: string[]) {
return types.map(type => { return types.map(type => {
return { return {
label: type, label: type,
value: type value: type
}; };
}); });
} }
} }
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const permission = ownProps.permission; const permission = ownProps.permission;
const loading = isModifyPermissionPending( const loading = isModifyPermissionPending(
state, state,
ownProps.namespace, ownProps.namespace,
ownProps.repoName, ownProps.repoName,
permission permission
); );
const deleteLoading = isDeletePermissionPending( const deleteLoading = isDeletePermissionPending(
state, state,
ownProps.namespace, ownProps.namespace,
ownProps.repoName, ownProps.repoName,
permission permission
); );
return { loading, deleteLoading }; return { loading, deleteLoading };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
modifyPermission: ( modifyPermission: (
permission: Permission, permission: Permission,
namespace: string, namespace: string,
repoName: string repoName: string
) => { ) => {
dispatch(modifyPermission(permission, namespace, repoName)); dispatch(modifyPermission(permission, namespace, repoName));
}, },
deletePermission: ( deletePermission: (
permission: Permission, permission: Permission,
namespace: string, namespace: string,
repoName: string repoName: string
) => { ) => {
dispatch(deletePermission(permission, namespace, repoName)); dispatch(deletePermission(permission, namespace, repoName));
} }
}; };
}; };
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(translate("repos")(SinglePermission)); )(translate("repos")(SinglePermission));

View File

@@ -41,6 +41,9 @@ const styles = {
isVerticalCenter: { isVerticalCenter: {
display: "flex", display: "flex",
alignItems: "center" alignItems: "center"
},
hasBackground: {
backgroundColor: "#FBFBFB"
} }
}; };
@@ -120,8 +123,14 @@ class Content extends React.Component<Props, State> {
const fileSize = file.directory ? "" : <FileSize bytes={file.length} />; const fileSize = file.directory ? "" : <FileSize bytes={file.length} />;
if (!collapsed) { if (!collapsed) {
return ( return (
<div className={classNames("panel-block", classes.toCenterContent)}> <div
<table className="table"> className={classNames(
"panel-block",
classes.toCenterContent,
classes.hasBackground
)}
>
<table className={classNames("table", classes.hasBackground)}>
<tbody> <tbody>
<tr> <tr>
<td>{t("sources.content.path")}</td> <td>{t("sources.content.path")}</td>

View File

@@ -93,7 +93,7 @@ class Sources extends React.Component<Props> {
if (currentFileIsDirectory) { if (currentFileIsDirectory) {
return ( return (
<> <div className={"has-border-around"}>
{this.renderBranchSelector()} {this.renderBranchSelector()}
<FileTree <FileTree
repository={repository} repository={repository}
@@ -101,7 +101,7 @@ class Sources extends React.Component<Props> {
path={path} path={path}
baseUrl={baseUrl} baseUrl={baseUrl}
/> />
</> </div>
); );
} else { } else {
return ( return (

View File

@@ -105,41 +105,55 @@ class UserForm extends React.Component<Props, State> {
} }
return ( return (
<form onSubmit={this.submit}> <form onSubmit={this.submit}>
{nameField} <div className="columns">
<InputField <div className="column is-half">
label={t("user.displayName")} {nameField}
onChange={this.handleDisplayNameChange} <InputField
value={user ? user.displayName : ""} label={t("user.displayName")}
validationError={this.state.displayNameValidationError} onChange={this.handleDisplayNameChange}
errorMessage={t("validation.displayname-invalid")} value={user ? user.displayName : ""}
helpText={t("help.displayNameHelpText")} validationError={this.state.displayNameValidationError}
/> errorMessage={t("validation.displayname-invalid")}
<InputField helpText={t("help.displayNameHelpText")}
label={t("user.mail")} />
onChange={this.handleEmailChange} </div>
value={user ? user.mail : ""} <div className="column is-half">
validationError={this.state.mailValidationError} <InputField
errorMessage={t("validation.mail-invalid")} label={t("user.mail")}
helpText={t("help.mailHelpText")} onChange={this.handleEmailChange}
/> value={user ? user.mail : ""}
{passwordChangeField} validationError={this.state.mailValidationError}
<Checkbox errorMessage={t("validation.mail-invalid")}
label={t("user.admin")} helpText={t("help.mailHelpText")}
onChange={this.handleAdminChange} />
checked={user ? user.admin : false} </div>
helpText={t("help.adminHelpText")} </div>
/> <div className="columns">
<Checkbox <div className="column">
label={t("user.active")} {passwordChangeField}
onChange={this.handleActiveChange} <Checkbox
checked={user ? user.active : false} label={t("user.admin")}
helpText={t("help.activeHelpText")} onChange={this.handleAdminChange}
/> checked={user ? user.admin : false}
<SubmitButton helpText={t("help.adminHelpText")}
disabled={!this.isValid()} />
loading={loading} <Checkbox
label={t("user-form.submit")} label={t("user.active")}
/> onChange={this.handleActiveChange}
checked={user ? user.active : false}
helpText={t("help.activeHelpText")}
/>
</div>
</div>
<div className="columns">
<div className="column">
<SubmitButton
disabled={!this.isValid()}
loading={loading}
label={t("user-form.submit")}
/>
</div>
</div>
</form> </form>
); );
} }

View File

@@ -1,66 +1,66 @@
//@flow //@flow
import React from "react"; import React from "react";
import type { User } from "@scm-manager/ui-types"; import type { User } from "@scm-manager/ui-types";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { Checkbox, MailLink, DateFromNow } from "@scm-manager/ui-components"; import { Checkbox, MailLink, DateFromNow } from "@scm-manager/ui-components";
type Props = { type Props = {
user: User, user: User,
t: string => string t: string => string
}; };
class Details extends React.Component<Props> { class Details extends React.Component<Props> {
render() { render() {
const { user, t } = this.props; const { user, t } = this.props;
return ( return (
<table className="table"> <table className="table">
<tbody> <tbody>
<tr> <tr>
<td>{t("user.name")}</td> <td className="has-text-weight-semibold">{t("user.name")}</td>
<td>{user.name}</td> <td>{user.name}</td>
</tr> </tr>
<tr> <tr>
<td>{t("user.displayName")}</td> <td className="has-text-weight-semibold">{t("user.displayName")}</td>
<td>{user.displayName}</td> <td>{user.displayName}</td>
</tr> </tr>
<tr> <tr>
<td>{t("user.mail")}</td> <td className="has-text-weight-semibold">{t("user.mail")}</td>
<td> <td>
<MailLink address={user.mail} /> <MailLink address={user.mail} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{t("user.admin")}</td> <td className="has-text-weight-semibold">{t("user.admin")}</td>
<td> <td>
<Checkbox checked={user.admin} /> <Checkbox checked={user.admin} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{t("user.active")}</td> <td className="has-text-weight-semibold">{t("user.active")}</td>
<td> <td>
<Checkbox checked={user.active} /> <Checkbox checked={user.active} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{t("user.type")}</td> <td className="has-text-weight-semibold">{t("user.type")}</td>
<td>{user.type}</td> <td>{user.type}</td>
</tr> </tr>
<tr> <tr>
<td>{t("user.creationDate")}</td> <td className="has-text-weight-semibold">{t("user.creationDate")}</td>
<td> <td>
<DateFromNow date={user.creationDate} /> <DateFromNow date={user.creationDate} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{t("user.lastModified")}</td> <td className="has-text-weight-semibold">{t("user.lastModified")}</td>
<td> <td>
<DateFromNow date={user.lastModified} /> <DateFromNow date={user.lastModified} />
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
); );
} }
} }
export default translate("users")(Details); export default translate("users")(Details);

View File

@@ -1,31 +1,31 @@
// @flow // @flow
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import type { User } from "@scm-manager/ui-types"; import type { User } from "@scm-manager/ui-types";
type Props = { type Props = {
user: User user: User
}; };
export default class UserRow extends React.Component<Props> { export default class UserRow extends React.Component<Props> {
renderLink(to: string, label: string) { renderLink(to: string, label: string) {
return <Link to={to}>{label}</Link>; return <Link to={to}>{label}</Link>;
} }
render() { render() {
const { user } = this.props; const { user } = this.props;
const to = `/user/${user.name}`; const to = `/user/${user.name}`;
return ( return (
<tr> <tr>
<td className="is-hidden-mobile">{this.renderLink(to, user.name)}</td> <td className="is-hidden-mobile">{this.renderLink(to, user.name)}</td>
<td>{this.renderLink(to, user.displayName)}</td> <td>{this.renderLink(to, user.displayName)}</td>
<td> <td>
<a href={`mailto: ${user.mail}`}>{user.mail}</a> <a href={`mailto: ${user.mail}`}>{user.mail}</a>
</td> </td>
<td className="is-hidden-mobile"> <td className="is-hidden-mobile">
<input type="checkbox" id="admin" checked={user.admin} readOnly /> <input type="checkbox" id="admin" checked={user.admin} readOnly />
</td> </td>
</tr> </tr>
); );
} }
} }

View File

@@ -1,35 +1,37 @@
// @flow // @flow
import React from "react"; import React from "react";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import UserRow from "./UserRow"; import UserRow from "./UserRow";
import type { User } from "@scm-manager/ui-types"; import type { User } from "@scm-manager/ui-types";
type Props = { type Props = {
t: string => string, t: string => string,
users: User[] users: User[]
}; };
class UserTable extends React.Component<Props> { ;
render() {
const { users, t } = this.props; class UserTable extends React.Component<Props> {
return ( render() {
<table className="table is-hoverable is-fullwidth"> const { users, t } = this.props;
<thead> return (
<tr> <table className="card-table table is-hoverable is-fullwidth">
<th className="is-hidden-mobile">{t("user.name")}</th> <thead>
<th>{t("user.displayName")}</th> <tr>
<th>{t("user.mail")}</th> <th className="is-hidden-mobile">{t("user.name")}</th>
<th className="is-hidden-mobile">{t("user.admin")}</th> <th>{t("user.displayName")}</th>
</tr> <th>{t("user.mail")}</th>
</thead> <th className="is-hidden-mobile">{t("user.admin")}</th>
<tbody> </tr>
{users.map((user, index) => { </thead>
return <UserRow key={index} user={user} />; <tbody>
})} {users.map((user, index) => {
</tbody> return <UserRow key={index} user={user} />;
</table> })}
); </tbody>
} </table>
} );
}
export default translate("users")(UserTable); }
export default translate("users")(UserTable);

View File

@@ -1,143 +1,143 @@
// @flow // @flow
import React from "react"; import React from "react";
import type { History } from "history"; import type { History } from "history";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { translate } from "react-i18next"; import { translate } from "react-i18next";
import { import {
fetchUsersByPage, fetchUsersByPage,
fetchUsersByLink, fetchUsersByLink,
getUsersFromState, getUsersFromState,
selectListAsCollection, selectListAsCollection,
isPermittedToCreateUsers, isPermittedToCreateUsers,
isFetchUsersPending, isFetchUsersPending,
getFetchUsersFailure getFetchUsersFailure
} from "../modules/users"; } from "../modules/users";
import { Page, Paginator } from "@scm-manager/ui-components"; import { Page, Paginator } from "@scm-manager/ui-components";
import { UserTable } from "./../components/table"; import { UserTable } from "./../components/table";
import type { User, PagedCollection } from "@scm-manager/ui-types"; import type { User, PagedCollection } from "@scm-manager/ui-types";
import CreateUserButton from "../components/buttons/CreateUserButton"; import CreateUserButton from "../components/buttons/CreateUserButton";
import { getUsersLink } from "../../modules/indexResource"; import { getUsersLink } from "../../modules/indexResource";
type Props = { type Props = {
users: User[], users: User[],
loading: boolean, loading: boolean,
error: Error, error: Error,
canAddUsers: boolean, canAddUsers: boolean,
list: PagedCollection, list: PagedCollection,
page: number, page: number,
usersLink: string, usersLink: string,
// context objects // context objects
t: string => string, t: string => string,
history: History, history: History,
// dispatch functions // dispatch functions
fetchUsersByPage: (link: string, page: number) => void, fetchUsersByPage: (link: string, page: number) => void,
fetchUsersByLink: (link: string) => void fetchUsersByLink: (link: string) => void
}; };
class Users extends React.Component<Props> { class Users extends React.Component<Props> {
componentDidMount() { componentDidMount() {
this.props.fetchUsersByPage(this.props.usersLink, this.props.page); this.props.fetchUsersByPage(this.props.usersLink, this.props.page);
} }
onPageChange = (link: string) => { onPageChange = (link: string) => {
this.props.fetchUsersByLink(link); this.props.fetchUsersByLink(link);
}; };
/** /**
* reflect page transitions in the uri * reflect page transitions in the uri
*/ */
componentDidUpdate() { componentDidUpdate() {
const { page, list } = this.props; const { page, list } = this.props;
if (list && (list.page || list.page === 0)) { if (list && (list.page || list.page === 0)) {
// backend starts paging by 0 // backend starts paging by 0
const statePage: number = list.page + 1; const statePage: number = list.page + 1;
if (page !== statePage) { if (page !== statePage) {
this.props.history.push(`/users/${statePage}`); this.props.history.push(`/users/${statePage}`);
} }
} }
} }
render() { render() {
const { users, loading, error, t } = this.props; const { users, loading, error, t } = this.props;
return ( return (
<Page <Page
title={t("users.title")} title={t("users.title")}
subtitle={t("users.subtitle")} subtitle={t("users.subtitle")}
loading={loading || !users} loading={loading || !users}
error={error} error={error}
> >
<UserTable users={users} /> <UserTable users={users} />
{this.renderPaginator()} {this.renderPaginator()}
{this.renderCreateButton()} {this.renderCreateButton()}
</Page> </Page>
); );
} }
renderPaginator() { renderPaginator() {
const { list } = this.props; const { list } = this.props;
if (list) { if (list) {
return <Paginator collection={list} onPageChange={this.onPageChange} />; return <Paginator collection={list} onPageChange={this.onPageChange} />;
} }
return null; return null;
} }
renderCreateButton() { renderCreateButton() {
if (this.props.canAddUsers) { if (this.props.canAddUsers) {
return <CreateUserButton />; return <CreateUserButton />;
} else { } else {
return; return;
} }
} }
} }
const getPageFromProps = props => { const getPageFromProps = props => {
let page = props.match.params.page; let page = props.match.params.page;
if (page) { if (page) {
page = parseInt(page, 10); page = parseInt(page, 10);
} else { } else {
page = 1; page = 1;
} }
return page; return page;
}; };
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const users = getUsersFromState(state); const users = getUsersFromState(state);
const loading = isFetchUsersPending(state); const loading = isFetchUsersPending(state);
const error = getFetchUsersFailure(state); const error = getFetchUsersFailure(state);
const usersLink = getUsersLink(state); const usersLink = getUsersLink(state);
const page = getPageFromProps(ownProps); const page = getPageFromProps(ownProps);
const canAddUsers = isPermittedToCreateUsers(state); const canAddUsers = isPermittedToCreateUsers(state);
const list = selectListAsCollection(state); const list = selectListAsCollection(state);
return { return {
users, users,
loading, loading,
error, error,
canAddUsers, canAddUsers,
list, list,
page, page,
usersLink usersLink
}; };
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
fetchUsersByPage: (link: string, page: number) => { fetchUsersByPage: (link: string, page: number) => {
dispatch(fetchUsersByPage(link, page)); dispatch(fetchUsersByPage(link, page));
}, },
fetchUsersByLink: (link: string) => { fetchUsersByLink: (link: string) => {
dispatch(fetchUsersByLink(link)); dispatch(fetchUsersByLink(link));
} }
}; };
}; };
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(translate("users")(Users)); )(translate("users")(Users));

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
@import "bulma/sass/utilities/initial-variables"; @import "bulma/sass/utilities/initial-variables";
@import "bulma/sass/utilities/functions"; @import "bulma/sass/utilities/functions";
$blue: #33b2e8;
$blue: #33B2E8; $mint: #11dfd0;
// $footer-background-color // $footer-background-color
@@ -51,15 +51,239 @@ $blue: #33B2E8;
&:hover, &:hover,
&:focus { &:focus {
box-shadow: $box-link-hover-shadow; box-shadow: $box-link-hover-shadow;
} }
&:active { &:active {
box-shadow: $box-link-active-shadow; box-shadow: $box-link-active-shadow;
} }
} }
@import "@fortawesome/fontawesome-free/scss/fontawesome.scss"; @import "@fortawesome/fontawesome-free/scss/fontawesome.scss";
$fa-font-path: "webfonts"; $fa-font-path: "webfonts";
@import "@fortawesome/fontawesome-free/scss/solid.scss"; @import "@fortawesome/fontawesome-free/scss/solid.scss";
@import "diff2html/dist/diff2html"; @import "diff2html/dist/diff2html";
// NEW STYLES
//typography
.subtitle {
color: #666;
}
.has-border-white {
border-color: #fff !important;
}
// buttons
.button {
padding-left: 1.5em;
padding-right: 1.5em;
height: 2.5rem;
&.is-primary {
background-color: $mint;
}
}
//border around options
.has-border-around {
border-top: 1px solid #eee;
border-left: 1px solid #eee;
border-right: 1px solid #eee;
border-bottom: 1px solid #eee;
}
// multiline Columns
.columns.is-multiline {
.column.is-half {
width: calc(50% - 0.75rem);
max-height: 120px;
&:nth-child(odd) {
margin-right: 1.5rem;
}
}
@media screen and (max-width: 768px) {
.column.is-half {
width: 100%;
&:nth-child(odd) {
margin-right: 0;
}
}
}
}
.media {
.media-content {
width: calc(50% - 0.75rem);
max-height: 120px;
.shorten-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
// tables
.table {
width: 100%;
td {
border-color: #eee;
padding: 1rem;
}
}
// card tables
.card-table {
border-collapse: separate;
border-spacing: 0px 5px;
tr {
a {
color: #363636;
}
&:hover {
td {
background-color: whitesmoke;
&:nth-child(4) {
background-color: #e1e1e1;
}
}
a {
color: $blue;
}
}
}
td {
border-bottom: 1px solid whitesmoke;
background-color: #fafafa;
padding: 1em 1.25em;
&:first-child {
border-left: 3px solid $mint;
}
&:nth-child(4) {
background-color: whitesmoke;
}
}
&.is-hoverable tbody tr:not(.is-selected):hover {
background-color: whitesmoke;
}
thead th {
background-color: transparent;
border: none;
}
}
// forms
.field:not(.is-grouped) {
margin-bottom: 1rem;
}
.input,
.textarea {
/*background-color: whitesmoke;*/
border-color: #98d8f3;
box-shadow: none;
}
// pagination
.pagination-next,
.pagination-link,
.pagination-ellipsis {
padding-left: 1.5em;
padding-right: 1.5em;
height: 2.5rem;
}
.pagination-previous,
.pagination-next {
min-width: 6.75em;
}
// dark hero colors
.hero.is-dark {
background-color: #002e4b;
background-image: url(../images/scmManagerHero.jpg);
background-size: cover;
background-position: top center;
.tabs.is-boxed li.is-active a,
.tabs.is-boxed li.is-active a:hover,
.tabs.is-toggle li.is-active a,
.tabs.is-toggle li.is-active a:hover {
background-color: #28b1e8;
border-color: #28b1e8;
color: #fff;
}
}
// footer
.footer {
background-color: whitesmoke;
}
// sidebar menu
.aside-background {
bottom: 0;
left: 50%;
position: absolute;
right: 0;
top: 0;
background-color: whitesmoke;
}
.menu {
div {
height: 100%;
/*border: 1px solid #eee;*/
margin-bottom: 1rem;
}
}
.menu-label {
color: #fff;
font-size: 1em;
font-weight: 600;
background-color: #bbb;
border-radius: 5px 5px 0 0;
padding: 0.5rem 1rem;
text-transform: none;
&:last-child,
&:not(:last-child) {
margin-bottom: 0;
}
}
.menu div:first-child .menu-label {
background-color: $blue;
}
.menu-list {
a {
border-radius: 0;
color: #333;
padding: 1rem;
border-top: 1px solid #eee;
border-left: 1px solid #eee;
border-right: 1px solid #eee;
&.is-active {
color: $blue;
background-color: #fff;
&:before {
position: relative;
content: " ";
background: $blue;
height: 53px;
width: 2px;
display: block;
left: -17px;
float: left;
top: -16px;
}
}
}
> li:first-child > a {
border-top: none;
}
li:last-child > a {
border-bottom: 1px solid #eee;
}
}