Fluid layout
Reworked routing and breadcrumb mechanism. It allows resolved parameters in paths, which allows including names of entities in the breadcrumb. Secondary navigation which is aware of permissions.
This commit is contained in:
parent
86fce404a9
commit
602364caae
33 changed files with 808 additions and 907 deletions
|
@ -21,21 +21,17 @@ export default class CUD extends Component {
|
|||
|
||||
this.state = {};
|
||||
|
||||
if (props.edit) {
|
||||
this.state.entityId = parseInt(props.match.params.id);
|
||||
}
|
||||
|
||||
this.initForm();
|
||||
this.hasChildren = false;
|
||||
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
edit: PropTypes.bool
|
||||
action: PropTypes.string.isRequired,
|
||||
entity: PropTypes.object
|
||||
}
|
||||
|
||||
isEditGlobal() {
|
||||
return this.state.entityId === 1; /* Global namespace id */
|
||||
return this.props.entity && this.props.entity.id === 1; /* Global namespace id */
|
||||
}
|
||||
|
||||
isDelete() {
|
||||
|
@ -46,7 +42,7 @@ export default class CUD extends Component {
|
|||
for (let idx = 0; idx < data.length; idx++) {
|
||||
const entry = data[idx];
|
||||
|
||||
if (entry.key === this.state.entityId) {
|
||||
if (entry.key === this.props.entity.id) {
|
||||
if (entry.children.length > 0) {
|
||||
this.hasChildren = true;
|
||||
}
|
||||
|
@ -71,7 +67,7 @@ export default class CUD extends Component {
|
|||
root.expanded = true;
|
||||
}
|
||||
|
||||
if (this.props.edit && !this.isEditGlobal()) {
|
||||
if (this.props.entity && !this.isEditGlobal()) {
|
||||
this.removeNsIdSubtree(data);
|
||||
}
|
||||
|
||||
|
@ -81,14 +77,9 @@ export default class CUD extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
@withAsyncErrorHandler
|
||||
async loadFormValues() {
|
||||
await this.getFormValuesFromURL(`/rest/namespaces/${this.state.entityId}`);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.edit) {
|
||||
this.loadFormValues();
|
||||
if (this.props.entity) {
|
||||
this.getFormValuesFromEntity(this.props.entity);
|
||||
} else {
|
||||
this.populateFormValues({
|
||||
name: '',
|
||||
|
@ -122,12 +113,11 @@ export default class CUD extends Component {
|
|||
|
||||
async submitHandler() {
|
||||
const t = this.props.t;
|
||||
const edit = this.props.edit;
|
||||
|
||||
let sendMethod, url;
|
||||
if (edit) {
|
||||
if (this.props.entity) {
|
||||
sendMethod = FormSendMethod.PUT;
|
||||
url = `/rest/namespaces/${this.state.entityId}`
|
||||
url = `/rest/namespaces/${this.props.entity.id}`
|
||||
} else {
|
||||
sendMethod = FormSendMethod.POST;
|
||||
url = '/rest/namespaces'
|
||||
|
@ -188,23 +178,23 @@ export default class CUD extends Component {
|
|||
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
const edit = this.props.edit;
|
||||
const isEdit = !!this.props.entity;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!this.isEditGlobal() && !this.hasChildren && edit &&
|
||||
{!this.isEditGlobal() && !this.hasChildren && isEdit &&
|
||||
<DeleteModalDialog
|
||||
stateOwner={this}
|
||||
visible={this.props.match.params.action === 'delete'}
|
||||
deleteUrl={`/rest/namespaces/${this.state.entityId}`}
|
||||
cudUrl={`/namespaces/edit/${this.state.entityId}`}
|
||||
visible={this.props.action === 'delete'}
|
||||
deleteUrl={`/rest/namespaces/${this.props.entity.id}`}
|
||||
cudUrl={`/namespaces/${this.props.entity.id}/edit`}
|
||||
listUrl="/namespaces"
|
||||
deletingMsg={t('Deleting namespace ...')}
|
||||
deletedMsg={t('Namespace deleted')}
|
||||
onErrorAsync={::this.onDeleteError}/>
|
||||
}
|
||||
|
||||
<Title>{edit ? t('Edit Namespace') : t('Create Namespace')}</Title>
|
||||
<Title>{isEdit ? t('Edit Namespace') : t('Create Namespace')}</Title>
|
||||
|
||||
<Form stateOwner={this} onSubmitAsync={::this.submitHandler}>
|
||||
<InputField id="name" label={t('Name')}/>
|
||||
|
@ -215,7 +205,7 @@ export default class CUD extends Component {
|
|||
|
||||
<ButtonRow>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={t('Save')}/>
|
||||
{!this.isEditGlobal() && !this.hasChildren && edit && <NavButton className="btn-danger" icon="remove" label={t('Delete')} linkTo={`/namespaces/edit/${this.state.entityId}/delete`}/>}
|
||||
{!this.isEditGlobal() && !this.hasChildren && isEdit && <NavButton className="btn-danger" icon="remove" label={t('Delete')} linkTo={`/namespaces/${this.props.entity.id}/delete`}/>}
|
||||
</ButtonRow>
|
||||
</Form>
|
||||
</div>
|
||||
|
|
|
@ -4,13 +4,38 @@ import React, { Component } from 'react';
|
|||
import { translate } from 'react-i18next';
|
||||
import { requiresAuthenticatedUser, withPageHelpers, Title, Toolbar, NavButton } from '../lib/page';
|
||||
import { TreeTable } from '../lib/tree';
|
||||
import { withErrorHandling, withAsyncErrorHandler } from '../lib/error-handling';
|
||||
import axios from '../lib/axios';
|
||||
|
||||
@translate()
|
||||
@withErrorHandling
|
||||
@withPageHelpers
|
||||
@requiresAuthenticatedUser
|
||||
export default class List extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
@withAsyncErrorHandler
|
||||
async fetchPermissions() {
|
||||
const request = {
|
||||
createNamespace: {
|
||||
entityTypeId: 'namespace',
|
||||
requiredOperations: ['createNamespace']
|
||||
}
|
||||
};
|
||||
|
||||
const result = await axios.post('/rest/permissions-check', request);
|
||||
|
||||
this.setState({
|
||||
createPermitted: result.data.createNamespace
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchPermissions();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -21,15 +46,15 @@ export default class List extends Component {
|
|||
|
||||
if (node.data.permissions.includes('edit')) {
|
||||
actions.push({
|
||||
label: 'Edit',
|
||||
link: '/namespaces/edit/' + node.key
|
||||
label: <span className="glyphicon glyphicon-edit" aria-hidden="true" title="Edit"></span>,
|
||||
link: `/namespaces/${node.key}/edit`
|
||||
});
|
||||
}
|
||||
|
||||
if (node.data.permissions.includes('share')) {
|
||||
actions.push({
|
||||
label: 'Share',
|
||||
link: '/namespaces/share/' + node.key
|
||||
label: <span className="glyphicon glyphicon-share-alt" aria-hidden="true" title="Share"></span>,
|
||||
link: `/namespaces/${node.key}/share`
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -38,9 +63,11 @@ export default class List extends Component {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Toolbar>
|
||||
<NavButton linkTo="/namespaces/create" className="btn-primary" icon="plus" label={t('Create Namespace')}/>
|
||||
</Toolbar>
|
||||
{this.state.createPermitted &&
|
||||
<Toolbar>
|
||||
<NavButton linkTo="/namespaces/create" className="btn-primary" icon="plus" label={t('Create Namespace')}/>
|
||||
</Toolbar>
|
||||
}
|
||||
|
||||
<Title>{t('Namespaces')}</Title>
|
||||
|
||||
|
|
|
@ -20,20 +20,31 @@ const getStructure = t => ({
|
|||
link: '/namespaces',
|
||||
component: List,
|
||||
children: {
|
||||
edit : {
|
||||
title: t('Edit Namespace'),
|
||||
params: [':id', ':action?'],
|
||||
render: props => (<CUD edit {...props} />)
|
||||
':namespaceId([0-9]+)': {
|
||||
title: resolved => t('Namespace "{{name}}"', {name: resolved.namespace.name}),
|
||||
resolve: {
|
||||
namespace: params => `/rest/namespaces/${params.namespaceId}`
|
||||
},
|
||||
link: params => `/namespaces/${params.namespaceId}/edit`,
|
||||
navs: {
|
||||
':action(edit|delete)': {
|
||||
title: t('Edit'),
|
||||
link: params => `/namespaces/${params.namespaceId}/edit`,
|
||||
visible: resolved => resolved.namespace.permissions.includes('edit'),
|
||||
render: props => <CUD action={props.match.params.action} entity={props.resolved.namespace} />
|
||||
},
|
||||
share: {
|
||||
title: t('Share'),
|
||||
link: params => `/namespaces/${params.namespaceId}/share`,
|
||||
visible: resolved => resolved.namespace.permissions.includes('share'),
|
||||
render: props => <Share title={t('Share')} entity={props.resolved.namespace} entityTypeId="namespace" />
|
||||
}
|
||||
}
|
||||
},
|
||||
create : {
|
||||
title: t('Create Namespace'),
|
||||
render: props => (<CUD {...props} />)
|
||||
create: {
|
||||
title: t('Create'),
|
||||
render: props => <CUD action="create" />
|
||||
},
|
||||
share: {
|
||||
title: t('Share Namespace'),
|
||||
params: [':id'],
|
||||
render: props => (<Share title={entity => t('Share Namespace "{{name}}"', {name: entity.name})} getUrl={id => `/rest/namespaces/${id}`} entityTypeId="namespace" {...props} />)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue