mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 14:35:45 +01:00
Merged in feature/improved-navi (pull request #152)
Feature/improved navi
This commit is contained in:
@@ -127,6 +127,7 @@ class RepositoryConfig extends React.Component<Props, State> {
|
|||||||
disabled={!this.state.selectedBranchName}
|
disabled={!this.state.selectedBranchName}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
<hr />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -27,14 +27,9 @@ binder.bind(
|
|||||||
);
|
);
|
||||||
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
||||||
|
|
||||||
cfgBinder.bindRepository(
|
binder.bind("repo-config.route", RepositoryConfig, gitPredicate);
|
||||||
"/configuration",
|
|
||||||
"scm-git-plugin.repo-config.link",
|
|
||||||
"configuration",
|
|
||||||
RepositoryConfig
|
|
||||||
);
|
|
||||||
// global config
|
|
||||||
|
|
||||||
|
// global config
|
||||||
cfgBinder.bindGlobal(
|
cfgBinder.bindGlobal(
|
||||||
"/git",
|
"/git",
|
||||||
"scm-git-plugin.config.link",
|
"scm-git-plugin.config.link",
|
||||||
|
|||||||
@@ -72,6 +72,33 @@ class ConfigurationBinder {
|
|||||||
binder.bind("repository.route", RepoRoute, repoPredicate);
|
binder.bind("repository.route", RepoRoute, repoPredicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindRepositorySetting(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any) {
|
||||||
|
|
||||||
|
// create predicate based on the link name of the current repository route
|
||||||
|
// if the linkname is not available, the navigation link and the route are not bound to the extension points
|
||||||
|
const repoPredicate = (props: Object) => {
|
||||||
|
return props.repository && props.repository._links && props.repository._links[linkName];
|
||||||
|
};
|
||||||
|
|
||||||
|
// create NavigationLink with translated label
|
||||||
|
const RepoNavLink = translate(this.i18nNamespace)(({t, url}) => {
|
||||||
|
return this.navLink(url + "/settings" + to, labelI18nKey, t);
|
||||||
|
});
|
||||||
|
|
||||||
|
// bind navigation link to extension point
|
||||||
|
binder.bind("repository.subnavigation", RepoNavLink, repoPredicate);
|
||||||
|
|
||||||
|
|
||||||
|
// route for global configuration, passes the current repository to component
|
||||||
|
const RepoRoute = ({url, repository, ...additionalProps}) => {
|
||||||
|
const link = repository._links[linkName].href;
|
||||||
|
return this.route(url + "/settings" + to, <RepositoryComponent repository={repository} link={link} {...additionalProps}/>);
|
||||||
|
};
|
||||||
|
|
||||||
|
// bind config route to extension point
|
||||||
|
binder.bind("repository.route", RepoRoute, repoPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ConfigurationBinder();
|
export default new ConfigurationBinder();
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class NavLink extends React.Component<Props> {
|
|||||||
|
|
||||||
let showIcon = null;
|
let showIcon = null;
|
||||||
if (icon) {
|
if (icon) {
|
||||||
showIcon = (<><i className={icon}></i>{" "}</>);
|
showIcon = (<><i className={icon} />{" "}</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//@flow
|
||||||
|
import * as React from "react";
|
||||||
|
import { Link, Route } from "react-router-dom";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
to: string,
|
||||||
|
icon?: string,
|
||||||
|
label: string,
|
||||||
|
activeOnlyWhenExact?: boolean,
|
||||||
|
activeWhenMatch?: (route: any) => boolean,
|
||||||
|
children?: React.Node
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubNavigation extends React.Component<Props> {
|
||||||
|
static defaultProps = {
|
||||||
|
activeOnlyWhenExact: false
|
||||||
|
};
|
||||||
|
|
||||||
|
isActive(route: any) {
|
||||||
|
const { activeWhenMatch } = this.props;
|
||||||
|
return route.match || (activeWhenMatch && activeWhenMatch(route));
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLink = (route: any) => {
|
||||||
|
const { to, icon, label } = this.props;
|
||||||
|
|
||||||
|
let defaultIcon = "fas fa-cog";
|
||||||
|
if (icon) {
|
||||||
|
defaultIcon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
let children = null;
|
||||||
|
if (this.isActive(route)) {
|
||||||
|
children = <ul className="sub-menu">{this.props.children}</ul>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<Link className={this.isActive(route) ? "is-active" : ""} to={to}>
|
||||||
|
<i className={defaultIcon} /> {label}
|
||||||
|
</Link>
|
||||||
|
{children}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { to, activeOnlyWhenExact } = this.props;
|
||||||
|
|
||||||
|
// removes last part of url
|
||||||
|
let parents = to.split("/");
|
||||||
|
parents.splice(-1, 1);
|
||||||
|
let parent = parents.join("/");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Route
|
||||||
|
path={parent}
|
||||||
|
exact={activeOnlyWhenExact}
|
||||||
|
children={this.renderLink}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubNavigation;
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
export { default as NavAction } from "./NavAction.js";
|
export { default as NavAction } from "./NavAction.js";
|
||||||
export { default as NavLink } from "./NavLink.js";
|
export { default as NavLink } from "./NavLink.js";
|
||||||
export { default as Navigation } from "./Navigation.js";
|
export { default as Navigation } from "./Navigation.js";
|
||||||
|
export { default as SubNavigation } from "./SubNavigation.js";
|
||||||
export { default as PrimaryNavigation } from "./PrimaryNavigation.js";
|
export { default as PrimaryNavigation } from "./PrimaryNavigation.js";
|
||||||
export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink.js";
|
export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink.js";
|
||||||
export { default as Section } from "./Section.js";
|
export { default as Section } from "./Section.js";
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ChangesetDiff extends React.Component<Props> {
|
|||||||
render() {
|
render() {
|
||||||
const { changeset, t } = this.props;
|
const { changeset, t } = this.props;
|
||||||
if (!this.isDiffSupported(changeset)) {
|
if (!this.isDiffSupported(changeset)) {
|
||||||
return <Notification type="danger">{t("changesets.diff.not-supported")}</Notification>;
|
return <Notification type="danger">{t("changesets.changeset.diffNotSupported")}</Notification>;
|
||||||
} else {
|
} else {
|
||||||
const url = this.createUrl(changeset);
|
const url = this.createUrl(changeset);
|
||||||
return <LoadingDiff url={url} />;
|
return <LoadingDiff url={url} />;
|
||||||
|
|||||||
@@ -43,8 +43,10 @@
|
|||||||
"previous": "Zurück"
|
"previous": "Zurück"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"navigation-label": "Navigation",
|
"navigationLabel": "Profil Navigation",
|
||||||
"actions-label": "Aktionen",
|
"informationNavLink": "Information",
|
||||||
|
"changePasswordNavLink": "Passwort ändern",
|
||||||
|
"settingsNavLink": "Einstellungen",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"displayName": "Anzeigename",
|
"displayName": "Anzeigename",
|
||||||
"mail": "E-Mail",
|
"mail": "E-Mail",
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"navigation-title": "Navigation"
|
"navigationLabel": "Einstellungs Navigation",
|
||||||
},
|
"globalConfigurationNavLink": "Globale Einstellungen",
|
||||||
"global-config": {
|
|
||||||
"title": "Einstellungen",
|
"title": "Einstellungen",
|
||||||
"navigation-label": "Globale Einstellungen",
|
"errorTitle": "Fehler",
|
||||||
"error-title": "Fehler",
|
"errorSubtitle": "Unbekannter Einstellungen Fehler"
|
||||||
"error-subtitle": "Unbekannter Einstellungen Fehler"
|
|
||||||
},
|
},
|
||||||
"config-form": {
|
"config-form": {
|
||||||
"submit": "Speichern",
|
"submit": "Speichern",
|
||||||
|
|||||||
@@ -11,13 +11,16 @@
|
|||||||
"title": "Gruppen",
|
"title": "Gruppen",
|
||||||
"subtitle": "Verwaltung der Gruppen"
|
"subtitle": "Verwaltung der Gruppen"
|
||||||
},
|
},
|
||||||
"single-group": {
|
"singleGroup": {
|
||||||
"error-title": "Fehler",
|
"errorTitle": "Fehler",
|
||||||
"error-subtitle": "Unbekannter Gruppen Fehler",
|
"errorSubtitle": "Unbekannter Gruppen Fehler",
|
||||||
"navigation-label": "Navigation",
|
"menu": {
|
||||||
"actions-label": "Aktionen",
|
"navigationLabel": "Gruppen Navigation",
|
||||||
"information-label": "Informationen",
|
"informationNavLink": "Informationen",
|
||||||
"back-label": "Zurück"
|
"settingsNavLink": "Einstellungen",
|
||||||
|
"generalNavLink": "Generell",
|
||||||
|
"setPermissionsNavLink": "Berechtigungen"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"add-group": {
|
"add-group": {
|
||||||
"title": "Gruppe erstellen",
|
"title": "Gruppe erstellen",
|
||||||
@@ -44,27 +47,25 @@
|
|||||||
"loading": "Suche...",
|
"loading": "Suche...",
|
||||||
"no-options": "Kein Vorschlag für Benutzername verfügbar"
|
"no-options": "Kein Vorschlag für Benutzername verfügbar"
|
||||||
},
|
},
|
||||||
|
"groupForm": {
|
||||||
"group-form": {
|
"subtitle": "Gruppe bearbeiten",
|
||||||
"submit": "Speichern",
|
"submit": "Speichern",
|
||||||
"name-error": "Name ist ungültig",
|
"nameError": "Name ist ungültig",
|
||||||
"description-error": "Beschreibung ist ungültig",
|
"descriptionError": "Beschreibung ist ungültig",
|
||||||
"help": {
|
"help": {
|
||||||
"nameHelpText": "Eindeutiger Name der Gruppe",
|
"nameHelpText": "Eindeutiger Name der Gruppe",
|
||||||
"descriptionHelpText": "Eine kurze Beschreibung der Gruppe",
|
"descriptionHelpText": "Eine kurze Beschreibung der Gruppe",
|
||||||
"memberHelpText": "Benutzername des Mitglieds der Gruppe"
|
"memberHelpText": "Benutzername des Mitglieds der Gruppe"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"delete-group-button": {
|
"deleteGroup": {
|
||||||
"label": "Löschen",
|
"subtitle": "Gruppe löschen",
|
||||||
"confirm-alert": {
|
"button": "Löschen",
|
||||||
|
"confirmAlert": {
|
||||||
"title": "Gruppe löschen",
|
"title": "Gruppe löschen",
|
||||||
"message": "Soll die Gruppe wirklich gelöscht werden?",
|
"message": "Soll die Gruppe wirklich gelöscht werden?",
|
||||||
"submit": "Ja",
|
"submit": "Ja",
|
||||||
"cancel": "Nein"
|
"cancel": "Nein"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"set-permissions-button": {
|
|
||||||
"label": "Berechtigungen ändern"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
{
|
{
|
||||||
"form": {
|
"setPermissions": {
|
||||||
"submit-button": {
|
"button": "Berechtigungen speichern",
|
||||||
"label": "Berechtigungen speichern"
|
"setPermissionsSuccessful": "Berechtigungen erfolgreich gespeichert"
|
||||||
},
|
|
||||||
"set-permissions-successful": "Berechtigungen erfolgreich gespeichert"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,41 +11,55 @@
|
|||||||
"name-invalid": "Der Name des Repository ist ungültig",
|
"name-invalid": "Der Name des Repository ist ungültig",
|
||||||
"contact-invalid": "Der Kontakt muss eine gültige E-Mail Adresse sein"
|
"contact-invalid": "Der Kontakt muss eine gültige E-Mail Adresse sein"
|
||||||
},
|
},
|
||||||
|
"help": {
|
||||||
|
"nameHelpText": "Der Name des Repository. Dieser wird Teil der URL des Repository sein.",
|
||||||
|
"typeHelpText": "Der Typ des Repository (Mercurial, Git oder Subversion).",
|
||||||
|
"contactHelpText": "E-Mail Adresse der Person, die für das Repository verantwortlich ist.",
|
||||||
|
"descriptionHelpText": "Eine kurze Beschreibung des Repository."
|
||||||
|
},
|
||||||
|
"repositoryRoot": {
|
||||||
|
"errorTitle": "Fehler",
|
||||||
|
"errorSubtitle": "Unbekannter Repository Fehler",
|
||||||
|
"menu": {
|
||||||
|
"navigationLabel": "Repository Navigation",
|
||||||
|
"informationNavLink": "Informationen",
|
||||||
|
"historyNavLink": "Commits",
|
||||||
|
"sourcesNavLink": "Sources",
|
||||||
|
"settingsNavLink": "Einstellungen",
|
||||||
|
"generalNavLink": "Generell",
|
||||||
|
"permissionsNavLink": "Berechtigungen"
|
||||||
|
}
|
||||||
|
},
|
||||||
"overview": {
|
"overview": {
|
||||||
"title": "Repositories",
|
"title": "Repositories",
|
||||||
"subtitle": "Übersicht aller verfügbaren Repositories",
|
"subtitle": "Übersicht aller verfügbaren Repositories",
|
||||||
"create-button": "Repository erstellen"
|
"createButton": "Repository erstellen"
|
||||||
},
|
|
||||||
"repository-root": {
|
|
||||||
"error-title": "Fehler",
|
|
||||||
"error-subtitle": "Unbekannter Repository Fehler",
|
|
||||||
"actions-label": "Aktionen",
|
|
||||||
"back-label": "Zurück",
|
|
||||||
"navigation-label": "Navigation",
|
|
||||||
"history": "Commits",
|
|
||||||
"information": "Informationen",
|
|
||||||
"permissions": "Berechtigungen",
|
|
||||||
"sources": "Sources"
|
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Repository erstellen",
|
"title": "Repository erstellen",
|
||||||
"subtitle": "Erstellen eines neuen Repository"
|
"subtitle": "Erstellen eines neuen Repository"
|
||||||
},
|
},
|
||||||
"repository-form": {
|
"changesets": {
|
||||||
"submit": "Speichern"
|
"errorTitle": "Fehler",
|
||||||
|
"errorSubtitle": "Changesets konnten nicht abgerufen werden",
|
||||||
|
"branchSelectorLabel": "Branches",
|
||||||
|
"changeset": {
|
||||||
|
"description": "Beschreibung",
|
||||||
|
"summary": "Changeset {{id}} wurde committet {{time}}",
|
||||||
|
"diffNotSupported": "Diff des Changesets wird von diesem Repositorytyp nicht unterstützt",
|
||||||
|
"id": "ID",
|
||||||
|
"contact": "Kontakt",
|
||||||
|
"date": "Datum"
|
||||||
},
|
},
|
||||||
"edit-nav-link": {
|
"author": {
|
||||||
"label": "Bearbeiten"
|
"name": "Autor",
|
||||||
},
|
"mail": "Mail"
|
||||||
"delete-nav-action": {
|
|
||||||
"label": "Löschen",
|
|
||||||
"confirm-alert": {
|
|
||||||
"title": "Repository löschen",
|
|
||||||
"message": "Soll das Repository wirklich gelöscht werden?",
|
|
||||||
"submit": "Ja",
|
|
||||||
"cancel": "Nein"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repositoryForm": {
|
||||||
|
"subtitle": "Repository bearbeiten",
|
||||||
|
"submit": "Speichern"
|
||||||
|
},
|
||||||
"sources": {
|
"sources": {
|
||||||
"file-tree": {
|
"file-tree": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@@ -65,27 +79,6 @@
|
|||||||
"size": "Größe"
|
"size": "Größe"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"changesets": {
|
|
||||||
"diff": {
|
|
||||||
"not-supported": "Diff des Changesets wird von diesem Repositorytyp nicht unterstützt"
|
|
||||||
},
|
|
||||||
"error-title": "Fehler",
|
|
||||||
"error-subtitle": "Changesets konnten nicht abgerufen werden",
|
|
||||||
"changeset": {
|
|
||||||
"id": "ID",
|
|
||||||
"description": "Beschreibung",
|
|
||||||
"contact": "Kontakt",
|
|
||||||
"date": "Datum",
|
|
||||||
"summary": "Changeset {{id}} wurde committet {{time}}"
|
|
||||||
},
|
|
||||||
"author": {
|
|
||||||
"name": "Autor",
|
|
||||||
"mail": "Mail"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"branch-selector": {
|
|
||||||
"label": "Branches"
|
|
||||||
},
|
|
||||||
"permission": {
|
"permission": {
|
||||||
"user": "Benutzer",
|
"user": "Benutzer",
|
||||||
"group": "Gruppe",
|
"group": "Gruppe",
|
||||||
@@ -98,7 +91,7 @@
|
|||||||
"user-permission": "Benutzerberechtigung",
|
"user-permission": "Benutzerberechtigung",
|
||||||
"edit-permission": {
|
"edit-permission": {
|
||||||
"delete-button": "Löschen",
|
"delete-button": "Löschen",
|
||||||
"save-button": "Änderungen Speichern"
|
"save-button": "Änderungen speichern"
|
||||||
},
|
},
|
||||||
"advanced-button": {
|
"advanced-button": {
|
||||||
"label": "Erweitert"
|
"label": "Erweitert"
|
||||||
@@ -138,10 +131,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"help": {
|
"deleteRepo": {
|
||||||
"nameHelpText": "Der Name des Repository. Dieser wird Teil der URL des Repository sein.",
|
"subtitle": "Repository löschen",
|
||||||
"typeHelpText": "Der Typ des Repository (Mercurial, Git oder Subversion).",
|
"button": "Löschen",
|
||||||
"contactHelpText": "E-Mail Adresse der Person, die für das Repository verantwortlich ist.",
|
"confirmAlert": {
|
||||||
"descriptionHelpText": "Eine kurze Beschreibung des Repository."
|
"title": "Repository löschen",
|
||||||
|
"message": "Soll das Repository wirklich gelöscht werden?",
|
||||||
|
"submit": "Ja",
|
||||||
|
"cancel": "Nein"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,59 +10,55 @@
|
|||||||
"creationDate": "Erstellt",
|
"creationDate": "Erstellt",
|
||||||
"lastModified": "Zuletzt bearbeitet"
|
"lastModified": "Zuletzt bearbeitet"
|
||||||
},
|
},
|
||||||
"users": {
|
|
||||||
"title": "Benutzer",
|
|
||||||
"subtitle": "Verwaltung der Benutzer"
|
|
||||||
},
|
|
||||||
"create-user-button": {
|
|
||||||
"label": "Benutzer erstellen"
|
|
||||||
},
|
|
||||||
"delete-user-button": {
|
|
||||||
"label": "Löschen",
|
|
||||||
"confirm-alert": {
|
|
||||||
"title": "Benutzer löschen",
|
|
||||||
"message": "Soll der Benutzer wirklich gelöscht werden?",
|
|
||||||
"submit": "Ja",
|
|
||||||
"cancel": "Nein"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"edit-user-button": {
|
|
||||||
"label": "Bearbeiten"
|
|
||||||
},
|
|
||||||
"set-password-button": {
|
|
||||||
"label": "Passwort ändern"
|
|
||||||
},
|
|
||||||
"set-permissions-button": {
|
|
||||||
"label": "Berechtigungen ändern"
|
|
||||||
},
|
|
||||||
"user-form": {
|
|
||||||
"submit": "Speichern"
|
|
||||||
},
|
|
||||||
"add-user": {
|
|
||||||
"title": "Benutzer erstellen",
|
|
||||||
"subtitle": "Erstellen eines neuen Benutzers"
|
|
||||||
},
|
|
||||||
"single-user": {
|
|
||||||
"error-title": "Fehler",
|
|
||||||
"error-subtitle": "Unbekannter Benutzer Fehler",
|
|
||||||
"navigation-label": "Navigation",
|
|
||||||
"actions-label": "Aktionen",
|
|
||||||
"information-label": "Informationen",
|
|
||||||
"back-label": "Zurück"
|
|
||||||
},
|
|
||||||
"validation": {
|
"validation": {
|
||||||
"mail-invalid": "Diese E-Mail ist ungültig",
|
"mail-invalid": "Diese E-Mail ist ungültig",
|
||||||
"name-invalid": "Dieser Name ist ungültig",
|
"name-invalid": "Dieser Name ist ungültig",
|
||||||
"displayname-invalid": "Dieser Anzeigename ist ungültig"
|
"displayname-invalid": "Dieser Anzeigename ist ungültig"
|
||||||
},
|
},
|
||||||
"password": {
|
|
||||||
"set-password-successful": "Das Passwort wurde erfolgreich gespeichert."
|
|
||||||
},
|
|
||||||
"help": {
|
"help": {
|
||||||
"usernameHelpText": "Einzigartiger Name des Benutzers",
|
"usernameHelpText": "Einzigartiger Name des Benutzers",
|
||||||
"displayNameHelpText": "Anzeigename des Benutzers",
|
"displayNameHelpText": "Anzeigename des Benutzers",
|
||||||
"mailHelpText": "E-Mail Adresse des Benutzers",
|
"mailHelpText": "E-Mail Adresse des Benutzers",
|
||||||
"adminHelpText": "Ein Administrator kann Repositories, Gruppen und Benutzer erstellen, bearbeiten und löschen.",
|
"adminHelpText": "Ein Administrator kann Repositories, Gruppen und Benutzer erstellen, bearbeiten und löschen.",
|
||||||
"activeHelpText": "Aktivierung oder Deaktivierung eines Benutzers"
|
"activeHelpText": "Aktivierung oder Deaktivierung eines Benutzers"
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"title": "Benutzer",
|
||||||
|
"subtitle": "Verwaltung der Benutzer",
|
||||||
|
"createButton": "Benutzer erstellen"
|
||||||
|
},
|
||||||
|
"singleUser": {
|
||||||
|
"errorTitle": "Fehler",
|
||||||
|
"errorSubtitle": "Unbekannter Benutzer Fehler",
|
||||||
|
"menu": {
|
||||||
|
"navigationLabel": "Benutzer Navigation",
|
||||||
|
"informationNavLink": "Informationen",
|
||||||
|
"settingsNavLink": "Einstellungen",
|
||||||
|
"generalNavLink": "Generell",
|
||||||
|
"setPasswordNavLink": "Passwort",
|
||||||
|
"setPermissionsNavLink": "Berechtigungen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"addUser": {
|
||||||
|
"title": "Benutzer erstellen",
|
||||||
|
"subtitle": "Erstellen eines neuen Benutzers"
|
||||||
|
},
|
||||||
|
"deleteUser": {
|
||||||
|
"subtitle": "Benutzer löschen",
|
||||||
|
"button": "Löschen",
|
||||||
|
"confirmAlert": {
|
||||||
|
"title": "Benutzer löschen",
|
||||||
|
"message": "Soll der Benutzer wirklich gelöscht werden?",
|
||||||
|
"submit": "Ja",
|
||||||
|
"cancel": "Nein"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"singleUserPassword": {
|
||||||
|
"button": "Passwort setzen",
|
||||||
|
"setPasswordSuccessful": "Das Passwort wurde erfolgreich gespeichert."
|
||||||
|
},
|
||||||
|
"userForm": {
|
||||||
|
"subtitle": "Benutzer bearbeiten",
|
||||||
|
"button": "Speichern"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,10 @@
|
|||||||
"previous": "Previous"
|
"previous": "Previous"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"navigation-label": "Navigation",
|
"navigationLabel": "Profile Navigation",
|
||||||
"actions-label": "Actions",
|
"informationNavLink": "Information",
|
||||||
|
"changePasswordNavLink": "Change password",
|
||||||
|
"settingsNavLink": "Settings",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"displayName": "Display Name",
|
"displayName": "Display Name",
|
||||||
"mail": "E-Mail",
|
"mail": "E-Mail",
|
||||||
@@ -67,6 +69,6 @@
|
|||||||
"passwordInvalid": "Password has to be between 6 and 32 characters",
|
"passwordInvalid": "Password has to be between 6 and 32 characters",
|
||||||
"passwordConfirmFailed": "Passwords have to be identical",
|
"passwordConfirmFailed": "Passwords have to be identical",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"changedSuccessfully": "Password successfully changed"
|
"changedSuccessfully": "Password changed successfully"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"navigation-title": "Navigation"
|
"navigationLabel": "Configuration Navigation",
|
||||||
},
|
"globalConfigurationNavLink": "Global Configuration",
|
||||||
"global-config": {
|
|
||||||
"title": "Configuration",
|
"title": "Configuration",
|
||||||
"navigation-label": "Global Configuration",
|
"errorTitle": "Error",
|
||||||
"error-title": "Error",
|
"errorSubtitle": "Unknown Config Error"
|
||||||
"error-subtitle": "Unknown Config Error"
|
|
||||||
},
|
},
|
||||||
"config-form": {
|
"config-form": {
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
|
|||||||
@@ -11,13 +11,16 @@
|
|||||||
"title": "Groups",
|
"title": "Groups",
|
||||||
"subtitle": "Create, read, update and delete groups"
|
"subtitle": "Create, read, update and delete groups"
|
||||||
},
|
},
|
||||||
"single-group": {
|
"singleGroup": {
|
||||||
"error-title": "Error",
|
"errorTitle": "Error",
|
||||||
"error-subtitle": "Unknown group error",
|
"errorSubtitle": "Unknown group error",
|
||||||
"navigation-label": "Navigation",
|
"menu": {
|
||||||
"actions-label": "Actions",
|
"navigationLabel": "Group Navigation",
|
||||||
"information-label": "Information",
|
"informationNavLink": "Information",
|
||||||
"back-label": "Back"
|
"settingsNavLink": "Settings",
|
||||||
|
"generalNavLink": "General",
|
||||||
|
"setPermissionsNavLink": "Permissions"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"add-group": {
|
"add-group": {
|
||||||
"title": "Create Group",
|
"title": "Create Group",
|
||||||
@@ -44,27 +47,25 @@
|
|||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"no-options": "No suggestion available"
|
"no-options": "No suggestion available"
|
||||||
},
|
},
|
||||||
|
"groupForm": {
|
||||||
"group-form": {
|
"subtitle": "Edit Group",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"name-error": "Group name is invalid",
|
"nameError": "Group name is invalid",
|
||||||
"description-error": "Description is invalid",
|
"descriptionError": "Description is invalid",
|
||||||
"help": {
|
"help": {
|
||||||
"nameHelpText": "Unique name of the group",
|
"nameHelpText": "Unique name of the group",
|
||||||
"descriptionHelpText": "A short description of the group",
|
"descriptionHelpText": "A short description of the group",
|
||||||
"memberHelpText": "Usernames of the group members"
|
"memberHelpText": "Usernames of the group members"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"delete-group-button": {
|
"deleteGroup": {
|
||||||
"label": "Delete",
|
"subtitle": "Delete Group",
|
||||||
"confirm-alert": {
|
"button": "Delete",
|
||||||
|
"confirmAlert": {
|
||||||
"title": "Delete Group",
|
"title": "Delete Group",
|
||||||
"message": "Do you really want to delete the group?",
|
"message": "Do you really want to delete the group?",
|
||||||
"submit": "Yes",
|
"submit": "Yes",
|
||||||
"cancel": "No"
|
"cancel": "No"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"set-permissions-button": {
|
|
||||||
"label": "Set Permissions"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
{
|
{
|
||||||
"form": {
|
"setPermissions": {
|
||||||
"submit-button": {
|
"button": "Set permissions",
|
||||||
"label": "Set Permissions"
|
"setPermissionsSuccessful": "Permissions set successfully"
|
||||||
},
|
|
||||||
"set-permissions-successful": "Permissions set successfully"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,41 +11,55 @@
|
|||||||
"name-invalid": "The repository name is invalid",
|
"name-invalid": "The repository name is invalid",
|
||||||
"contact-invalid": "Contact must be a valid mail address"
|
"contact-invalid": "Contact must be a valid mail address"
|
||||||
},
|
},
|
||||||
|
"help": {
|
||||||
|
"nameHelpText": "The name of the repository. This name will be part of the repository url.",
|
||||||
|
"typeHelpText": "The type of the repository (e.g. Mercurial, Git or Subversion).",
|
||||||
|
"contactHelpText": "Email address of the person who is responsible for this repository.",
|
||||||
|
"descriptionHelpText": "A short description of the repository."
|
||||||
|
},
|
||||||
|
"repositoryRoot": {
|
||||||
|
"errorTitle": "Error",
|
||||||
|
"errorSubtitle": "Unknown repository error",
|
||||||
|
"menu": {
|
||||||
|
"navigationLabel": "Repository Navigation",
|
||||||
|
"informationNavLink": "Information",
|
||||||
|
"historyNavLink": "Commits",
|
||||||
|
"sourcesNavLink": "Sources",
|
||||||
|
"settingsNavLink": "Settings",
|
||||||
|
"generalNavLink": "General",
|
||||||
|
"permissionsNavLink": "Permissions"
|
||||||
|
}
|
||||||
|
},
|
||||||
"overview": {
|
"overview": {
|
||||||
"title": "Repositories",
|
"title": "Repositories",
|
||||||
"subtitle": "Overview of available repositories",
|
"subtitle": "Overview of available repositories",
|
||||||
"create-button": "Create Repository"
|
"createButton": "Create Repository"
|
||||||
},
|
|
||||||
"repository-root": {
|
|
||||||
"error-title": "Error",
|
|
||||||
"error-subtitle": "Unknown repository error",
|
|
||||||
"actions-label": "Actions",
|
|
||||||
"back-label": "Back",
|
|
||||||
"navigation-label": "Navigation",
|
|
||||||
"history": "Commits",
|
|
||||||
"information": "Information",
|
|
||||||
"permissions": "Permissions",
|
|
||||||
"sources": "Sources"
|
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Create Repository",
|
"title": "Create Repository",
|
||||||
"subtitle": "Create a new repository"
|
"subtitle": "Create a new repository"
|
||||||
},
|
},
|
||||||
"repository-form": {
|
"changesets": {
|
||||||
"submit": "Save"
|
"errorTitle": "Error",
|
||||||
|
"errorSubtitle": "Could not fetch changesets",
|
||||||
|
"branchSelectorLabel": "Branches",
|
||||||
|
"changeset": {
|
||||||
|
"description": "Description",
|
||||||
|
"summary": "Changeset {{id}} was committed {{time}}",
|
||||||
|
"diffNotSupported": "Diff of changesets is not supported by the type of repository",
|
||||||
|
"id": "ID",
|
||||||
|
"contact": "Contact",
|
||||||
|
"date": "Date"
|
||||||
},
|
},
|
||||||
"edit-nav-link": {
|
"author": {
|
||||||
"label": "Edit"
|
"name": "Author",
|
||||||
},
|
"mail": "Mail"
|
||||||
"delete-nav-action": {
|
|
||||||
"label": "Delete",
|
|
||||||
"confirm-alert": {
|
|
||||||
"title": "Delete Repository",
|
|
||||||
"message": "Do you really want to delete the repository?",
|
|
||||||
"submit": "Yes",
|
|
||||||
"cancel": "No"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repositoryForm": {
|
||||||
|
"subtitle": "Edit Repository",
|
||||||
|
"submit": "Save"
|
||||||
|
},
|
||||||
"sources": {
|
"sources": {
|
||||||
"file-tree": {
|
"file-tree": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
@@ -65,27 +79,6 @@
|
|||||||
"size": "Size"
|
"size": "Size"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"changesets": {
|
|
||||||
"diff": {
|
|
||||||
"not-supported": "Diff of changesets is not supported by the type of repository"
|
|
||||||
},
|
|
||||||
"error-title": "Error",
|
|
||||||
"error-subtitle": "Could not fetch changesets",
|
|
||||||
"changeset": {
|
|
||||||
"id": "ID",
|
|
||||||
"description": "Description",
|
|
||||||
"contact": "Contact",
|
|
||||||
"date": "Date",
|
|
||||||
"summary": "Changeset {{id}} was committed {{time}}"
|
|
||||||
},
|
|
||||||
"author": {
|
|
||||||
"name": "Author",
|
|
||||||
"mail": "Mail"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"branch-selector": {
|
|
||||||
"label": "Branches"
|
|
||||||
},
|
|
||||||
"permission": {
|
"permission": {
|
||||||
"user": "User",
|
"user": "User",
|
||||||
"group": "Group",
|
"group": "Group",
|
||||||
@@ -106,7 +99,7 @@
|
|||||||
"delete-permission-button": {
|
"delete-permission-button": {
|
||||||
"label": "Delete",
|
"label": "Delete",
|
||||||
"confirm-alert": {
|
"confirm-alert": {
|
||||||
"title": "Delete Permission",
|
"title": "Delete permission",
|
||||||
"message": "Do you really want to delete the permission?",
|
"message": "Do you really want to delete the permission?",
|
||||||
"submit": "Yes",
|
"submit": "Yes",
|
||||||
"cancel": "No"
|
"cancel": "No"
|
||||||
@@ -138,10 +131,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"help": {
|
"deleteRepo": {
|
||||||
"nameHelpText": "The name of the repository. This name will be part of the repository url.",
|
"subtitle": "Delete Repository",
|
||||||
"typeHelpText": "The type of the repository (e.g. Mercurial, Git or Subversion).",
|
"button": "Delete",
|
||||||
"contactHelpText": "Email address of the person who is responsible for this repository.",
|
"confirmAlert": {
|
||||||
"descriptionHelpText": "A short description of the repository."
|
"title": "Delete repository",
|
||||||
|
"message": "Do you really want to delete the repository?",
|
||||||
|
"submit": "Yes",
|
||||||
|
"cancel": "No"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,59 +10,55 @@
|
|||||||
"creationDate": "Creation Date",
|
"creationDate": "Creation Date",
|
||||||
"lastModified": "Last Modified"
|
"lastModified": "Last Modified"
|
||||||
},
|
},
|
||||||
"users": {
|
|
||||||
"title": "Users",
|
|
||||||
"subtitle": "Create, read, update and delete users"
|
|
||||||
},
|
|
||||||
"create-user-button": {
|
|
||||||
"label": "Create User"
|
|
||||||
},
|
|
||||||
"delete-user-button": {
|
|
||||||
"label": "Delete",
|
|
||||||
"confirm-alert": {
|
|
||||||
"title": "Delete User",
|
|
||||||
"message": "Do you really want to delete the user?",
|
|
||||||
"submit": "Yes",
|
|
||||||
"cancel": "No"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"edit-user-button": {
|
|
||||||
"label": "Edit"
|
|
||||||
},
|
|
||||||
"set-password-button": {
|
|
||||||
"label": "Set Password"
|
|
||||||
},
|
|
||||||
"set-permissions-button": {
|
|
||||||
"label": "Set Permissions"
|
|
||||||
},
|
|
||||||
"user-form": {
|
|
||||||
"submit": "Submit"
|
|
||||||
},
|
|
||||||
"add-user": {
|
|
||||||
"title": "Create User",
|
|
||||||
"subtitle": "Create a new user"
|
|
||||||
},
|
|
||||||
"single-user": {
|
|
||||||
"error-title": "Error",
|
|
||||||
"error-subtitle": "Unknown user error",
|
|
||||||
"navigation-label": "Navigation",
|
|
||||||
"actions-label": "Actions",
|
|
||||||
"information-label": "Information",
|
|
||||||
"back-label": "Back"
|
|
||||||
},
|
|
||||||
"validation": {
|
"validation": {
|
||||||
"mail-invalid": "This email is invalid",
|
"mail-invalid": "This email is invalid",
|
||||||
"name-invalid": "This name is invalid",
|
"name-invalid": "This name is invalid",
|
||||||
"displayname-invalid": "This displayname is invalid"
|
"displayname-invalid": "This displayname is invalid"
|
||||||
},
|
},
|
||||||
"password": {
|
|
||||||
"set-password-successful": "Password successfully set"
|
|
||||||
},
|
|
||||||
"help": {
|
"help": {
|
||||||
"usernameHelpText": "Unique name of the user.",
|
"usernameHelpText": "Unique name of the user.",
|
||||||
"displayNameHelpText": "Display name of the user.",
|
"displayNameHelpText": "Display name of the user.",
|
||||||
"mailHelpText": "Email address of the user.",
|
"mailHelpText": "Email address of the user.",
|
||||||
"adminHelpText": "An administrator is able to create, modify and delete repositories, groups and users.",
|
"adminHelpText": "An administrator is able to create, modify and delete repositories, groups and users.",
|
||||||
"activeHelpText": "Activate or deactivate the user."
|
"activeHelpText": "Activate or deactivate the user."
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"title": "Users",
|
||||||
|
"subtitle": "Create, read, update and delete users",
|
||||||
|
"createButton": "Create User"
|
||||||
|
},
|
||||||
|
"singleUser": {
|
||||||
|
"errorTitle": "Error",
|
||||||
|
"errorSubtitle": "Unknown user error",
|
||||||
|
"menu": {
|
||||||
|
"navigationLabel": "User Navigation",
|
||||||
|
"informationNavLink": "Information",
|
||||||
|
"settingsNavLink": "Settings",
|
||||||
|
"generalNavLink": "General",
|
||||||
|
"setPasswordNavLink": "Password",
|
||||||
|
"setPermissionsNavLink": "Permissions"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"addUser": {
|
||||||
|
"title": "Create User",
|
||||||
|
"subtitle": "Create a new user"
|
||||||
|
},
|
||||||
|
"deleteUser": {
|
||||||
|
"subtitle": "Delete User",
|
||||||
|
"button": "Delete",
|
||||||
|
"confirmAlert": {
|
||||||
|
"title": "Delete user",
|
||||||
|
"message": "Do you really want to delete the user?",
|
||||||
|
"submit": "Yes",
|
||||||
|
"cancel": "No"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"singleUserPassword": {
|
||||||
|
"button": "Set password",
|
||||||
|
"setPasswordSuccessful": "Password successfully set"
|
||||||
|
},
|
||||||
|
"userForm": {
|
||||||
|
"subtitle": "Edit User",
|
||||||
|
"button": "Submit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ 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 = {
|
||||||
@@ -47,19 +47,21 @@ class Config extends React.Component<Props> {
|
|||||||
<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 is-one-quarter">
|
<div className="column is-one-quarter">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<Section label={t("config.navigation-title")}>
|
<Section label={t("config.navigationLabel")}>
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`${url}`}
|
to={`${url}`}
|
||||||
label={t("global-config.navigation-label")}
|
label={t("config.globalConfigurationNavLink")}
|
||||||
/>
|
/>
|
||||||
<ExtensionPoint name="config.navigation"
|
<ExtensionPoint
|
||||||
|
name="config.navigation"
|
||||||
props={extensionProps}
|
props={extensionProps}
|
||||||
renderAll={true}
|
renderAll={true}
|
||||||
/>
|
/>
|
||||||
@@ -83,4 +85,3 @@ export default compose(
|
|||||||
connect(mapStateToProps),
|
connect(mapStateToProps),
|
||||||
translate("config")
|
translate("config")
|
||||||
)(Config);
|
)(Config);
|
||||||
|
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ class GlobalConfig extends React.Component<Props, State> {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<ErrorPage
|
<ErrorPage
|
||||||
title={t("global-config.error-title")}
|
title={t("config.errorTitle")}
|
||||||
subtitle={t("global-config.error-subtitle")}
|
subtitle={t("config.errorSubtitle")}
|
||||||
error={error}
|
error={error}
|
||||||
configUpdatePermission={configUpdatePermission}
|
configUpdatePermission={configUpdatePermission}
|
||||||
/>
|
/>
|
||||||
@@ -91,7 +91,7 @@ class GlobalConfig extends React.Component<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Title title={t("global-config.title")} />
|
<Title title={t("config.title")} />
|
||||||
{this.renderConfigChangedNotification()}
|
{this.renderConfigChangedNotification()}
|
||||||
<ConfigForm
|
<ConfigForm
|
||||||
submitForm={config => this.modifyConfig(config)}
|
submitForm={config => this.modifyConfig(config)}
|
||||||
|
|||||||
@@ -12,11 +12,13 @@ import {
|
|||||||
ErrorPage,
|
ErrorPage,
|
||||||
Page,
|
Page,
|
||||||
Navigation,
|
Navigation,
|
||||||
|
SubNavigation,
|
||||||
Section,
|
Section,
|
||||||
NavLink
|
NavLink
|
||||||
} from "@scm-manager/ui-components";
|
} from "@scm-manager/ui-components";
|
||||||
import ChangeUserPassword from "./ChangeUserPassword";
|
import ChangeUserPassword from "./ChangeUserPassword";
|
||||||
import ProfileInfo from "./ProfileInfo";
|
import ProfileInfo from "./ProfileInfo";
|
||||||
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
me: Me,
|
me: Me,
|
||||||
@@ -57,26 +59,43 @@ class Profile extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extensionProps = {
|
||||||
|
me,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title={me.displayName}>
|
<Page title={me.displayName}>
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column is-three-quarters">
|
<div className="column is-three-quarters">
|
||||||
<Route path={url} exact render={() => <ProfileInfo me={me} />} />
|
<Route path={url} exact render={() => <ProfileInfo me={me} />} />
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/password`}
|
path={`${url}/settings/password`}
|
||||||
render={() => <ChangeUserPassword me={me} />}
|
render={() => <ChangeUserPassword me={me} />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<Section label={t("profile.navigation-label")}>
|
<Section label={t("profile.navigationLabel")}>
|
||||||
<NavLink to={`${url}`} icon="fas fa-info-circle" label={t("profile.information")} />
|
|
||||||
</Section>
|
|
||||||
<Section label={t("profile.actions-label")}>
|
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`${url}/password`}
|
to={`${url}`}
|
||||||
label={t("profile.change-password")}
|
icon="fas fa-info-circle"
|
||||||
|
label={t("profile.informationNavLink")}
|
||||||
/>
|
/>
|
||||||
|
<SubNavigation
|
||||||
|
to={`${url}/settings/password`}
|
||||||
|
label={t("profile.settingsNavLink")}
|
||||||
|
>
|
||||||
|
<NavLink
|
||||||
|
to={`${url}/settings/password`}
|
||||||
|
label={t("profile.changePasswordNavLink")}
|
||||||
|
/>
|
||||||
|
<ExtensionPoint
|
||||||
|
name="profile.subnavigation"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
|
</SubNavigation>
|
||||||
</Section>
|
</Section>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,16 +42,6 @@ class ProfileInfo extends React.Component<Props, State> {
|
|||||||
<MailLink address={me.mail} />
|
<MailLink address={me.mail} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td className="has-text-weight-semibold">{t("profile.groups")}</td>
|
|
||||||
<td className="content">
|
|
||||||
<ul>
|
|
||||||
{me.groups.map((group) => {
|
|
||||||
return <li>{group}</li>;
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
Subtitle,
|
||||||
AutocompleteAddEntryToTableField,
|
AutocompleteAddEntryToTableField,
|
||||||
LabelWithHelpIcon,
|
LabelWithHelpIcon,
|
||||||
MemberNameTable,
|
MemberNameTable,
|
||||||
@@ -71,36 +72,43 @@ class GroupForm extends React.Component<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t, loading } = this.props;
|
const { loading, t } = this.props;
|
||||||
const { group } = this.state;
|
const { group } = this.state;
|
||||||
let nameField = null;
|
let nameField = null;
|
||||||
|
let subtitle = null;
|
||||||
if (!this.props.group) {
|
if (!this.props.group) {
|
||||||
|
// create new group
|
||||||
nameField = (
|
nameField = (
|
||||||
<InputField
|
<InputField
|
||||||
label={t("group.name")}
|
label={t("group.name")}
|
||||||
errorMessage={t("group-form.name-error")}
|
errorMessage={t("groupForm.nameError")}
|
||||||
onChange={this.handleGroupNameChange}
|
onChange={this.handleGroupNameChange}
|
||||||
value={group.name}
|
value={group.name}
|
||||||
validationError={this.state.nameValidationError}
|
validationError={this.state.nameValidationError}
|
||||||
helpText={t("group-form.help.nameHelpText")}
|
helpText={t("groupForm.help.nameHelpText")}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// edit existing group
|
||||||
|
subtitle = <Subtitle subtitle={t("groupForm.subtitle")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{subtitle}
|
||||||
<form onSubmit={this.submit}>
|
<form onSubmit={this.submit}>
|
||||||
{nameField}
|
{nameField}
|
||||||
<Textarea
|
<Textarea
|
||||||
label={t("group.description")}
|
label={t("group.description")}
|
||||||
errorMessage={t("group-form.description-error")}
|
errorMessage={t("groupForm.descriptionError")}
|
||||||
onChange={this.handleDescriptionChange}
|
onChange={this.handleDescriptionChange}
|
||||||
value={group.description}
|
value={group.description}
|
||||||
validationError={false}
|
validationError={false}
|
||||||
helpText={t("group-form.help.descriptionHelpText")}
|
helpText={t("groupForm.help.descriptionHelpText")}
|
||||||
/>
|
/>
|
||||||
<LabelWithHelpIcon
|
<LabelWithHelpIcon
|
||||||
label={t("group.members")}
|
label={t("group.members")}
|
||||||
helpText={t("group-form.help.memberHelpText")}
|
helpText={t("groupForm.help.memberHelpText")}
|
||||||
/>
|
/>
|
||||||
<MemberNameTable
|
<MemberNameTable
|
||||||
members={group.members}
|
members={group.members}
|
||||||
@@ -120,10 +128,11 @@ class GroupForm extends React.Component<Props, State> {
|
|||||||
/>
|
/>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={!this.isValid()}
|
disabled={!this.isValid()}
|
||||||
label={t("group-form.submit")}
|
label={t("groupForm.submit")}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
// @flow
|
|
||||||
import React from "react";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import type { Group } from "@scm-manager/ui-types";
|
|
||||||
import { NavAction, confirmAlert } from "@scm-manager/ui-components";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
group: Group,
|
|
||||||
confirmDialog?: boolean,
|
|
||||||
t: string => string,
|
|
||||||
deleteGroup: (group: Group) => void
|
|
||||||
};
|
|
||||||
|
|
||||||
export class DeleteGroupNavLink extends React.Component<Props> {
|
|
||||||
static defaultProps = {
|
|
||||||
confirmDialog: true
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteGroup = () => {
|
|
||||||
this.props.deleteGroup(this.props.group);
|
|
||||||
};
|
|
||||||
|
|
||||||
confirmDelete = () => {
|
|
||||||
const { t } = this.props;
|
|
||||||
confirmAlert({
|
|
||||||
title: t("delete-group-button.confirm-alert.title"),
|
|
||||||
message: t("delete-group-button.confirm-alert.message"),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: t("delete-group-button.confirm-alert.submit"),
|
|
||||||
onClick: () => this.deleteGroup(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("delete-group-button.confirm-alert.cancel"),
|
|
||||||
onClick: () => null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
isDeletable = () => {
|
|
||||||
return this.props.group._links.delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { confirmDialog, t } = this.props;
|
|
||||||
const action = confirmDialog ? this.confirmDelete : this.deleteGroup;
|
|
||||||
|
|
||||||
if (!this.isDeletable()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return <NavAction icon="fas fa-times" label={t("delete-group-button.label")} action={action} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate("groups")(DeleteGroupNavLink);
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { mount, shallow } from "enzyme";
|
|
||||||
import "../../../tests/enzyme";
|
|
||||||
import "../../../tests/i18n";
|
|
||||||
import DeleteGroupNavLink from "./DeleteGroupNavLink";
|
|
||||||
|
|
||||||
import { confirmAlert } from "@scm-manager/ui-components";
|
|
||||||
jest.mock("@scm-manager/ui-components", () => ({
|
|
||||||
confirmAlert: jest.fn(),
|
|
||||||
NavAction: require.requireActual("@scm-manager/ui-components").NavAction
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("DeleteGroupNavLink", () => {
|
|
||||||
it("should render nothing, if the delete link is missing", () => {
|
|
||||||
const group = {
|
|
||||||
_links: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = shallow(
|
|
||||||
<DeleteGroupNavLink group={group} deleteGroup={() => {}} />
|
|
||||||
);
|
|
||||||
expect(navLink.text()).toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the navLink", () => {
|
|
||||||
const group = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/groups"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteGroupNavLink group={group} deleteGroup={() => {}} />
|
|
||||||
);
|
|
||||||
expect(navLink.text()).not.toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should open the confirm dialog on navLink click", () => {
|
|
||||||
const group = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/groups"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteGroupNavLink group={group} deleteGroup={() => {}} />
|
|
||||||
);
|
|
||||||
navLink.find("a").simulate("click");
|
|
||||||
|
|
||||||
expect(confirmAlert.mock.calls.length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the delete group function with delete url", () => {
|
|
||||||
const group = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/groups"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let calledUrl = null;
|
|
||||||
function capture(group) {
|
|
||||||
calledUrl = group._links.delete.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteGroupNavLink
|
|
||||||
group={group}
|
|
||||||
confirmDialog={false}
|
|
||||||
deleteGroup={capture}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
navLink.find("a").simulate("click");
|
|
||||||
|
|
||||||
expect(calledUrl).toBe("/groups");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,29 +1,28 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import type { Group } from "@scm-manager/ui-types";
|
||||||
import { NavLink } from "@scm-manager/ui-components";
|
import { NavLink } from "@scm-manager/ui-components";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import type { Group } from "@scm-manager/ui-types";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
t: string => string,
|
group: Group,
|
||||||
editUrl: string,
|
editUrl: string,
|
||||||
group: Group
|
t: string => string
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {};
|
class EditGroupNavLink extends React.Component<Props> {
|
||||||
|
|
||||||
class EditGroupNavLink extends React.Component<Props, State> {
|
|
||||||
render() {
|
|
||||||
const { t, editUrl } = this.props;
|
|
||||||
if (!this.isEditable()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return <NavLink to={editUrl} icon="fas fa-cog" label={t("edit-group-button.label")} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
isEditable = () => {
|
isEditable = () => {
|
||||||
return this.props.group._links.update;
|
return this.props.group._links.update;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { t, editUrl } = this.props;
|
||||||
|
|
||||||
|
if (!this.isEditable()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <NavLink to={editUrl} label={t("singleGroup.menu.generalNavLink")} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate("groups")(EditGroupNavLink);
|
export default translate("groups")(EditGroupNavLink);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class ChangePermissionNavLink extends React.Component<Props> {
|
|||||||
if (!this.hasPermissionToSetPermission()) {
|
if (!this.hasPermissionToSetPermission()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <NavLink to={permissionsUrl} label={t("set-permissions-button.label")} />;
|
return <NavLink to={permissionsUrl} label={t("singleGroup.menu.setPermissionsNavLink")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPermissionToSetPermission = () => {
|
hasPermissionToSetPermission = () => {
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export { default as DeleteGroupNavLink } from "./DeleteGroupNavLink";
|
|
||||||
export { default as EditGroupNavLink } from "./EditGroupNavLink";
|
export { default as EditGroupNavLink } from "./EditGroupNavLink";
|
||||||
export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink";
|
export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink";
|
||||||
|
|||||||
113
scm-ui/src/groups/containers/DeleteGroup.js
Normal file
113
scm-ui/src/groups/containers/DeleteGroup.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// @flow
|
||||||
|
import React from "react";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import type { Group } from "@scm-manager/ui-types";
|
||||||
|
import {
|
||||||
|
Subtitle,
|
||||||
|
DeleteButton,
|
||||||
|
confirmAlert,
|
||||||
|
ErrorNotification
|
||||||
|
} from "@scm-manager/ui-components";
|
||||||
|
import {
|
||||||
|
deleteGroup,
|
||||||
|
getDeleteGroupFailure,
|
||||||
|
isDeleteGroupPending
|
||||||
|
} from "../modules/groups";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import type { History } from "history";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
loading: boolean,
|
||||||
|
error: Error,
|
||||||
|
group: Group,
|
||||||
|
confirmDialog?: boolean,
|
||||||
|
deleteGroup: (group: Group, callback?: () => void) => void,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
history: History,
|
||||||
|
t: string => string
|
||||||
|
};
|
||||||
|
|
||||||
|
export class DeleteGroup extends React.Component<Props> {
|
||||||
|
static defaultProps = {
|
||||||
|
confirmDialog: true
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteGroup = () => {
|
||||||
|
this.props.deleteGroup(this.props.group, this.groupDeleted);
|
||||||
|
};
|
||||||
|
|
||||||
|
groupDeleted = () => {
|
||||||
|
this.props.history.push("/groups");
|
||||||
|
};
|
||||||
|
|
||||||
|
confirmDelete = () => {
|
||||||
|
const { t } = this.props;
|
||||||
|
confirmAlert({
|
||||||
|
title: t("deleteGroup.confirmAlert.title"),
|
||||||
|
message: t("deleteGroup.confirmAlert.message"),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: t("deleteGroup.confirmAlert.submit"),
|
||||||
|
onClick: () => this.deleteGroup()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("deleteGroup.confirmAlert.cancel"),
|
||||||
|
onClick: () => null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
isDeletable = () => {
|
||||||
|
return this.props.group._links.delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { loading, error, confirmDialog, t } = this.props;
|
||||||
|
const action = confirmDialog ? this.confirmDelete : this.deleteGroup;
|
||||||
|
|
||||||
|
if (!this.isDeletable()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Subtitle subtitle={t("deleteGroup.subtitle")} />
|
||||||
|
<ErrorNotification error={error} />
|
||||||
|
<div className="columns">
|
||||||
|
<div className="column">
|
||||||
|
<DeleteButton
|
||||||
|
label={t("deleteGroup.button")}
|
||||||
|
action={action}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
const loading = isDeleteGroupPending(state, ownProps.group.name);
|
||||||
|
const error = getDeleteGroupFailure(state, ownProps.group.name);
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
deleteGroup: (group: Group, callback?: () => void) => {
|
||||||
|
dispatch(deleteGroup(group, callback));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(withRouter(translate("groups")(DeleteGroup)));
|
||||||
@@ -3,9 +3,9 @@ import React from "react";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import GroupForm from "../components/GroupForm";
|
import GroupForm from "../components/GroupForm";
|
||||||
import {
|
import {
|
||||||
|
modifyGroup,
|
||||||
getModifyGroupFailure,
|
getModifyGroupFailure,
|
||||||
isModifyGroupPending,
|
isModifyGroupPending,
|
||||||
modifyGroup,
|
|
||||||
modifyGroupReset
|
modifyGroupReset
|
||||||
} from "../modules/groups";
|
} from "../modules/groups";
|
||||||
import type { History } from "history";
|
import type { History } from "history";
|
||||||
@@ -13,12 +13,13 @@ import { withRouter } from "react-router-dom";
|
|||||||
import type { Group } from "@scm-manager/ui-types";
|
import type { Group } from "@scm-manager/ui-types";
|
||||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||||
import { getUserAutoCompleteLink } from "../../modules/indexResource";
|
import { getUserAutoCompleteLink } from "../../modules/indexResource";
|
||||||
|
import DeleteGroup from "./DeleteGroup";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
group: Group,
|
group: Group,
|
||||||
|
fetchGroup: (name: string) => void,
|
||||||
modifyGroup: (group: Group, callback?: () => void) => void,
|
modifyGroup: (group: Group, callback?: () => void) => void,
|
||||||
modifyGroupReset: Group => void,
|
modifyGroupReset: Group => void,
|
||||||
fetchGroup: (name: string) => void,
|
|
||||||
autocompleteLink: string,
|
autocompleteLink: string,
|
||||||
history: History,
|
history: History,
|
||||||
loading?: boolean,
|
loading?: boolean,
|
||||||
@@ -54,7 +55,7 @@ class EditGroup extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { group, loading, error } = this.props;
|
const { loading, error, group } = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ErrorNotification error={error} />
|
<ErrorNotification error={error} />
|
||||||
@@ -66,6 +67,8 @@ class EditGroup extends React.Component<Props> {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
loadUserSuggestions={this.loadUserAutocompletion}
|
loadUserSuggestions={this.loadUserAutocompletion}
|
||||||
/>
|
/>
|
||||||
|
<hr />
|
||||||
|
<DeleteGroup group={group} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,33 +6,30 @@ import {
|
|||||||
ErrorPage,
|
ErrorPage,
|
||||||
Loading,
|
Loading,
|
||||||
Navigation,
|
Navigation,
|
||||||
|
SubNavigation,
|
||||||
Section,
|
Section,
|
||||||
NavLink
|
NavLink
|
||||||
} from "@scm-manager/ui-components";
|
} from "@scm-manager/ui-components";
|
||||||
import { Route } from "react-router";
|
import { Route } from "react-router";
|
||||||
import { Details } from "./../components/table";
|
import { Details } from "./../components/table";
|
||||||
import {
|
import {
|
||||||
DeleteGroupNavLink,
|
|
||||||
EditGroupNavLink,
|
EditGroupNavLink,
|
||||||
SetPermissionsNavLink
|
SetPermissionsNavLink
|
||||||
} from "./../components/navLinks";
|
} from "./../components/navLinks";
|
||||||
import type { Group } from "@scm-manager/ui-types";
|
import type { Group } from "@scm-manager/ui-types";
|
||||||
import type { History } from "history";
|
import type { History } from "history";
|
||||||
import {
|
import {
|
||||||
deleteGroup,
|
|
||||||
fetchGroupByName,
|
fetchGroupByName,
|
||||||
getGroupByName,
|
getGroupByName,
|
||||||
isFetchGroupPending,
|
isFetchGroupPending,
|
||||||
getFetchGroupFailure,
|
getFetchGroupFailure
|
||||||
getDeleteGroupFailure,
|
|
||||||
isDeleteGroupPending
|
|
||||||
} from "../modules/groups";
|
} from "../modules/groups";
|
||||||
|
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import EditGroup from "./EditGroup";
|
import EditGroup from "./EditGroup";
|
||||||
import { getGroupsLink } from "../../modules/indexResource";
|
import { getGroupsLink } from "../../modules/indexResource";
|
||||||
import SetPermissions from "../../permissions/components/SetPermissions";
|
import SetPermissions from "../../permissions/components/SetPermissions";
|
||||||
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name: string,
|
name: string,
|
||||||
@@ -42,7 +39,6 @@ type Props = {
|
|||||||
groupLink: string,
|
groupLink: string,
|
||||||
|
|
||||||
// dispatcher functions
|
// dispatcher functions
|
||||||
deleteGroup: (group: Group, callback?: () => void) => void,
|
|
||||||
fetchGroupByName: (string, string) => void,
|
fetchGroupByName: (string, string) => void,
|
||||||
|
|
||||||
// context objects
|
// context objects
|
||||||
@@ -63,14 +59,6 @@ class SingleGroup extends React.Component<Props> {
|
|||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteGroup = (group: Group) => {
|
|
||||||
this.props.deleteGroup(group, this.groupDeleted);
|
|
||||||
};
|
|
||||||
|
|
||||||
groupDeleted = () => {
|
|
||||||
this.props.history.push("/groups");
|
|
||||||
};
|
|
||||||
|
|
||||||
matchedUrl = () => {
|
matchedUrl = () => {
|
||||||
return this.stripEndingSlash(this.props.match.url);
|
return this.stripEndingSlash(this.props.match.url);
|
||||||
};
|
};
|
||||||
@@ -81,8 +69,8 @@ class SingleGroup extends React.Component<Props> {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<ErrorPage
|
<ErrorPage
|
||||||
title={t("single-group.error-title")}
|
title={t("singleGroup.errorTitle")}
|
||||||
subtitle={t("single-group.error-subtitle")}
|
subtitle={t("singleGroup.errorSubtitle")}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -109,15 +97,17 @@ class SingleGroup extends React.Component<Props> {
|
|||||||
component={() => <Details group={group} />}
|
component={() => <Details group={group} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/edit`}
|
path={`${url}/settings/general`}
|
||||||
exact
|
exact
|
||||||
component={() => <EditGroup group={group} />}
|
component={() => <EditGroup group={group} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/permissions`}
|
path={`${url}/settings/permissions`}
|
||||||
exact
|
exact
|
||||||
component={() => (
|
component={() => (
|
||||||
<SetPermissions selectedPermissionsLink={group._links.permissions} />
|
<SetPermissions
|
||||||
|
selectedPermissionsLink={group._links.permissions}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<ExtensionPoint
|
<ExtensionPoint
|
||||||
@@ -128,33 +118,35 @@ class SingleGroup extends React.Component<Props> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<Section label={t("single-group.navigation-label")}>
|
<Section label={t("singleGroup.menu.navigationLabel")}>
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`${url}`}
|
to={`${url}`}
|
||||||
icon="fas fa-info-circle"
|
icon="fas fa-info-circle"
|
||||||
label={t("single-group.information-label")}
|
label={t("singleGroup.menu.informationNavLink")}
|
||||||
/>
|
|
||||||
<SetPermissionsNavLink
|
|
||||||
group={group}
|
|
||||||
permissionsUrl={`${url}/permissions`}
|
|
||||||
/>
|
/>
|
||||||
<ExtensionPoint
|
<ExtensionPoint
|
||||||
name="group.navigation"
|
name="group.navigation"
|
||||||
props={extensionProps}
|
props={extensionProps}
|
||||||
renderAll={true}
|
renderAll={true}
|
||||||
/>
|
/>
|
||||||
</Section>
|
<SubNavigation
|
||||||
<Section label={t("single-group.actions-label")}>
|
to={`${url}/settings/general`}
|
||||||
<DeleteGroupNavLink
|
label={t("singleGroup.menu.settingsNavLink")}
|
||||||
|
>
|
||||||
|
<EditGroupNavLink
|
||||||
group={group}
|
group={group}
|
||||||
deleteGroup={this.deleteGroup}
|
editUrl={`${url}/settings/general`}
|
||||||
/>
|
/>
|
||||||
<EditGroupNavLink group={group} editUrl={`${url}/edit`} />
|
<SetPermissionsNavLink
|
||||||
<NavLink
|
group={group}
|
||||||
to="/groups"
|
permissionsUrl={`${url}/settings/permissions`}
|
||||||
icon="fas fa-undo-alt"
|
|
||||||
label={t("single-group.back-label")}
|
|
||||||
/>
|
/>
|
||||||
|
<ExtensionPoint
|
||||||
|
name="group.subnavigation"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
|
</SubNavigation>
|
||||||
</Section>
|
</Section>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,10 +159,8 @@ class SingleGroup extends React.Component<Props> {
|
|||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
const name = ownProps.match.params.name;
|
const name = ownProps.match.params.name;
|
||||||
const group = getGroupByName(state, name);
|
const group = getGroupByName(state, name);
|
||||||
const loading =
|
const loading = isFetchGroupPending(state, name);
|
||||||
isFetchGroupPending(state, name) || isDeleteGroupPending(state, name);
|
const error = getFetchGroupFailure(state, name);
|
||||||
const error =
|
|
||||||
getFetchGroupFailure(state, name) || getDeleteGroupFailure(state, name);
|
|
||||||
const groupLink = getGroupsLink(state);
|
const groupLink = getGroupsLink(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -186,9 +176,6 @@ const mapDispatchToProps = dispatch => {
|
|||||||
return {
|
return {
|
||||||
fetchGroupByName: (link: string, name: string) => {
|
fetchGroupByName: (link: string, name: string) => {
|
||||||
dispatch(fetchGroupByName(link, name));
|
dispatch(fetchGroupByName(link, name));
|
||||||
},
|
|
||||||
deleteGroup: (group: Group, callback?: () => void) => {
|
|
||||||
dispatch(deleteGroup(group, callback));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class SetPermissions extends React.Component<Props, State> {
|
|||||||
message = (
|
message = (
|
||||||
<Notification
|
<Notification
|
||||||
type={"success"}
|
type={"success"}
|
||||||
children={t("form.set-permissions-successful")}
|
children={t("setPermissions.setPermissionsSuccessful")}
|
||||||
onClose={() => this.onClose()}
|
onClose={() => this.onClose()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -128,7 +128,7 @@ class SetPermissions extends React.Component<Props, State> {
|
|||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={!this.state.permissionsChanged}
|
disabled={!this.state.permissionsChanged}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
label={t("form.submit-button.label")}
|
label={t("setPermissions.button")}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
//@flow
|
|
||||||
import React from "react";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import { NavAction, confirmAlert } from "@scm-manager/ui-components";
|
|
||||||
import type { Repository } from "@scm-manager/ui-types";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
repository: Repository,
|
|
||||||
confirmDialog?: boolean,
|
|
||||||
delete: Repository => void,
|
|
||||||
|
|
||||||
// context props
|
|
||||||
t: string => string
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeleteNavAction extends React.Component<Props> {
|
|
||||||
static defaultProps = {
|
|
||||||
confirmDialog: true
|
|
||||||
};
|
|
||||||
|
|
||||||
delete = () => {
|
|
||||||
this.props.delete(this.props.repository);
|
|
||||||
};
|
|
||||||
|
|
||||||
confirmDelete = () => {
|
|
||||||
const { t } = this.props;
|
|
||||||
confirmAlert({
|
|
||||||
title: t("delete-nav-action.confirm-alert.title"),
|
|
||||||
message: t("delete-nav-action.confirm-alert.message"),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: t("delete-nav-action.confirm-alert.submit"),
|
|
||||||
onClick: () => this.delete()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("delete-nav-action.confirm-alert.cancel"),
|
|
||||||
onClick: () => null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
isDeletable = () => {
|
|
||||||
return this.props.repository._links.delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { confirmDialog, t } = this.props;
|
|
||||||
const action = confirmDialog ? this.confirmDelete : this.delete();
|
|
||||||
|
|
||||||
if (!this.isDeletable()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return <NavAction action={action} icon="fas fa-times" label={t("delete-nav-action.label")} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate("repos")(DeleteNavAction);
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { mount, shallow } from "enzyme";
|
|
||||||
import "../../tests/enzyme";
|
|
||||||
import "../../tests/i18n";
|
|
||||||
import DeleteNavAction from "./DeleteNavAction";
|
|
||||||
|
|
||||||
import { confirmAlert } from "@scm-manager/ui-components";
|
|
||||||
jest.mock("@scm-manager/ui-components", () => ({
|
|
||||||
confirmAlert: jest.fn(),
|
|
||||||
NavAction: require.requireActual("@scm-manager/ui-components").NavAction
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("DeleteNavAction", () => {
|
|
||||||
it("should render nothing, if the delete link is missing", () => {
|
|
||||||
const repository = {
|
|
||||||
_links: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = shallow(
|
|
||||||
<DeleteNavAction repository={repository} delete={() => {}} />
|
|
||||||
);
|
|
||||||
expect(navLink.text()).toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the navLink", () => {
|
|
||||||
const repository = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/repositories"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteNavAction repository={repository} delete={() => {}} />
|
|
||||||
);
|
|
||||||
expect(navLink.text()).not.toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should open the confirm dialog on navLink click", () => {
|
|
||||||
const repository = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/repositorys"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteNavAction repository={repository} delete={() => {}} />
|
|
||||||
);
|
|
||||||
navLink.find("a").simulate("click");
|
|
||||||
|
|
||||||
expect(confirmAlert.mock.calls.length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the delete repository function with delete url", () => {
|
|
||||||
const repository = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/repos"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let calledUrl = null;
|
|
||||||
function capture(repository) {
|
|
||||||
calledUrl = repository._links.delete.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteNavAction
|
|
||||||
repository={repository}
|
|
||||||
confirmDialog={false}
|
|
||||||
delete={capture}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
navLink.find("a").simulate("click");
|
|
||||||
|
|
||||||
expect(calledUrl).toBe("/repos");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,22 +1,28 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import type { Repository } from "@scm-manager/ui-types";
|
||||||
import { NavLink } from "@scm-manager/ui-components";
|
import { NavLink } from "@scm-manager/ui-components";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import type { Repository } from "@scm-manager/ui-types";
|
|
||||||
|
|
||||||
type Props = { editUrl: string, t: string => string, repository: Repository };
|
type Props = {
|
||||||
|
repository: Repository,
|
||||||
|
editUrl: string,
|
||||||
|
t: string => string
|
||||||
|
};
|
||||||
|
|
||||||
class EditNavLink extends React.Component<Props> {
|
class EditRepoNavLink extends React.Component<Props> {
|
||||||
isEditable = () => {
|
isEditable = () => {
|
||||||
return this.props.repository._links.update;
|
return this.props.repository._links.update;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { editUrl, t } = this.props;
|
||||||
|
|
||||||
if (!this.isEditable()) {
|
if (!this.isEditable()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { editUrl, t } = this.props;
|
return <NavLink to={editUrl} label={t("repositoryRoot.menu.generalNavLink")} />;
|
||||||
return <NavLink to={editUrl} icon="fas fa-cog" label={t("edit-nav-link.label")} />;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate("repos")(EditNavLink);
|
export default translate("repos")(EditRepoNavLink);
|
||||||
@@ -3,9 +3,9 @@ import { shallow, mount } from "enzyme";
|
|||||||
import "../../tests/enzyme";
|
import "../../tests/enzyme";
|
||||||
import "../../tests/i18n";
|
import "../../tests/i18n";
|
||||||
import ReactRouterEnzymeContext from "react-router-enzyme-context";
|
import ReactRouterEnzymeContext from "react-router-enzyme-context";
|
||||||
import EditNavLink from "./EditNavLink";
|
import EditRepoNavLink from "./EditRepoNavLink";
|
||||||
|
|
||||||
describe("EditNavLink", () => {
|
describe("GeneralNavLink", () => {
|
||||||
const options = new ReactRouterEnzymeContext();
|
const options = new ReactRouterEnzymeContext();
|
||||||
|
|
||||||
it("should render nothing, if the modify link is missing", () => {
|
it("should render nothing, if the modify link is missing", () => {
|
||||||
@@ -14,7 +14,7 @@ describe("EditNavLink", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const navLink = shallow(
|
const navLink = shallow(
|
||||||
<EditNavLink repository={repository} editUrl="" />,
|
<EditRepoNavLink repository={repository} editUrl="" />,
|
||||||
options.get()
|
options.get()
|
||||||
);
|
);
|
||||||
expect(navLink.text()).toBe("");
|
expect(navLink.text()).toBe("");
|
||||||
@@ -30,9 +30,9 @@ describe("EditNavLink", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const navLink = mount(
|
const navLink = mount(
|
||||||
<EditNavLink repository={repository} editUrl="" />,
|
<EditRepoNavLink repository={repository} editUrl="" />,
|
||||||
options.get()
|
options.get()
|
||||||
);
|
);
|
||||||
expect(navLink.text()).toBe(" edit-nav-link.label");
|
expect(navLink.text()).toBe("repositoryRoot.menu.generalNavLink");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -20,7 +20,7 @@ class PermissionsNavLink extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
const { permissionUrl, t } = this.props;
|
const { permissionUrl, t } = this.props;
|
||||||
return (
|
return (
|
||||||
<NavLink to={permissionUrl} icon="fas fa-lock" label={t("repository-root.permissions")} />
|
<NavLink to={permissionUrl} label={t("repositoryRoot.menu.permissionsNavLink")} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,6 @@ describe("PermissionsNavLink", () => {
|
|||||||
<PermissionsNavLink repository={repository} permissionUrl="" />,
|
<PermissionsNavLink repository={repository} permissionUrl="" />,
|
||||||
options.get()
|
options.get()
|
||||||
);
|
);
|
||||||
expect(navLink.text()).toBe(" repository-root.permissions");
|
expect(navLink.text()).toBe("repositoryRoot.menu.permissionsNavLink");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
Subtitle,
|
||||||
InputField,
|
InputField,
|
||||||
Select,
|
Select,
|
||||||
SubmitButton,
|
SubmitButton,
|
||||||
@@ -81,7 +82,15 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
const { loading, t } = this.props;
|
const { loading, t } = this.props;
|
||||||
const repository = this.state.repository;
|
const repository = this.state.repository;
|
||||||
|
|
||||||
|
let subtitle = null;
|
||||||
|
if (this.props.repository) {
|
||||||
|
// edit existing repo
|
||||||
|
subtitle = <Subtitle subtitle={t("repositoryForm.subtitle")} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{subtitle}
|
||||||
<form onSubmit={this.submit}>
|
<form onSubmit={this.submit}>
|
||||||
{this.renderCreateOnlyFields()}
|
{this.renderCreateOnlyFields()}
|
||||||
<InputField
|
<InputField
|
||||||
@@ -102,9 +111,10 @@ class RepositoryForm extends React.Component<Props, State> {
|
|||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={!this.isValid()}
|
disabled={!this.isValid()}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
label={t("repository-form.submit")}
|
label={t("repositoryForm.submit")}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class RepositoryEntry extends React.Component<Props> {
|
|||||||
return (
|
return (
|
||||||
<RepositoryEntryLink
|
<RepositoryEntryLink
|
||||||
iconClass="fa-cog fa-lg"
|
iconClass="fa-cog fa-lg"
|
||||||
to={repositoryLink + "/edit"}
|
to={repositoryLink + "/settings/general"}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ class ChangesetView extends React.Component<Props> {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<ErrorPage
|
<ErrorPage
|
||||||
title={t("changeset-error.title")}
|
title={t("changesets.errorTitle")}
|
||||||
subtitle={t("changeset-error.subtitle")}
|
subtitle={t("changesets.errorSubtitle")}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class BranchRoot extends React.Component<Props> {
|
|||||||
if (repository._links.branches) {
|
if (repository._links.branches) {
|
||||||
return (
|
return (
|
||||||
<BranchSelector
|
<BranchSelector
|
||||||
label={t("branch-selector.label")}
|
label={t("changesets.branchSelectorLabel")}
|
||||||
branches={branches}
|
branches={branches}
|
||||||
selectedBranch={selected}
|
selectedBranch={selected}
|
||||||
selected={(b: Branch) => {
|
selected={(b: Branch) => {
|
||||||
|
|||||||
114
scm-ui/src/repos/containers/DeleteRepo.js
Normal file
114
scm-ui/src/repos/containers/DeleteRepo.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import type { Repository } from "@scm-manager/ui-types";
|
||||||
|
import {
|
||||||
|
Subtitle,
|
||||||
|
DeleteButton,
|
||||||
|
confirmAlert,
|
||||||
|
ErrorNotification
|
||||||
|
} from "@scm-manager/ui-components";
|
||||||
|
import {
|
||||||
|
deleteRepo,
|
||||||
|
getDeleteRepoFailure,
|
||||||
|
isDeleteRepoPending
|
||||||
|
} from "../modules/repos";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import type { History } from "history";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
loading: boolean,
|
||||||
|
error: Error,
|
||||||
|
repository: Repository,
|
||||||
|
confirmDialog?: boolean,
|
||||||
|
deleteRepo: (Repository, () => void) => void,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
history: History,
|
||||||
|
t: string => string
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeleteRepo extends React.Component<Props> {
|
||||||
|
static defaultProps = {
|
||||||
|
confirmDialog: true
|
||||||
|
};
|
||||||
|
|
||||||
|
deleted = () => {
|
||||||
|
this.props.history.push("/repos");
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteRepo = () => {
|
||||||
|
this.props.deleteRepo(this.props.repository, this.deleted);
|
||||||
|
};
|
||||||
|
|
||||||
|
confirmDelete = () => {
|
||||||
|
const { t } = this.props;
|
||||||
|
confirmAlert({
|
||||||
|
title: t("deleteRepo.confirmAlert.title"),
|
||||||
|
message: t("deleteRepo.confirmAlert.message"),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: t("deleteRepo.confirmAlert.submit"),
|
||||||
|
onClick: () => this.deleteRepo()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("deleteRepo.confirmAlert.cancel"),
|
||||||
|
onClick: () => null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
isDeletable = () => {
|
||||||
|
return this.props.repository._links.delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { loading, error, confirmDialog, t } = this.props;
|
||||||
|
const action = confirmDialog ? this.confirmDelete : this.deleteRepo;
|
||||||
|
|
||||||
|
if (!this.isDeletable()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
const { namespace, name } = ownProps.repository;
|
||||||
|
const loading = isDeleteRepoPending(state, namespace, name);
|
||||||
|
const error = getDeleteRepoFailure(state, namespace, name);
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
deleteRepo: (repo: Repository, callback: () => void) => {
|
||||||
|
dispatch(deleteRepo(repo, callback));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(withRouter(translate("repos")(DeleteRepo)));
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
// @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 { withRouter } from "react-router-dom";
|
||||||
import RepositoryForm from "../components/form";
|
import RepositoryForm from "../components/form";
|
||||||
|
import DeleteRepo from "./DeleteRepo";
|
||||||
import type { Repository } from "@scm-manager/ui-types";
|
import type { Repository } from "@scm-manager/ui-types";
|
||||||
import {
|
import {
|
||||||
modifyRepo,
|
modifyRepo,
|
||||||
@@ -10,34 +11,55 @@ import {
|
|||||||
getModifyRepoFailure,
|
getModifyRepoFailure,
|
||||||
modifyRepoReset
|
modifyRepoReset
|
||||||
} from "../modules/repos";
|
} from "../modules/repos";
|
||||||
import { withRouter } from "react-router-dom";
|
|
||||||
import type { History } from "history";
|
import type { History } from "history";
|
||||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||||
|
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository,
|
|
||||||
modifyRepo: (Repository, () => void) => void,
|
|
||||||
modifyRepoReset: Repository => void,
|
|
||||||
loading: boolean,
|
loading: boolean,
|
||||||
error: Error,
|
error: Error,
|
||||||
|
|
||||||
|
modifyRepo: (Repository, () => void) => void,
|
||||||
|
modifyRepoReset: Repository => void,
|
||||||
|
|
||||||
// context props
|
// context props
|
||||||
t: string => string,
|
repository: Repository,
|
||||||
history: History
|
history: History,
|
||||||
|
match: any
|
||||||
};
|
};
|
||||||
|
|
||||||
class Edit extends React.Component<Props> {
|
class EditRepo extends React.Component<Props> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { modifyRepoReset, repository } = this.props;
|
const { modifyRepoReset, repository } = this.props;
|
||||||
modifyRepoReset(repository);
|
modifyRepoReset(repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
repoModified = () => {
|
repoModified = () => {
|
||||||
const { history, repository } = this.props;
|
const { history, repository } = this.props;
|
||||||
history.push(`/repo/${repository.namespace}/${repository.name}`);
|
history.push(`/repo/${repository.namespace}/${repository.name}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
stripEndingSlash = (url: string) => {
|
||||||
|
if (url.endsWith("/")) {
|
||||||
|
return url.substring(0, url.length - 2);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
matchedUrl = () => {
|
||||||
|
return this.stripEndingSlash(this.props.match.url);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading, error } = this.props;
|
const { loading, error, repository } = this.props;
|
||||||
|
|
||||||
|
const url = this.matchedUrl();
|
||||||
|
|
||||||
|
const extensionProps = {
|
||||||
|
repository,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ErrorNotification error={error} />
|
<ErrorNotification error={error} />
|
||||||
@@ -48,6 +70,13 @@ class Edit extends React.Component<Props> {
|
|||||||
this.props.modifyRepo(repo, this.repoModified);
|
this.props.modifyRepo(repo, this.repoModified);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<hr />
|
||||||
|
<ExtensionPoint
|
||||||
|
name="repo-config.route"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
|
<DeleteRepo repository={repository} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -77,4 +106,4 @@ const mapDispatchToProps = dispatch => {
|
|||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(translate("repos")(withRouter(Edit)));
|
)(withRouter(EditRepo));
|
||||||
@@ -90,7 +90,7 @@ class Overview extends React.Component<Props> {
|
|||||||
if (showCreateButton) {
|
if (showCreateButton) {
|
||||||
return (
|
return (
|
||||||
<CreateButton
|
<CreateButton
|
||||||
label={t("overview.create-button")}
|
label={t("overview.createButton")}
|
||||||
link="/repos/create"
|
link="/repos/create"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {deleteRepo, fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos";
|
import {
|
||||||
|
fetchRepoByName,
|
||||||
|
getFetchRepoFailure,
|
||||||
|
getRepository,
|
||||||
|
isFetchRepoPending
|
||||||
|
} from "../modules/repos";
|
||||||
|
|
||||||
import {connect} from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import {Route, Switch} from "react-router-dom";
|
import { Route, Switch } from "react-router-dom";
|
||||||
import type {Repository} from "@scm-manager/ui-types";
|
import type { Repository } from "@scm-manager/ui-types";
|
||||||
|
|
||||||
import {ErrorPage, Loading, Navigation, NavLink, Page, Section} from "@scm-manager/ui-components";
|
import {
|
||||||
import {translate} from "react-i18next";
|
ErrorPage,
|
||||||
|
Loading,
|
||||||
|
Navigation,
|
||||||
|
SubNavigation,
|
||||||
|
NavLink,
|
||||||
|
Page,
|
||||||
|
Section
|
||||||
|
} from "@scm-manager/ui-components";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
import RepositoryDetails from "../components/RepositoryDetails";
|
import RepositoryDetails from "../components/RepositoryDetails";
|
||||||
import DeleteNavAction from "../components/DeleteNavAction";
|
import EditRepo from "./EditRepo";
|
||||||
import Edit from "../containers/Edit";
|
|
||||||
import Permissions from "../permissions/containers/Permissions";
|
import Permissions from "../permissions/containers/Permissions";
|
||||||
|
|
||||||
import type {History} from "history";
|
import type { History } from "history";
|
||||||
import EditNavLink from "../components/EditNavLink";
|
import EditRepoNavLink from "../components/EditRepoNavLink";
|
||||||
|
|
||||||
import BranchRoot from "./ChangesetsRoot";
|
import BranchRoot from "./ChangesetsRoot";
|
||||||
import ChangesetView from "./ChangesetView";
|
import ChangesetView from "./ChangesetView";
|
||||||
@@ -35,7 +47,6 @@ type Props = {
|
|||||||
|
|
||||||
// dispatch functions
|
// dispatch functions
|
||||||
fetchRepoByName: (link: string, namespace: string, name: string) => void,
|
fetchRepoByName: (link: string, namespace: string, name: string) => void,
|
||||||
deleteRepo: (repository: Repository, () => void) => void,
|
|
||||||
|
|
||||||
// context props
|
// context props
|
||||||
t: string => string,
|
t: string => string,
|
||||||
@@ -61,14 +72,6 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
return this.stripEndingSlash(this.props.match.url);
|
return this.stripEndingSlash(this.props.match.url);
|
||||||
};
|
};
|
||||||
|
|
||||||
deleted = () => {
|
|
||||||
this.props.history.push("/repos");
|
|
||||||
};
|
|
||||||
|
|
||||||
delete = (repository: Repository) => {
|
|
||||||
this.props.deleteRepo(repository, this.deleted);
|
|
||||||
};
|
|
||||||
|
|
||||||
matches = (route: any) => {
|
matches = (route: any) => {
|
||||||
const url = this.matchedUrl();
|
const url = this.matchedUrl();
|
||||||
const regex = new RegExp(`${url}(/branches)?/?[^/]*/changesets?.*`);
|
const regex = new RegExp(`${url}(/branches)?/?[^/]*/changesets?.*`);
|
||||||
@@ -81,8 +84,8 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<ErrorPage
|
<ErrorPage
|
||||||
title={t("repository-root.error-title")}
|
title={t("repositoryRoot.errorTitle")}
|
||||||
subtitle={t("repository-root.error-subtitle")}
|
subtitle={t("repositoryRoot.errorSubtitle")}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -111,11 +114,11 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
component={() => <RepositoryDetails repository={repository} />}
|
component={() => <RepositoryDetails repository={repository} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/edit`}
|
path={`${url}/settings/general`}
|
||||||
component={() => <Edit repository={repository} />}
|
component={() => <EditRepo repository={repository} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/permissions`}
|
path={`${url}/settings/permissions`}
|
||||||
render={() => (
|
render={() => (
|
||||||
<Permissions
|
<Permissions
|
||||||
namespace={this.props.repository.namespace}
|
namespace={this.props.repository.namespace}
|
||||||
@@ -170,14 +173,18 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<Section label={t("repository-root.navigation-label")}>
|
<Section label={t("repositoryRoot.menu.navigationLabel")}>
|
||||||
<NavLink to={url} icon="fas fa-info-circle" label={t("repository-root.information")} />
|
<NavLink
|
||||||
|
to={url}
|
||||||
|
icon="fas fa-info-circle"
|
||||||
|
label={t("repositoryRoot.menu.informationNavLink")}
|
||||||
|
/>
|
||||||
<RepositoryNavLink
|
<RepositoryNavLink
|
||||||
repository={repository}
|
repository={repository}
|
||||||
linkName="changesets"
|
linkName="changesets"
|
||||||
to={`${url}/changesets/`}
|
to={`${url}/changesets/`}
|
||||||
icon="fas fa-code-branch"
|
icon="fas fa-code-branch"
|
||||||
label={t("repository-root.history")}
|
label={t("repositoryRoot.menu.historyNavLink")}
|
||||||
activeWhenMatch={this.matches}
|
activeWhenMatch={this.matches}
|
||||||
activeOnlyWhenExact={false}
|
activeOnlyWhenExact={false}
|
||||||
/>
|
/>
|
||||||
@@ -186,23 +193,32 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
linkName="sources"
|
linkName="sources"
|
||||||
to={`${url}/sources`}
|
to={`${url}/sources`}
|
||||||
icon="fas fa-code"
|
icon="fas fa-code"
|
||||||
label={t("repository-root.sources")}
|
label={t("repositoryRoot.menu.sourcesNavLink")}
|
||||||
activeOnlyWhenExact={false}
|
activeOnlyWhenExact={false}
|
||||||
/>
|
/>
|
||||||
<PermissionsNavLink
|
|
||||||
permissionUrl={`${url}/permissions`}
|
|
||||||
repository={repository}
|
|
||||||
/>
|
|
||||||
<ExtensionPoint
|
<ExtensionPoint
|
||||||
name="repository.navigation"
|
name="repository.navigation"
|
||||||
props={extensionProps}
|
props={extensionProps}
|
||||||
renderAll={true}
|
renderAll={true}
|
||||||
/>
|
/>
|
||||||
</Section>
|
<SubNavigation
|
||||||
<Section label={t("repository-root.actions-label")}>
|
to={`${url}/settings/general`}
|
||||||
<DeleteNavAction repository={repository} delete={this.delete} />
|
label={t("repositoryRoot.menu.settingsNavLink")}
|
||||||
<EditNavLink repository={repository} editUrl={`${url}/edit`} />
|
>
|
||||||
<NavLink to="/repos" icon="fas fa-undo" label={t("repository-root.back-label")} />
|
<EditRepoNavLink
|
||||||
|
repository={repository}
|
||||||
|
editUrl={`${url}/settings/general`}
|
||||||
|
/>
|
||||||
|
<PermissionsNavLink
|
||||||
|
permissionUrl={`${url}/settings/permissions`}
|
||||||
|
repository={repository}
|
||||||
|
/>
|
||||||
|
<ExtensionPoint
|
||||||
|
name="repository.subnavigation"
|
||||||
|
props={extensionProps}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
|
</SubNavigation>
|
||||||
</Section>
|
</Section>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
</div>
|
</div>
|
||||||
@@ -234,9 +250,6 @@ const mapDispatchToProps = dispatch => {
|
|||||||
return {
|
return {
|
||||||
fetchRepoByName: (link: string, namespace: string, name: string) => {
|
fetchRepoByName: (link: string, namespace: string, name: string) => {
|
||||||
dispatch(fetchRepoByName(link, namespace, name));
|
dispatch(fetchRepoByName(link, namespace, name));
|
||||||
},
|
|
||||||
deleteRepo: (repository: Repository, callback: () => void) => {
|
|
||||||
dispatch(deleteRepo(repository, callback));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class Sources extends React.Component<Props> {
|
|||||||
<BranchSelector
|
<BranchSelector
|
||||||
branches={branches}
|
branches={branches}
|
||||||
selectedBranch={revision}
|
selectedBranch={revision}
|
||||||
label={t("branch-selector.label")}
|
label={t("changesets.branchSelectorLabel")}
|
||||||
selected={(b: Branch) => {
|
selected={(b: Branch) => {
|
||||||
this.branchSelected(b);
|
this.branchSelected(b);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class SetUserPassword extends React.Component<Props, State> {
|
|||||||
message = (
|
message = (
|
||||||
<Notification
|
<Notification
|
||||||
type={"success"}
|
type={"success"}
|
||||||
children={t("password.set-password-successful")}
|
children={t("singleUserPassword.setPasswordSuccessful")}
|
||||||
onClose={() => this.onClose()}
|
onClose={() => this.onClose()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -108,7 +108,7 @@ class SetUserPassword extends React.Component<Props, State> {
|
|||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={!this.state.passwordValid}
|
disabled={!this.state.passwordValid}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
label={t("user-form.submit")}
|
label={t("singleUserPassword.button")}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import React from "react";
|
|||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import type { User } from "@scm-manager/ui-types";
|
import type { User } from "@scm-manager/ui-types";
|
||||||
import {
|
import {
|
||||||
|
Subtitle,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
InputField,
|
InputField,
|
||||||
PasswordConfirmation,
|
PasswordConfirmation,
|
||||||
@@ -113,7 +114,9 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
|
|
||||||
let nameField = null;
|
let nameField = null;
|
||||||
let passwordChangeField = null;
|
let passwordChangeField = null;
|
||||||
|
let subtitle = null;
|
||||||
if (!this.props.user) {
|
if (!this.props.user) {
|
||||||
|
// create new user
|
||||||
nameField = (
|
nameField = (
|
||||||
<div className="column is-half">
|
<div className="column is-half">
|
||||||
<InputField
|
<InputField
|
||||||
@@ -130,8 +133,13 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
passwordChangeField = (
|
passwordChangeField = (
|
||||||
<PasswordConfirmation passwordChanged={this.handlePasswordChange} />
|
<PasswordConfirmation passwordChanged={this.handlePasswordChange} />
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// edit existing user
|
||||||
|
subtitle = <Subtitle subtitle={t("userForm.subtitle")} />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{subtitle}
|
||||||
<form onSubmit={this.submit}>
|
<form onSubmit={this.submit}>
|
||||||
<div className="columns is-multiline">
|
<div className="columns is-multiline">
|
||||||
{nameField}
|
{nameField}
|
||||||
@@ -178,11 +186,12 @@ class UserForm extends React.Component<Props, State> {
|
|||||||
<SubmitButton
|
<SubmitButton
|
||||||
disabled={!this.isValid()}
|
disabled={!this.isValid()}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
label={t("user-form.submit")}
|
label={t("userForm.button")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
//@flow
|
|
||||||
import React from "react";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import { CreateButton } from "@scm-manager/ui-components";
|
|
||||||
|
|
||||||
// TODO remove
|
|
||||||
type Props = {
|
|
||||||
t: string => string
|
|
||||||
};
|
|
||||||
|
|
||||||
class CreateUserButton extends React.Component<Props> {
|
|
||||||
render() {
|
|
||||||
const { t } = this.props;
|
|
||||||
return (
|
|
||||||
<CreateButton label={t("create-user-button.label")} link="/users/add" />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate("users")(CreateUserButton);
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
// @flow
|
|
||||||
import React from "react";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import type { User } from "@scm-manager/ui-types";
|
|
||||||
import { NavAction, confirmAlert } from "@scm-manager/ui-components";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
user: User,
|
|
||||||
confirmDialog?: boolean,
|
|
||||||
t: string => string,
|
|
||||||
deleteUser: (user: User) => void
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeleteUserNavLink extends React.Component<Props> {
|
|
||||||
static defaultProps = {
|
|
||||||
confirmDialog: true
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteUser = () => {
|
|
||||||
this.props.deleteUser(this.props.user);
|
|
||||||
};
|
|
||||||
|
|
||||||
confirmDelete = () => {
|
|
||||||
const { t } = this.props;
|
|
||||||
confirmAlert({
|
|
||||||
title: t("delete-user-button.confirm-alert.title"),
|
|
||||||
message: t("delete-user-button.confirm-alert.message"),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: t("delete-user-button.confirm-alert.submit"),
|
|
||||||
onClick: () => this.deleteUser()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("delete-user-button.confirm-alert.cancel"),
|
|
||||||
onClick: () => null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
isDeletable = () => {
|
|
||||||
return this.props.user._links.delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { confirmDialog, t } = this.props;
|
|
||||||
const action = confirmDialog ? this.confirmDelete : this.deleteUser;
|
|
||||||
|
|
||||||
if (!this.isDeletable()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return <NavAction icon="fas fa-times" label={t("delete-user-button.label")} action={action} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate("users")(DeleteUserNavLink);
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { mount, shallow } from "enzyme";
|
|
||||||
import "../../../tests/enzyme";
|
|
||||||
import "../../../tests/i18n";
|
|
||||||
import DeleteUserNavLink from "./DeleteUserNavLink";
|
|
||||||
|
|
||||||
import { confirmAlert } from "@scm-manager/ui-components";
|
|
||||||
jest.mock("@scm-manager/ui-components", () => ({
|
|
||||||
confirmAlert: jest.fn(),
|
|
||||||
NavAction: require.requireActual("@scm-manager/ui-components").NavAction
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("DeleteUserNavLink", () => {
|
|
||||||
it("should render nothing, if the delete link is missing", () => {
|
|
||||||
const user = {
|
|
||||||
_links: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = shallow(
|
|
||||||
<DeleteUserNavLink user={user} deleteUser={() => {}} />
|
|
||||||
);
|
|
||||||
expect(navLink.text()).toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render the navLink", () => {
|
|
||||||
const user = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/users"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteUserNavLink user={user} deleteUser={() => {}} />
|
|
||||||
);
|
|
||||||
expect(navLink.text()).not.toBe("");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should open the confirm dialog on navLink click", () => {
|
|
||||||
const user = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/users"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteUserNavLink user={user} deleteUser={() => {}} />
|
|
||||||
);
|
|
||||||
navLink.find("a").simulate("click");
|
|
||||||
|
|
||||||
expect(confirmAlert.mock.calls.length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call the delete user function with delete url", () => {
|
|
||||||
const user = {
|
|
||||||
_links: {
|
|
||||||
delete: {
|
|
||||||
href: "/users"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let calledUrl = null;
|
|
||||||
function capture(user) {
|
|
||||||
calledUrl = user._links.delete.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
const navLink = mount(
|
|
||||||
<DeleteUserNavLink
|
|
||||||
user={user}
|
|
||||||
confirmDialog={false}
|
|
||||||
deleteUser={capture}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
navLink.find("a").simulate("click");
|
|
||||||
|
|
||||||
expect(calledUrl).toBe("/users");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import type { User } from "@scm-manager/ui-types";
|
import type { User } from "@scm-manager/ui-types";
|
||||||
import { NavLink } from "@scm-manager/ui-components";
|
import { NavLink } from "@scm-manager/ui-components";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
t: string => string,
|
|
||||||
user: User,
|
user: User,
|
||||||
editUrl: String
|
editUrl: String,
|
||||||
|
t: string => string
|
||||||
};
|
};
|
||||||
|
|
||||||
class EditUserNavLink extends React.Component<Props> {
|
class EditUserNavLink extends React.Component<Props> {
|
||||||
|
isEditable = () => {
|
||||||
|
return this.props.user._links.update;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { t, editUrl } = this.props;
|
const { t, editUrl } = this.props;
|
||||||
|
|
||||||
if (!this.isEditable()) {
|
if (!this.isEditable()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <NavLink to={editUrl} icon="fas fa-cog" label={t("edit-user-button.label")} />;
|
return <NavLink to={editUrl} label={t("singleUser.menu.generalNavLink")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
isEditable = () => {
|
|
||||||
return this.props.user._links.update;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate("users")(EditUserNavLink);
|
export default translate("users")(EditUserNavLink);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class ChangePasswordNavLink extends React.Component<Props> {
|
|||||||
if (!this.hasPermissionToSetPassword()) {
|
if (!this.hasPermissionToSetPassword()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <NavLink to={passwordUrl} label={t("set-password-button.label")} />;
|
return <NavLink to={passwordUrl} label={t("singleUser.menu.setPasswordNavLink")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPermissionToSetPassword = () => {
|
hasPermissionToSetPassword = () => {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class ChangePermissionNavLink extends React.Component<Props> {
|
|||||||
if (!this.hasPermissionToSetPermission()) {
|
if (!this.hasPermissionToSetPermission()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <NavLink to={permissionsUrl} label={t("set-permissions-button.label")} />;
|
return <NavLink to={permissionsUrl} label={t("singleUser.menu.setPermissionsNavLink")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPermissionToSetPermission = () => {
|
hasPermissionToSetPermission = () => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export { default as DeleteUserNavLink } from "./DeleteUserNavLink";
|
|
||||||
export { default as EditUserNavLink } from "./EditUserNavLink";
|
export { default as EditUserNavLink } from "./EditUserNavLink";
|
||||||
export { default as SetPasswordNavLink } from "./SetPasswordNavLink";
|
export { default as SetPasswordNavLink } from "./SetPasswordNavLink";
|
||||||
export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink";
|
export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink";
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ class AddUser extends React.Component<Props> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
title={t("add-user.title")}
|
title={t("addUser.title")}
|
||||||
subtitle={t("add-user.subtitle")}
|
subtitle={t("addUser.subtitle")}
|
||||||
error={error}
|
error={error}
|
||||||
showContentOnError={true}
|
showContentOnError={true}
|
||||||
>
|
>
|
||||||
|
|||||||
113
scm-ui/src/users/containers/DeleteUser.js
Normal file
113
scm-ui/src/users/containers/DeleteUser.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// @flow
|
||||||
|
import React from "react";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import type { User } from "@scm-manager/ui-types";
|
||||||
|
import {
|
||||||
|
Subtitle,
|
||||||
|
DeleteButton,
|
||||||
|
confirmAlert,
|
||||||
|
ErrorNotification
|
||||||
|
} from "@scm-manager/ui-components";
|
||||||
|
import {
|
||||||
|
deleteUser,
|
||||||
|
getDeleteUserFailure,
|
||||||
|
isDeleteUserPending
|
||||||
|
} from "../modules/users";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import type { History } from "history";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
loading: boolean,
|
||||||
|
error: Error,
|
||||||
|
user: User,
|
||||||
|
confirmDialog?: boolean,
|
||||||
|
deleteUser: (user: User, callback?: () => void) => void,
|
||||||
|
|
||||||
|
// context props
|
||||||
|
history: History,
|
||||||
|
t: string => string
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeleteUser extends React.Component<Props> {
|
||||||
|
static defaultProps = {
|
||||||
|
confirmDialog: true
|
||||||
|
};
|
||||||
|
|
||||||
|
userDeleted = () => {
|
||||||
|
this.props.history.push("/users");
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteUser = () => {
|
||||||
|
this.props.deleteUser(this.props.user, this.userDeleted);
|
||||||
|
};
|
||||||
|
|
||||||
|
confirmDelete = () => {
|
||||||
|
const { t } = this.props;
|
||||||
|
confirmAlert({
|
||||||
|
title: t("deleteUser.confirmAlert.title"),
|
||||||
|
message: t("deleteUser.confirmAlert.message"),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: t("deleteUser.confirmAlert.submit"),
|
||||||
|
onClick: () => this.deleteUser()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("deleteUser.confirmAlert.cancel"),
|
||||||
|
onClick: () => null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
isDeletable = () => {
|
||||||
|
return this.props.user._links.delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { loading, error, confirmDialog, t } = this.props;
|
||||||
|
const action = confirmDialog ? this.confirmDelete : this.deleteUser;
|
||||||
|
|
||||||
|
if (!this.isDeletable()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Subtitle subtitle={t("deleteUser.subtitle")} />
|
||||||
|
<ErrorNotification error={error} />
|
||||||
|
<div className="columns">
|
||||||
|
<div className="column">
|
||||||
|
<DeleteButton
|
||||||
|
label={t("deleteUser.button")}
|
||||||
|
action={action}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
const loading = isDeleteUserPending(state, ownProps.user.name);
|
||||||
|
const error = getDeleteUserFailure(state, ownProps.user.name);
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
deleteUser: (user: User, callback?: () => void) => {
|
||||||
|
dispatch(deleteUser(user, callback));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(withRouter(translate("users")(DeleteUser)));
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { withRouter } from "react-router-dom";
|
import { withRouter } from "react-router-dom";
|
||||||
import UserForm from "./../components/UserForm";
|
import UserForm from "../components/UserForm";
|
||||||
|
import DeleteUser from "./DeleteUser";
|
||||||
import type { User } from "@scm-manager/ui-types";
|
import type { User } from "@scm-manager/ui-types";
|
||||||
import {
|
import {
|
||||||
modifyUser,
|
modifyUser,
|
||||||
@@ -31,6 +32,7 @@ class EditUser extends React.Component<Props> {
|
|||||||
const { modifyUserReset, user } = this.props;
|
const { modifyUserReset, user } = this.props;
|
||||||
modifyUserReset(user);
|
modifyUserReset(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
userModified = (user: User) => () => {
|
userModified = (user: User) => () => {
|
||||||
this.props.history.push(`/user/${user.name}`);
|
this.props.history.push(`/user/${user.name}`);
|
||||||
};
|
};
|
||||||
@@ -49,11 +51,22 @@ class EditUser extends React.Component<Props> {
|
|||||||
user={user}
|
user={user}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
|
<hr />
|
||||||
|
<DeleteUser user={user} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => {
|
||||||
|
const loading = isModifyUserPending(state, ownProps.user.name);
|
||||||
|
const error = getModifyUserFailure(state, ownProps.user.name);
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
modifyUser: (user: User, callback?: () => void) => {
|
modifyUser: (user: User, callback?: () => void) => {
|
||||||
@@ -65,15 +78,6 @@ const mapDispatchToProps = dispatch => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
|
||||||
const loading = isModifyUserPending(state, ownProps.user.name);
|
|
||||||
const error = getModifyUserFailure(state, ownProps.user.name);
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
error
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Page,
|
Page,
|
||||||
Loading,
|
Loading,
|
||||||
Navigation,
|
Navigation,
|
||||||
|
SubNavigation,
|
||||||
Section,
|
Section,
|
||||||
NavLink,
|
NavLink,
|
||||||
ErrorPage
|
ErrorPage
|
||||||
@@ -16,24 +17,16 @@ import type { User } from "@scm-manager/ui-types";
|
|||||||
import type { History } from "history";
|
import type { History } from "history";
|
||||||
import {
|
import {
|
||||||
fetchUserByName,
|
fetchUserByName,
|
||||||
deleteUser,
|
|
||||||
getUserByName,
|
getUserByName,
|
||||||
isFetchUserPending,
|
isFetchUserPending,
|
||||||
getFetchUserFailure,
|
getFetchUserFailure
|
||||||
isDeleteUserPending,
|
|
||||||
getDeleteUserFailure
|
|
||||||
} from "../modules/users";
|
} from "../modules/users";
|
||||||
|
import { EditUserNavLink, SetPasswordNavLink, SetPermissionsNavLink } from "./../components/navLinks";
|
||||||
import {
|
|
||||||
DeleteUserNavLink,
|
|
||||||
EditUserNavLink,
|
|
||||||
SetPasswordNavLink,
|
|
||||||
SetPermissionsNavLink
|
|
||||||
} from "./../components/navLinks";
|
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import { getUsersLink } from "../../modules/indexResource";
|
import { getUsersLink } from "../../modules/indexResource";
|
||||||
import SetUserPassword from "../components/SetUserPassword";
|
import SetUserPassword from "../components/SetUserPassword";
|
||||||
import SetPermissions from "../../permissions/components/SetPermissions";
|
import SetPermissions from "../../permissions/components/SetPermissions";
|
||||||
|
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name: string,
|
name: string,
|
||||||
@@ -42,8 +35,7 @@ type Props = {
|
|||||||
error: Error,
|
error: Error,
|
||||||
usersLink: string,
|
usersLink: string,
|
||||||
|
|
||||||
// dispatcher functions
|
// dispatcher function
|
||||||
deleteUser: (user: User, callback?: () => void) => void,
|
|
||||||
fetchUserByName: (string, string) => void,
|
fetchUserByName: (string, string) => void,
|
||||||
|
|
||||||
// context objects
|
// context objects
|
||||||
@@ -57,14 +49,6 @@ class SingleUser extends React.Component<Props> {
|
|||||||
this.props.fetchUserByName(this.props.usersLink, this.props.name);
|
this.props.fetchUserByName(this.props.usersLink, this.props.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
userDeleted = () => {
|
|
||||||
this.props.history.push("/users");
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteUser = (user: User) => {
|
|
||||||
this.props.deleteUser(user, this.userDeleted);
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -82,8 +66,8 @@ class SingleUser extends React.Component<Props> {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<ErrorPage
|
<ErrorPage
|
||||||
title={t("single-user.error-title")}
|
title={t("singleUser.errorTitle")}
|
||||||
subtitle={t("single-user.error-subtitle")}
|
subtitle={t("singleUser.errorSubtitle")}
|
||||||
error={error}
|
error={error}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -95,21 +79,26 @@ class SingleUser extends React.Component<Props> {
|
|||||||
|
|
||||||
const url = this.matchedUrl();
|
const url = this.matchedUrl();
|
||||||
|
|
||||||
|
const extensionProps = {
|
||||||
|
user,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title={user.displayName}>
|
<Page title={user.displayName}>
|
||||||
<div className="columns">
|
<div className="columns">
|
||||||
<div className="column is-three-quarters">
|
<div className="column is-three-quarters">
|
||||||
<Route path={url} exact component={() => <Details user={user} />} />
|
<Route path={url} exact component={() => <Details user={user} />} />
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/edit`}
|
path={`${url}/settings/general`}
|
||||||
component={() => <EditUser user={user} />}
|
component={() => <EditUser user={user} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/password`}
|
path={`${url}/settings/password`}
|
||||||
component={() => <SetUserPassword user={user} />}
|
component={() => <SetUserPassword user={user} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/permissions`}
|
path={`${url}/settings/permissions`}
|
||||||
component={() => (
|
component={() => (
|
||||||
<SetPermissions
|
<SetPermissions
|
||||||
selectedPermissionsLink={user._links.permissions}
|
selectedPermissionsLink={user._links.permissions}
|
||||||
@@ -119,25 +108,34 @@ class SingleUser extends React.Component<Props> {
|
|||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<Section label={t("single-user.navigation-label")}>
|
<Section label={t("singleUser.menu.navigationLabel")}>
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`${url}`}
|
to={`${url}`}
|
||||||
icon="fas fa-info-circle"
|
icon="fas fa-info-circle"
|
||||||
label={t("single-user.information-label")}
|
label={t("singleUser.menu.informationNavLink")}
|
||||||
|
/>
|
||||||
|
<SubNavigation
|
||||||
|
to={`${url}/settings/general`}
|
||||||
|
label={t("singleUser.menu.settingsNavLink")}
|
||||||
|
>
|
||||||
|
<EditUserNavLink
|
||||||
|
user={user}
|
||||||
|
editUrl={`${url}/settings/general`}
|
||||||
/>
|
/>
|
||||||
<EditUserNavLink user={user} editUrl={`${url}/edit`} />
|
|
||||||
<SetPasswordNavLink
|
<SetPasswordNavLink
|
||||||
user={user}
|
user={user}
|
||||||
passwordUrl={`${url}/password`}
|
passwordUrl={`${url}/settings/password`}
|
||||||
/>
|
/>
|
||||||
<SetPermissionsNavLink
|
<SetPermissionsNavLink
|
||||||
user={user}
|
user={user}
|
||||||
permissionsUrl={`${url}/permissions`}
|
permissionsUrl={`${url}/settings/permissions`}
|
||||||
/>
|
/>
|
||||||
</Section>
|
<ExtensionPoint
|
||||||
<Section label={t("single-user.actions-label")}>
|
name="user.subnavigation"
|
||||||
<DeleteUserNavLink user={user} deleteUser={this.deleteUser} />
|
props={extensionProps}
|
||||||
<NavLink to="/users" icon="fas fa-undo" label={t("single-user.back-label")} />
|
renderAll={true}
|
||||||
|
/>
|
||||||
|
</SubNavigation>
|
||||||
</Section>
|
</Section>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,10 +148,8 @@ class SingleUser extends React.Component<Props> {
|
|||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
const name = ownProps.match.params.name;
|
const name = ownProps.match.params.name;
|
||||||
const user = getUserByName(state, name);
|
const user = getUserByName(state, name);
|
||||||
const loading =
|
const loading = isFetchUserPending(state, name);
|
||||||
isFetchUserPending(state, name) || isDeleteUserPending(state, name);
|
const error = getFetchUserFailure(state, name);
|
||||||
const error =
|
|
||||||
getFetchUserFailure(state, name) || getDeleteUserFailure(state, name);
|
|
||||||
const usersLink = getUsersLink(state);
|
const usersLink = getUsersLink(state);
|
||||||
return {
|
return {
|
||||||
usersLink,
|
usersLink,
|
||||||
@@ -168,9 +164,6 @@ const mapDispatchToProps = dispatch => {
|
|||||||
return {
|
return {
|
||||||
fetchUserByName: (link: string, name: string) => {
|
fetchUserByName: (link: string, name: string) => {
|
||||||
dispatch(fetchUserByName(link, name));
|
dispatch(fetchUserByName(link, name));
|
||||||
},
|
|
||||||
deleteUser: (user: User, callback?: () => void) => {
|
|
||||||
dispatch(deleteUser(user, callback));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,10 +14,9 @@ import {
|
|||||||
getFetchUsersFailure
|
getFetchUsersFailure
|
||||||
} from "../modules/users";
|
} from "../modules/users";
|
||||||
|
|
||||||
import { Page, Paginator } from "@scm-manager/ui-components";
|
import { Page, CreateButton, 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 { getUsersLink } from "../../modules/indexResource";
|
import { getUsersLink } from "../../modules/indexResource";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -86,8 +85,9 @@ class Users extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderCreateButton() {
|
renderCreateButton() {
|
||||||
|
const { t } = this.props;
|
||||||
if (this.props.canAddUsers) {
|
if (this.props.canAddUsers) {
|
||||||
return <CreateUserButton />;
|
return <CreateButton label={t("users.createButton")} link="/users/add" />;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,18 +283,29 @@ $fa-font-path: "webfonts";
|
|||||||
}
|
}
|
||||||
.menu-list {
|
.menu-list {
|
||||||
a {
|
a {
|
||||||
border-radius: 0;
|
|
||||||
color: #333;
|
color: #333;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-top: 1px solid #eee;
|
|
||||||
border-left: 1px solid #eee;
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
color: $blue;
|
color: $blue;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:before {
|
> li {
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
|
||||||
|
li {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> a.is-active:before {
|
||||||
position: relative;
|
position: relative;
|
||||||
content: " ";
|
content: " ";
|
||||||
background: $blue;
|
background: $blue;
|
||||||
@@ -305,12 +316,43 @@ $fa-font-path: "webfonts";
|
|||||||
float: left;
|
float: left;
|
||||||
top: -16px;
|
top: -16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
border-radius: 0;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
border-left: 1px solid #eee;
|
||||||
|
border-right: 1px solid #eee;
|
||||||
}
|
}
|
||||||
}
|
> li:first-child {
|
||||||
> li:first-child > a {
|
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
li:last-child > a {
|
li:last-child {
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
div {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sub-menu li {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:before {
|
||||||
|
font-family: "Font Awesome 5 Free";
|
||||||
|
font-weight: 900;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
display: inline-block;
|
||||||
|
font-style: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
text-rendering: auto;
|
||||||
|
line-height: 1;
|
||||||
|
content: "\f105";
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user