Merged in feature/ui_adjustments (pull request #365)

Feature/ui adjustments
This commit is contained in:
Eduard Heimbuch
2019-11-28 09:32:51 +00:00
47 changed files with 336 additions and 278 deletions

View File

@@ -14,6 +14,12 @@ const Switcher = styled(ButtonAddons)`
right: 0;
`;
const SmallButton = styled(Button).attrs(props => ({
className: "is-small"
}))`
height: inherit;
`;
type Props = {
repository: Repository;
};
@@ -62,9 +68,9 @@ export default class ProtocolInformation extends React.Component<Props, State> {
}
return (
<Button color={color} action={() => this.selectProtocol(protocol)}>
<SmallButton color={color} action={() => this.selectProtocol(protocol)}>
{name.toUpperCase()}
</Button>
</SmallButton>
);
};

View File

@@ -1,4 +1,5 @@
import React from "react";
import classNames from "classnames";
import { Async, AsyncCreatable } from "react-select";
import { SelectValue } from "@scm-manager/ui-types";
import LabelWithHelpIcon from "./forms/LabelWithHelpIcon";
@@ -14,6 +15,7 @@ type Props = {
loadingMessage: string;
noOptionsMessage: string;
creatable?: boolean;
className?: string;
};
type State = {};
@@ -53,10 +55,11 @@ class Autocomplete extends React.Component<Props, State> {
loadingMessage,
noOptionsMessage,
loadSuggestions,
creatable
creatable,
className
} = this.props;
return (
<div className="field">
<div className={classNames("field", className)}>
<LabelWithHelpIcon label={label} helpText={helpText} />
<div className="control">
{creatable ? (

View File

@@ -336,7 +336,7 @@ exports[`Storyshots DateFromNow Default 1`] = `
exports[`Storyshots Forms|Checkbox Default 1`] = `
<div
className="sc-gipzik xsalO"
className="sc-hzDkRC itNtPz"
>
<div
className="field"
@@ -381,7 +381,7 @@ exports[`Storyshots Forms|Checkbox Default 1`] = `
exports[`Storyshots Forms|Checkbox Disabled 1`] = `
<div
className="sc-gipzik xsalO"
className="sc-hzDkRC itNtPz"
>
<div
className="field"
@@ -409,7 +409,7 @@ exports[`Storyshots Forms|Checkbox Disabled 1`] = `
exports[`Storyshots Forms|Radio Default 1`] = `
<div
className="sc-csuQGl fFFkRK"
className="sc-jhAzac edPAWD"
>
<label
className="radio"
@@ -438,7 +438,7 @@ exports[`Storyshots Forms|Radio Default 1`] = `
exports[`Storyshots Forms|Radio Disabled 1`] = `
<div
className="sc-csuQGl fFFkRK"
className="sc-jhAzac edPAWD"
>
<label
className="radio"

View File

@@ -2,8 +2,8 @@ import React from "react";
type Props = {
displayName: string;
url: string;
disabled: boolean;
url?: string;
disabled?: boolean;
onClick?: () => void;
};

View File

@@ -1,8 +1,7 @@
import React from "react";
import React, { FormEvent } from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { Links, Link } from "@scm-manager/ui-types";
import { apiClient, SubmitButton, Loading, ErrorNotification } from "../";
import { FormEvent } from "react";
import { apiClient, Level, SubmitButton, Loading, ErrorNotification } from "../";
type RenderProps = {
readOnly: boolean;
@@ -179,7 +178,9 @@ class Configuration extends React.Component<Props, State> {
<form onSubmit={this.modifyConfiguration}>
{this.props.render(renderProps)}
<hr />
<SubmitButton label={t("config.form.submit")} disabled={!valid || readOnly} loading={modifying} />
<Level
right={<SubmitButton label={t("config.form.submit")} disabled={!valid || readOnly} loading={modifying} />}
/>
</form>
</>
);

View File

@@ -1,7 +1,8 @@
import React, { MouseEvent } from "react";
import { AddButton } from "../buttons";
import styled from "styled-components";
import Level from "../layout/Level";
import InputField from "./InputField";
import AddButton from "../buttons/AddButton";
type Props = {
addEntry: (p: string) => void;
@@ -17,6 +18,22 @@ type State = {
entryToAdd: string;
};
const StyledLevel = styled(Level)`
align-items: stretch;
margin-bottom: 1rem !important; // same margin as field
`;
const StyledInputField = styled(InputField)`
width: 100%;
margin-right: 1.5rem;
`;
const StyledField = styled.div.attrs(props => ({
className: "field"
}))`
align-self: flex-end;
`;
class AddEntryToTableField extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
@@ -37,23 +54,29 @@ class AddEntryToTableField extends React.Component<Props, State> {
render() {
const { disabled, buttonLabel, fieldLabel, errorMessage, helpText } = this.props;
return (
<>
<InputField
label={fieldLabel}
errorMessage={errorMessage}
onChange={this.handleAddEntryChange}
validationError={!this.isValid()}
value={this.state.entryToAdd}
onReturnPressed={this.appendEntry}
disabled={disabled}
helpText={helpText}
/>
<AddButton
label={buttonLabel}
action={this.addButtonClicked}
disabled={disabled || this.state.entryToAdd === "" || !this.isValid()}
/>
</>
<StyledLevel
children={
<StyledInputField
label={fieldLabel}
errorMessage={errorMessage}
onChange={this.handleAddEntryChange}
validationError={!this.isValid()}
value={this.state.entryToAdd}
onReturnPressed={this.appendEntry}
disabled={disabled}
helpText={helpText}
/>
}
right={
<StyledField>
<AddButton
label={buttonLabel}
action={this.addButtonClicked}
disabled={disabled || this.state.entryToAdd === "" || !this.isValid()}
/>
</StyledField>
}
/>
);
}

View File

@@ -1,6 +1,7 @@
import React, { MouseEvent } from "react";
import { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
import styled from "styled-components";
import { SelectValue } from "@scm-manager/ui-types";
import Level from "../layout/Level";
import Autocomplete from "../Autocomplete";
import AddButton from "../buttons/AddButton";
@@ -20,6 +21,11 @@ type State = {
selectedValue?: SelectValue;
};
const StyledAutocomplete = styled(Autocomplete)`
width: 100%;
margin-right: 1.5rem;
`;
class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
@@ -41,21 +47,26 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
const { selectedValue } = this.state;
return (
<div className="field">
<Autocomplete
label={fieldLabel}
loadSuggestions={loadSuggestions}
valueSelected={this.handleAddEntryChange}
helpText={helpText}
value={selectedValue}
placeholder={placeholder}
loadingMessage={loadingMessage}
noOptionsMessage={noOptionsMessage}
creatable={true}
/>
<AddButton label={buttonLabel} action={this.addButtonClicked} disabled={disabled} />
</div>
<Level
children={
<StyledAutocomplete
label={fieldLabel}
loadSuggestions={loadSuggestions}
valueSelected={this.handleAddEntryChange}
helpText={helpText}
value={selectedValue}
placeholder={placeholder}
loadingMessage={loadingMessage}
noOptionsMessage={noOptionsMessage}
creatable={true}
/>
}
right={
<div className="field">
<AddButton label={buttonLabel} action={this.addButtonClicked} disabled={disabled} />
</div>
}
/>
);
}

View File

@@ -15,6 +15,7 @@ type Props = {
errorMessage?: string;
disabled?: boolean;
helpText?: string;
className?: string;
};
class InputField extends React.Component<Props> {
@@ -47,11 +48,21 @@ class InputField extends React.Component<Props> {
};
render() {
const { type, placeholder, value, validationError, errorMessage, disabled, label, helpText } = this.props;
const {
type,
placeholder,
value,
validationError,
errorMessage,
disabled,
label,
helpText,
className
} = this.props;
const errorView = validationError ? "is-danger" : "";
const helper = validationError ? <p className="help is-danger">{errorMessage}</p> : "";
return (
<div className="field">
<div className={classNames("field", className)}>
<LabelWithHelpIcon label={label} helpText={helpText} />
<div className="control">
<input

View File

@@ -4,15 +4,22 @@ import classNames from "classnames";
type Props = {
className?: string;
left?: ReactNode;
children?: ReactNode;
right?: ReactNode;
};
export default class Level extends React.Component<Props> {
render() {
const { className, left, right } = this.props;
const { className, left, children, right } = this.props;
let child = null;
if (children) {
child = <div className="level-item">{children}</div>;
}
return (
<div className={classNames("level", className)}>
<div className="level-left">{left}</div>
{child}
<div className="level-right">{right}</div>
</div>
);

View File

@@ -622,6 +622,10 @@ form .field:not(.is-grouped) {
}
}
.help {
position: absolute;
}
// label with help-icon compensation
.label-icon-spacing {
margin-top: 30px;

View File

@@ -6,7 +6,7 @@
"settingsNavLink": "Einstellungen",
"generalNavLink": "Generell"
},
"info": {
"info": {
"currentAppVersion": "Aktuelle Software-Versionsnummer",
"communityTitle": "Community Support",
"communityIconAlt": "Community Support Icon",
@@ -91,8 +91,7 @@
"submit": "Speichern"
},
"delete": {
"button": "Löschen",
"subtitle": "Berechtigungsrolle löschen",
"button": "Berechtigungsrolle löschen",
"confirmAlert": {
"title": "Berechtigungsrolle löschen?",
"message": "Wollen Sie diese Rolle wirklich löschen? Alle Benutzer mit dieser Rolle verlieren die entsprechenden Berechtigungen.",

View File

@@ -60,8 +60,7 @@
}
},
"deleteGroup": {
"subtitle": "Gruppe löschen",
"button": "Löschen",
"button": "Gruppe löschen",
"confirmAlert": {
"title": "Gruppe löschen",
"message": "Soll die Gruppe wirklich gelöscht werden?",

View File

@@ -114,7 +114,7 @@
"size": "Größe"
},
"noSources": "Keine Sources in diesem Branch gefunden.",
"extension" : {
"extension": {
"notBound": "Keine Erweiterung angebunden."
}
},
@@ -166,8 +166,7 @@
}
},
"deleteRepo": {
"subtitle": "Repository löschen",
"button": "Löschen",
"button": "Repository löschen",
"confirmAlert": {
"title": "Repository löschen",
"message": "Soll das Repository wirklich gelöscht werden?",

View File

@@ -45,8 +45,7 @@
"subtitle": "Erstellen eines neuen Benutzers"
},
"deleteUser": {
"subtitle": "Benutzer löschen",
"button": "Löschen",
"button": "Benutzer löschen",
"confirmAlert": {
"title": "Benutzer löschen",
"message": "Soll der Benutzer wirklich gelöscht werden?",

View File

@@ -11,7 +11,7 @@
"communityTitle": "Community Support",
"communityIconAlt": "Community Support Icon",
"communityInfo": "Contact the SCM-Manager support team for questions about SCM-Manager, to report bugs or to request features through the official channels.",
"communityButton": "Contact our team",
"communityButton": "Contact our Team",
"enterpriseTitle": "Enterprise Support",
"enterpriseIconAlt": "Enterprise Support Icon",
"enterpriseInfo": "You require support with the integration of SCM-Manager into your processes, with the customization of the tool or simply a service level agreement (SLA)?",
@@ -76,8 +76,8 @@
"createSubtitle": "Create Permission Role",
"editSubtitle": "Edit Permission Role",
"overview": {
"title": "Overview of all permission roles",
"noPermissionRoles": "No permission roles found.",
"title": "Overview of all Permission Roles",
"noPermissionRoles": "No Permission Roles found.",
"createButton": "Create Permission Role"
},
"editButton": "Edit",
@@ -91,10 +91,9 @@
"submit": "Save"
},
"delete": {
"button": "Delete",
"subtitle": "Delete permission role",
"button": "Delete Permission Role",
"confirmAlert": {
"title": "Delete permission role",
"title": "Delete Permission Role",
"message": "Do you really want to delete this permission role? All users will lose their corresponding permissions.",
"submit": "Yes",
"cancel": "No"

View File

@@ -60,8 +60,7 @@
}
},
"deleteGroup": {
"subtitle": "Delete Group",
"button": "Delete",
"button": "Delete Group",
"confirmAlert": {
"title": "Delete Group",
"message": "Do you really want to delete the group?",

View File

@@ -114,7 +114,7 @@
"size": "Size"
},
"noSources": "No sources found for this branch.",
"extension" : {
"extension": {
"notBound": "No extension bound."
}
},
@@ -166,8 +166,7 @@
}
},
"deleteRepo": {
"subtitle": "Delete Repository",
"button": "Delete",
"button": "Delete Repository",
"confirmAlert": {
"title": "Delete repository",
"message": "Do you really want to delete the repository?",
@@ -178,7 +177,7 @@
"diff": {
"changes": {
"add": "added",
"delete": "deleted",
"delete": "deleted",
"modify": "modified",
"rename": "renamed",
"copy": "copied"

View File

@@ -45,17 +45,16 @@
"subtitle": "Create a new user"
},
"deleteUser": {
"subtitle": "Delete User",
"button": "Delete",
"button": "Delete User",
"confirmAlert": {
"title": "Delete user",
"title": "Delete User",
"message": "Do you really want to delete the user?",
"submit": "Yes",
"cancel": "No"
}
},
"singleUserPassword": {
"button": "Set password",
"button": "Set Password",
"setPasswordSuccessful": "Password successfully set"
},
"userForm": {

View File

@@ -86,8 +86,7 @@
"submit": "Guardar"
},
"delete": {
"button": "Borrar",
"subtitle": "Eliminar el rol",
"button": "Eliminar el rol",
"confirmAlert": {
"title": "Eliminar el rol",
"message": "¿Realmente desea borrar el rol? Todos los usuarios de este rol perderń sus permisos.",

View File

@@ -60,8 +60,7 @@
}
},
"deleteGroup": {
"subtitle": "Borrar grupo",
"button": "Borrar",
"button": "Borrar grupo",
"confirmAlert": {
"title": "Borrar grupo",
"message": "¿Realmente desea borrar el grupo?",

View File

@@ -114,7 +114,7 @@
"size": "tamaño"
},
"noSources": "No se han encontrado fuentes para esta rama.",
"extension" : {
"extension": {
"notBound": "Sin extensión conectada."
}
},
@@ -166,8 +166,7 @@
}
},
"deleteRepo": {
"subtitle": "Borrar repositorio",
"button": "Borrar",
"button": "Borrar repositorio",
"confirmAlert": {
"title": "Borrar repositorio",
"message": "¿Realmente desea borrar el repositorio?",
@@ -178,7 +177,7 @@
"diff": {
"changes": {
"add": "añadido",
"delete": "borrado",
"delete": "borrado",
"modify": "modificado",
"rename": "renombrado",
"copy": "copiado"

View File

@@ -45,8 +45,7 @@
"subtitle": "Crear un nuevo usuario"
},
"deleteUser": {
"subtitle": "Borrar usuario",
"button": "Borrar",
"button": "Borrar usuario",
"confirmAlert": {
"title": "Borrar usuario",
"message": "¿Realmente desea borrar el usuario?",

View File

@@ -1,7 +1,7 @@
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { NamespaceStrategies, Config } from "@scm-manager/ui-types";
import { SubmitButton, Notification } from "@scm-manager/ui-components";
import { Level, SubmitButton, Notification } from "@scm-manager/ui-components";
import ProxySettings from "./ProxySettings";
import GeneralSettings from "./GeneralSettings";
import BaseUrlSettings from "./BaseUrlSettings";
@@ -151,10 +151,14 @@ class ConfigForm extends React.Component<Props, State> {
hasUpdatePermission={configUpdatePermission}
/>
<hr />
<SubmitButton
loading={loading}
label={t("config.form.submit")}
disabled={!configUpdatePermission || this.hasError() || !this.state.changed}
<Level
right={
<SubmitButton
loading={loading}
label={t("config.form.submit")}
disabled={!configUpdatePermission || this.hasError() || !this.state.changed}
/>
}
/>
</form>
);

View File

@@ -13,15 +13,13 @@ class AvailableVerbs extends React.Component<Props> {
let verbs = null;
if (role.verbs.length > 0) {
verbs = (
<tr>
<td className="is-paddingless">
<ul>
{role.verbs.map(verb => {
return <li>{t("verbs.repository." + verb + ".displayName")}</li>;
})}
</ul>
</td>
</tr>
<td className="is-paddingless">
<ul>
{role.verbs.map((verb, key) => {
return <li key={key}>{t("verbs.repository." + verb + ".displayName")}</li>;
})}
</ul>
</td>
);
}
return verbs;

View File

@@ -2,7 +2,7 @@ import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { RepositoryRole } from "@scm-manager/ui-types";
import { Button } from "@scm-manager/ui-components";
import { Level, Button } from "@scm-manager/ui-components";
import PermissionRoleDetailsTable from "./PermissionRoleDetailsTable";
type Props = WithTranslation & {
@@ -14,7 +14,12 @@ class PermissionRoleDetails extends React.Component<Props> {
renderEditButton() {
const { t, url } = this.props;
if (!!this.props.role._links.update) {
return <Button label={t("repositoryRole.editButton")} link={`${url}/edit`} color="primary" />;
return (
<>
<hr />
<Level right={<Button label={t("repositoryRole.editButton")} link={`${url}/edit`} color="primary" />} />
</>
);
}
return null;
}
@@ -25,7 +30,6 @@ class PermissionRoleDetails extends React.Component<Props> {
return (
<>
<PermissionRoleDetailsTable role={role} />
<hr />
{this.renderEditButton()}
<ExtensionPoint
name="repositoryRole.role-details.information"

View File

@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history";
import { RepositoryRole } from "@scm-manager/ui-types";
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { deleteRole, getDeleteRoleFailure, isDeleteRolePending } from "../modules/roles";
type Props = WithTranslation & {
@@ -64,13 +64,9 @@ class DeleteRepositoryRole extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t("repositoryRole.delete.subtitle")} />
<div className="columns">
<div className="column">
<ErrorNotification error={error} />
<DeleteButton label={t("repositoryRole.delete.button")} action={action} loading={loading} />
</div>
</div>
<hr />
<ErrorNotification error={error} />
<Level right={<DeleteButton label={t("repositoryRole.delete.button")} action={action} loading={loading} />} />
</>
);
}

View File

@@ -44,7 +44,6 @@ class EditRepositoryRole extends React.Component<Props> {
<>
<Subtitle subtitle={t("repositoryRole.editSubtitle")} />
<RepositoryRoleForm role={this.props.role} submitForm={role => this.updateRepositoryRole(role)} />
<hr />
<DeleteRepositoryRole role={this.props.role} />
</>
);

View File

@@ -2,10 +2,10 @@ import React from "react";
import { connect } from "react-redux";
import { WithTranslation, withTranslation } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
import { InputField, SubmitButton } from "@scm-manager/ui-components";
import { InputField, Level, SubmitButton } from "@scm-manager/ui-components";
import { getRepositoryRolesLink, getRepositoryVerbsLink } from "../../../modules/indexResource";
import { fetchAvailableVerbs, getFetchVerbsFailure, getVerbsFromState, isFetchVerbsPending } from "../modules/roles";
import PermissionCheckbox from "../../../repos/permissions/components/PermissionCheckbox";
import PermissionsWrapper from "../../../permissions/components/PermissionsWrapper";
type Props = WithTranslation & {
role?: RepositoryRole;
@@ -89,16 +89,9 @@ class RepositoryRoleForm extends React.Component<Props, State> {
const { loading, availableVerbs, t } = this.props;
const { role } = this.state;
const verbSelectBoxes = !availableVerbs
? null
: availableVerbs.map(verb => (
<PermissionCheckbox
key={verb}
name={verb}
checked={role.verbs.includes(verb)}
onChange={this.handleVerbChange}
/>
));
const selectedVerbs = availableVerbs
? availableVerbs.reduce((obj, verb) => ({ ...obj, [verb]: role.verbs.includes(verb) }), {})
: {};
return (
<form onSubmit={this.submit}>
@@ -111,16 +104,23 @@ class RepositoryRoleForm extends React.Component<Props, State> {
/>
<div className="field">
<label className="label">{t("repositoryRole.form.permissions")}</label>
{verbSelectBoxes}
<PermissionsWrapper
permissions={selectedVerbs}
onChange={this.handleVerbChange}
disabled={false}
role={true}
/>
</div>
<hr />
<SubmitButton loading={loading} label={t("repositoryRole.form.submit")} disabled={!this.isValid()} />
<Level
right={<SubmitButton loading={loading} label={t("repositoryRole.form.submit")} disabled={!this.isValid()} />}
/>
</form>
);
}
}
const mapStateToProps = (state) => {
const mapStateToProps = state => {
const loading = isFetchVerbsPending(state);
const error = getFetchVerbsFailure(state);
const verbsLink = getRepositoryVerbsLink(state);

View File

@@ -4,6 +4,7 @@ import {
InputField,
Notification,
PasswordConfirmation,
Level,
SubmitButton
} from "@scm-manager/ui-components";
import { WithTranslation, withTranslation } from "react-i18next";
@@ -124,11 +125,7 @@ class ChangeUserPassword extends React.Component<Props, State> {
passwordChanged={this.passwordChanged}
key={this.state.passwordChanged ? "changed" : "unchanged"}
/>
<div className="columns">
<div className="column">
<SubmitButton disabled={!this.isValid()} loading={loading} label={t("password.submit")} />
</div>
</div>
<Level right={<SubmitButton disabled={!this.isValid()} loading={loading} label={t("password.submit")} />} />
</form>
);
}

View File

@@ -8,9 +8,9 @@ import {
InputField,
SubmitButton,
Textarea,
Level,
Checkbox
} from "@scm-manager/ui-components";
import * as validator from "./groupValidation";
type Props = WithTranslation & {
@@ -154,7 +154,7 @@ class GroupForm extends React.Component<Props, State> {
/>
{this.renderExternalField(group)}
{this.renderMemberfields(group)}
<SubmitButton disabled={!this.isValid()} label={t("groupForm.submit")} loading={loading} />
<Level right={<SubmitButton disabled={!this.isValid()} label={t("groupForm.submit")} loading={loading} />} />
</form>
</>
);

View File

@@ -17,9 +17,9 @@ class GroupRow extends React.Component<Props> {
const { group, t } = this.props;
const to = `/group/${group.name}`;
const iconType = group.external ? (
<Icon title={t("group.external")} name="sign-out-alt fa-rotate-270" />
<Icon title={t("group.external")} name="globe-americas" />
) : (
<Icon title={t("group.internal")} name="sign-in-alt fa-rotate-90" />
<Icon title={t("group.internal")} name="home" />
);
return (

View File

@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history";
import { Group } from "@scm-manager/ui-types";
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { deleteGroup, getDeleteGroupFailure, isDeleteGroupPending } from "../modules/groups";
type Props = WithTranslation & {
@@ -64,13 +64,9 @@ export class DeleteGroup extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t("deleteGroup.subtitle")} />
<hr />
<ErrorNotification error={error} />
<div className="columns">
<div className="column">
<DeleteButton label={t("deleteGroup.button")} action={action} loading={loading} />
</div>
</div>
<Level right={<DeleteButton label={t("deleteGroup.button")} action={action} loading={loading} />} />
</>
);
}

View File

@@ -63,7 +63,6 @@ class EditGroup extends React.Component<Props> {
loading={loading}
loadUserSuggestions={this.loadUserAutocompletion}
/>
<hr />
<DeleteGroup group={group} />
</div>
);

View File

@@ -3,24 +3,34 @@ import { WithTranslation, withTranslation } from "react-i18next";
import { Checkbox } from "@scm-manager/ui-components";
type Props = WithTranslation & {
permission: string;
name: string;
checked: boolean;
onChange: (value: boolean, name: string) => void;
onChange?: (value: boolean, name?: string) => void;
disabled: boolean;
role?: boolean;
};
class PermissionCheckbox extends React.Component<Props> {
render() {
const { t, permission, checked, onChange, disabled } = this.props;
const key = permission.split(":").join(".");
const { name, checked, onChange, disabled, role, t } = this.props;
const key = name.split(":").join(".");
const label = role
? t("verbs.repository." + name + ".displayName")
: this.translateOrDefault("permissions." + key + ".displayName", key);
const helpText = role
? t("verbs.repository." + name + ".description")
: this.translateOrDefault("permissions." + key + ".description", t("permissions.unknown"));
return (
<Checkbox
name={permission}
label={this.translateOrDefault("permissions." + key + ".displayName", key)}
key={name}
name={name}
label={label}
helpText={helpText}
checked={checked}
onChange={onChange}
disabled={disabled}
helpText={this.translateOrDefault("permissions." + key + ".description", t("permissions.unknown"))}
/>
);
}

View File

@@ -0,0 +1,63 @@
import React from "react";
import classNames from "classnames";
import styled from "styled-components";
import PermissionCheckbox from "./PermissionCheckbox";
import { Loading } from "@scm-manager/ui-components";
type Props = {
permissions: {
[key: string]: boolean;
};
onChange: (value: boolean, name: string) => void;
disabled: boolean;
role?: boolean;
};
const StyledWrapper = styled.div`
padding-bottom: 0;
& .field .control {
width: 100%;
word-wrap: break-word;
}
`;
export default class PermissionsWrapper extends React.Component<Props> {
render() {
const { permissions, onChange, disabled, role } = this.props;
if (!permissions) {
return <Loading />;
}
const permissionArray = Object.keys(permissions);
return (
<div className="columns">
<StyledWrapper className={classNames("column", "is-half")}>
{permissionArray.slice(0, permissionArray.length / 2 + 1).map(p => (
<PermissionCheckbox
key={p}
name={p}
checked={permissions[p]}
onChange={onChange}
disabled={disabled}
role={role}
/>
))}
</StyledWrapper>
<StyledWrapper className={classNames("column", "is-half")}>
{permissionArray.slice(permissionArray.length / 2 + 1, permissionArray.length).map(p => (
<PermissionCheckbox
key={p}
name={p}
checked={permissions[p]}
onChange={onChange}
disabled={disabled}
role={role}
/>
))}
</StyledWrapper>
</div>
);
}
}

View File

@@ -1,13 +1,11 @@
import React from "react";
import { connect } from "react-redux";
import { WithTranslation, withTranslation } from "react-i18next";
import classNames from "classnames";
import styled from "styled-components";
import { Link } from "@scm-manager/ui-types";
import { Notification, ErrorNotification, SubmitButton } from "@scm-manager/ui-components";
import { Notification, ErrorNotification, SubmitButton, Level } from "@scm-manager/ui-components";
import { getLink } from "../../modules/indexResource";
import { loadPermissionsForEntity, setPermissions } from "./handlePermissions";
import PermissionCheckbox from "./PermissionCheckbox";
import PermissionsWrapper from "./PermissionsWrapper";
type Props = WithTranslation & {
availablePermissionLink: string;
@@ -25,15 +23,6 @@ type State = {
overwritePermissionsLink?: Link;
};
const PermissionsWrapper = styled.div`
padding-bottom: 0;
& .field .control {
width: 100%;
word-wrap: break-word;
}
`;
class SetPermissions extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
@@ -43,7 +32,6 @@ class SetPermissions extends React.Component<Props, State> {
loading: true,
permissionsChanged: false,
permissionsSubmitted: false,
modifiable: false,
overwritePermissionsLink: undefined
};
}
@@ -125,39 +113,23 @@ class SetPermissions extends React.Component<Props, State> {
<form onSubmit={this.submit}>
{message}
{this.renderPermissions()}
<SubmitButton disabled={!this.state.permissionsChanged} loading={loading} label={t("setPermissions.button")} />
<Level
right={
<SubmitButton
disabled={!this.state.permissionsChanged}
loading={loading}
label={t("setPermissions.button")}
/>
}
/>
</form>
);
}
renderPermissions = () => {
const { overwritePermissionsLink, permissions } = this.state;
const permissionArray = Object.keys(permissions);
return (
<div className="columns">
<PermissionsWrapper className={classNames("column", "is-half")}>
{permissionArray.slice(0, permissionArray.length / 2 + 1).map(p => (
<PermissionCheckbox
key={p}
permission={p}
checked={permissions[p]}
onChange={this.valueChanged}
disabled={!overwritePermissionsLink}
/>
))}
</PermissionsWrapper>
<PermissionsWrapper className={classNames("column", "is-half")}>
{permissionArray.slice(permissionArray.length / 2 + 1, permissionArray.length).map(p => (
<PermissionCheckbox
key={p}
permission={p}
checked={permissions[p]}
onChange={this.valueChanged}
disabled={!overwritePermissionsLink}
/>
))}
</PermissionsWrapper>
</div>
<PermissionsWrapper permissions={permissions} onChange={this.valueChanged} disabled={!overwritePermissionsLink} />
);
};

View File

@@ -2,7 +2,7 @@ import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { Repository, RepositoryType } from "@scm-manager/ui-types";
import { Subtitle, InputField, Select, SubmitButton, Textarea } from "@scm-manager/ui-components";
import { Subtitle, InputField, Select, Textarea, Level, SubmitButton } from "@scm-manager/ui-components";
import * as validator from "./repositoryValidation";
type Props = WithTranslation & {
@@ -52,9 +52,8 @@ class RepositoryForm extends React.Component<Props, State> {
}
}
isFalsy(value) {
isFalsy(value: string) {
return !value;
}
isValid = () => {
@@ -91,7 +90,7 @@ class RepositoryForm extends React.Component<Props, State> {
const disabled = !this.isModifiable() && !this.isCreateMode();
const submitButton = disabled ? null : (
<SubmitButton disabled={!this.isValid()} loading={loading} label={t("repositoryForm.submit")} />
<Level right={<SubmitButton disabled={!this.isValid()} loading={loading} label={t("repositoryForm.submit")} />} />
);
let subtitle = null;

View File

@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history";
import { Repository } from "@scm-manager/ui-types";
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { deleteRepo, getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos";
type Props = WithTranslation & {
@@ -65,13 +65,8 @@ class DeleteRepo extends React.Component<Props> {
return (
<>
<hr />
<Subtitle subtitle={t("deleteRepo.subtitle")} />
<ErrorNotification error={error} />
<div className="columns">
<div className="column">
<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />
</div>
</div>
<Level right={<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />} />
</>
);
}

View File

@@ -1,29 +0,0 @@
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { Checkbox } from "@scm-manager/ui-components";
type Props = WithTranslation & {
disabled: boolean;
name: string;
checked: boolean;
onChange?: (value: boolean, name?: string) => void;
};
class PermissionCheckbox extends React.Component<Props> {
render() {
const { t } = this.props;
return (
<Checkbox
key={this.props.name}
name={this.props.name}
helpText={t("verbs.repository." + this.props.name + ".description")}
label={t("verbs.repository." + this.props.name + ".displayName")}
checked={this.props.checked}
onChange={this.props.onChange}
disabled={this.props.disabled}
/>
);
}
}
export default withTranslation("plugins")(PermissionCheckbox);

View File

@@ -1,7 +1,7 @@
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { ButtonGroup, Button, SubmitButton, Modal } from "@scm-manager/ui-components";
import PermissionCheckbox from "../components/PermissionCheckbox";
import PermissionCheckbox from "../../../permissions/components/PermissionCheckbox";
type Props = WithTranslation & {
readOnly: boolean;
@@ -33,7 +33,14 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> {
const { verbs } = this.state;
const verbSelectBoxes = Object.entries(verbs).map(e => (
<PermissionCheckbox key={e[0]} disabled={readOnly} name={e[0]} checked={e[1]} onChange={this.handleChange} />
<PermissionCheckbox
key={e[0]}
name={e[0]}
checked={e[1]}
onChange={this.handleChange}
disabled={readOnly}
role={true}
/>
));
const submitButton = !readOnly ? <SubmitButton label={t("permission.advanced.dialog.submit")} /> : null;

View File

@@ -6,6 +6,7 @@ import {
GroupAutocomplete,
LabelWithHelpIcon,
Radio,
Level,
SubmitButton,
Subtitle,
UserAutocomplete
@@ -141,8 +142,8 @@ class CreatePermissionForm extends React.Component<Props, State> {
</div>
</div>
<div className="columns">
<div className="column is-three-fifths">{this.renderAutocompletionField()}</div>
<div className="column is-two-fifths">
<div className="column is-half">{this.renderAutocompletionField()}</div>
<div className="column is-half">
<div className="columns">
<div className="column is-narrow">
<RoleSelector
@@ -163,15 +164,15 @@ class CreatePermissionForm extends React.Component<Props, State> {
</div>
</div>
</div>
<div className="columns">
<div className="column">
<Level
right={
<SubmitButton
label={t("permission.add-permission.submit-button")}
loading={loading}
disabled={!this.state.valid || this.state.name === ""}
/>
</div>
</div>
}
/>
</form>
</>
);

View File

@@ -18,7 +18,7 @@ class FileButtonAddons extends React.Component<Props> {
};
color = (selected: boolean) => {
return selected ? "link is-selected" : null;
return selected ? "link is-selected" : "";
};
render() {
@@ -27,14 +27,14 @@ class FileButtonAddons extends React.Component<Props> {
return (
<ButtonAddons className={className}>
<div title={t("sources.content.sourcesButton")}>
<Button action={this.showSources} className="reduced" color={this.color(!historyIsSelected)}>
<Button action={this.showSources} color={this.color(!historyIsSelected)}>
<span className="icon">
<i className="fas fa-code" />
</span>
</Button>
</div>
<div title={t("sources.content.historyButton")}>
<Button action={this.showHistory} className="reduced" color={this.color(historyIsSelected)}>
<Button action={this.showHistory} color={this.color(historyIsSelected)}>
<span className="icon">
<i className="fas fa-history" />
</span>

View File

@@ -7,24 +7,25 @@ import { fetchSources, getFetchSourcesFailure, getSources, isFetchSourcesPending
import { connect } from "react-redux";
import { Loading, ErrorNotification } from "@scm-manager/ui-components";
import Notification from "@scm-manager/ui-components/src/Notification";
import {WithTranslation, withTranslation} from "react-i18next";
import { WithTranslation, withTranslation } from "react-i18next";
type Props = WithTranslation & RouteComponentProps & {
repository: Repository;
type Props = WithTranslation &
RouteComponentProps & {
repository: Repository;
// url params
extension: string;
revision?: string;
path?: string;
// url params
extension: string;
revision?: string;
path?: string;
// redux state
loading: boolean;
error?: Error | null;
sources?: File | null;
// redux state
loading: boolean;
error?: Error | null;
sources?: File | null;
// dispatch props
fetchSources: (repository: Repository, revision: string, path: string) => void;
};
// dispatch props
fetchSources: (repository: Repository, revision: string, path: string) => void;
};
const extensionPointName = "repos.sources.extensions";
@@ -32,7 +33,7 @@ class SourceExtensions extends React.Component<Props> {
componentDidMount() {
const { fetchSources, repository, revision, path } = this.props;
// TODO get typing right
fetchSources(repository,revision || "", path || "");
fetchSources(repository, revision || "", path || "");
}
render() {

View File

@@ -1,7 +1,7 @@
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import { User } from "@scm-manager/ui-types";
import { SubmitButton, Notification, ErrorNotification, PasswordConfirmation } from "@scm-manager/ui-components";
import { Level, SubmitButton, Notification, ErrorNotification, PasswordConfirmation } from "@scm-manager/ui-components";
import { setPassword } from "./setPassword";
type Props = WithTranslation & {
@@ -98,15 +98,15 @@ class SetUserPassword extends React.Component<Props, State> {
passwordChanged={this.passwordChanged}
key={this.state.passwordChanged ? "changed" : "unchanged"}
/>
<div className="columns">
<div className="column">
<Level
right={
<SubmitButton
disabled={!this.state.passwordValid}
loading={loading}
label={t("singleUserPassword.button")}
/>
</div>
</div>
}
/>
</form>
);
}

View File

@@ -6,6 +6,7 @@ import {
Checkbox,
InputField,
PasswordConfirmation,
Level,
SubmitButton,
validation as validator
} from "@scm-manager/ui-components";
@@ -166,11 +167,7 @@ class UserForm extends React.Component<Props, State> {
/>
</div>
</div>
<div className="columns">
<div className="column">
<SubmitButton disabled={!this.isValid()} loading={loading} label={t("userForm.button")} />
</div>
</div>
<Level right={<SubmitButton disabled={!this.isValid()} loading={loading} label={t("userForm.button")} />} />
</form>
</>
);

View File

@@ -4,7 +4,7 @@ import { withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { History } from "history";
import { User } from "@scm-manager/ui-types";
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { Level, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
import { deleteUser, getDeleteUserFailure, isDeleteUserPending } from "../modules/users";
type Props = WithTranslation & {
@@ -64,13 +64,9 @@ class DeleteUser extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t("deleteUser.subtitle")} />
<hr />
<ErrorNotification error={error} />
<div className="columns">
<div className="column">
<DeleteButton label={t("deleteUser.button")} action={action} loading={loading} />
</div>
</div>
<Level right={<DeleteButton label={t("deleteUser.button")} action={action} loading={loading} />} />
</>
);
}

View File

@@ -41,7 +41,6 @@ class EditUser extends React.Component<Props> {
<div>
<ErrorNotification error={error} />
<UserForm submitForm={user => this.modifyUser(user)} user={user} loading={loading} />
<hr />
<DeleteUser user={user} />
</div>
);