mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
fix: cleanup edit application a bit
This commit is contained in:
parent
130110f5a4
commit
987fce309c
@ -92,6 +92,7 @@ exports[`renders correctly with permissions 1`] = `
|
|||||||
>
|
>
|
||||||
<react-mdl-Cell
|
<react-mdl-Cell
|
||||||
col={6}
|
col={6}
|
||||||
|
hidePhone={true}
|
||||||
phone={12}
|
phone={12}
|
||||||
tablet={4}
|
tablet={4}
|
||||||
>
|
>
|
||||||
@ -199,8 +200,7 @@ exports[`renders correctly with permissions 1`] = `
|
|||||||
subtitle={
|
subtitle={
|
||||||
<span>
|
<span>
|
||||||
123.123.123.123
|
123.123.123.123
|
||||||
last seen at
|
last seen at
|
||||||
|
|
||||||
<small>
|
<small>
|
||||||
02/23/2017, 03:56:49 PM
|
02/23/2017, 03:56:49 PM
|
||||||
</small>
|
</small>
|
||||||
@ -263,6 +263,7 @@ exports[`renders correctly without permission 1`] = `
|
|||||||
>
|
>
|
||||||
<react-mdl-Cell
|
<react-mdl-Cell
|
||||||
col={6}
|
col={6}
|
||||||
|
hidePhone={true}
|
||||||
phone={12}
|
phone={12}
|
||||||
tablet={4}
|
tablet={4}
|
||||||
>
|
>
|
||||||
@ -360,8 +361,7 @@ exports[`renders correctly without permission 1`] = `
|
|||||||
subtitle={
|
subtitle={
|
||||||
<span>
|
<span>
|
||||||
123.123.123.123
|
123.123.123.123
|
||||||
last seen at
|
last seen at
|
||||||
|
|
||||||
<small>
|
<small>
|
||||||
02/23/2017, 03:56:49 PM
|
02/23/2017, 03:56:49 PM
|
||||||
</small>
|
</small>
|
||||||
|
@ -1,63 +1,13 @@
|
|||||||
/* eslint react/no-multi-comp:off */
|
/* eslint react/no-multi-comp:off */
|
||||||
import React, { Component, PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Button, Card, CardActions, CardTitle, CardText, CardMenu, Icon, ProgressBar, Tabs, Tab } from 'react-mdl';
|
||||||
import {
|
import { IconLink, styles as commonStyles } from '../common';
|
||||||
Button,
|
|
||||||
Grid,
|
|
||||||
Cell,
|
|
||||||
Card,
|
|
||||||
CardActions,
|
|
||||||
CardTitle,
|
|
||||||
CardText,
|
|
||||||
CardMenu,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemContent,
|
|
||||||
Textfield,
|
|
||||||
Icon,
|
|
||||||
ProgressBar,
|
|
||||||
Tabs,
|
|
||||||
Tab,
|
|
||||||
Switch,
|
|
||||||
} from 'react-mdl';
|
|
||||||
import { IconLink, shorten, styles as commonStyles } from '../common';
|
|
||||||
import { formatFullDateTimeWithLocale } from '../common/util';
|
import { formatFullDateTimeWithLocale } from '../common/util';
|
||||||
import { CREATE_FEATURE, CREATE_STRATEGY, UPDATE_APPLICATION } from '../../permissions';
|
import { UPDATE_APPLICATION } from '../../permissions';
|
||||||
import icons from './icon-names';
|
import ApplicationView from './application-view';
|
||||||
import MySelect from '../common/select';
|
import ApplicationUpdate from './application-update';
|
||||||
|
|
||||||
class StatefulTextfield extends Component {
|
|
||||||
static propTypes = {
|
|
||||||
value: PropTypes.string,
|
|
||||||
label: PropTypes.string,
|
|
||||||
rows: PropTypes.number,
|
|
||||||
onBlur: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = { value: props.value };
|
|
||||||
this.setValue = function setValue(e) {
|
|
||||||
this.setState({ value: e.target.value });
|
|
||||||
}.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Textfield
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
label={this.props.label}
|
|
||||||
floatingLabel
|
|
||||||
rows={this.props.rows}
|
|
||||||
value={this.state.value}
|
|
||||||
onChange={this.setValue}
|
|
||||||
onBlur={this.props.onBlur}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClientApplications extends PureComponent {
|
class ClientApplications extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -71,17 +21,15 @@ class ClientApplications extends PureComponent {
|
|||||||
history: PropTypes.object.isRequired,
|
history: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor() {
|
||||||
super(props);
|
super();
|
||||||
this.state = { activeTab: 0 };
|
this.state = { activeTab: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.fetchApplication(this.props.appName);
|
this.props.fetchApplication(this.props.appName);
|
||||||
}
|
}
|
||||||
formatFullDateTime(v) {
|
formatFullDateTime = v => formatFullDateTimeWithLocale(v, this.props.location.locale);
|
||||||
return formatFullDateTimeWithLocale(v, this.props.location.locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteApplication = async evt => {
|
deleteApplication = async evt => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
@ -95,126 +43,19 @@ class ClientApplications extends PureComponent {
|
|||||||
return <ProgressBar indeterminate />;
|
return <ProgressBar indeterminate />;
|
||||||
}
|
}
|
||||||
const { application, storeApplicationMetaData, hasPermission } = this.props;
|
const { application, storeApplicationMetaData, hasPermission } = this.props;
|
||||||
const { appName, instances, strategies, seenToggles, url, description, icon = 'apps', color } = application;
|
const { appName, instances, strategies, seenToggles, url, description, icon = 'apps' } = application;
|
||||||
|
|
||||||
const content =
|
const content =
|
||||||
this.state.activeTab === 0 ? (
|
this.state.activeTab === 0 ? (
|
||||||
<Grid style={{ margin: 0 }}>
|
<ApplicationView
|
||||||
<Cell col={6} tablet={4} phone={12}>
|
strategies={strategies}
|
||||||
<h6> Toggles</h6>
|
instances={instances}
|
||||||
<hr />
|
seenToggles={seenToggles}
|
||||||
<List>
|
hasPermission={hasPermission}
|
||||||
{seenToggles.map(({ name, description, enabled, notFound }, i) =>
|
formatFullDateTime={this.formatFullDateTime}
|
||||||
notFound ? (
|
/>
|
||||||
<ListItem twoLine key={i}>
|
|
||||||
{hasPermission(CREATE_FEATURE) ? (
|
|
||||||
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
|
||||||
<Link to={`/features/create?name=${name}`}>{name}</Link>
|
|
||||||
</ListItemContent>
|
|
||||||
) : (
|
|
||||||
<ListItemContent icon={'report'} subtitle={'Missing'}>
|
|
||||||
{name}
|
|
||||||
</ListItemContent>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
|
||||||
) : (
|
|
||||||
<ListItem twoLine key={i}>
|
|
||||||
<ListItemContent
|
|
||||||
icon={
|
|
||||||
<span>
|
|
||||||
<Switch disabled checked={!!enabled} />
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
subtitle={shorten(description, 60)}
|
|
||||||
>
|
|
||||||
<Link to={`/features/view/${name}`}>{shorten(name, 50)}</Link>
|
|
||||||
</ListItemContent>
|
|
||||||
</ListItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
</Cell>
|
|
||||||
<Cell col={6} tablet={4} phone={12}>
|
|
||||||
<h6>Implemented strategies</h6>
|
|
||||||
<hr />
|
|
||||||
<List>
|
|
||||||
{strategies.map(({ name, description, notFound }, i) =>
|
|
||||||
notFound ? (
|
|
||||||
<ListItem twoLine key={`${name}-${i}`}>
|
|
||||||
{hasPermission(CREATE_STRATEGY) ? (
|
|
||||||
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
|
||||||
<Link to={`/strategies/create?name=${name}`}>{name}</Link>
|
|
||||||
</ListItemContent>
|
|
||||||
) : (
|
|
||||||
<ListItemContent icon={'report'} subtitle={'Missing'}>
|
|
||||||
{name}
|
|
||||||
</ListItemContent>
|
|
||||||
)}
|
|
||||||
</ListItem>
|
|
||||||
) : (
|
|
||||||
<ListItem twoLine key={`${name}-${i}`}>
|
|
||||||
<ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
|
|
||||||
<Link to={`/strategies/view/${name}`}>{shorten(name, 50)}</Link>
|
|
||||||
</ListItemContent>
|
|
||||||
</ListItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
</Cell>
|
|
||||||
<Cell col={12} tablet={12}>
|
|
||||||
<h6>{instances.length} Instances registered</h6>
|
|
||||||
<hr />
|
|
||||||
<List>
|
|
||||||
{instances.map(({ instanceId, clientIp, lastSeen, sdkVersion }, i) => (
|
|
||||||
<ListItem key={i} twoLine>
|
|
||||||
<ListItemContent
|
|
||||||
icon="timeline"
|
|
||||||
subtitle={
|
|
||||||
<span>
|
|
||||||
{clientIp} last seen at{' '}
|
|
||||||
<small>{this.formatFullDateTime(lastSeen)}</small>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{instanceId} {sdkVersion ? `(${sdkVersion})` : ''}
|
|
||||||
</ListItemContent>
|
|
||||||
</ListItem>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
</Cell>
|
|
||||||
</Grid>
|
|
||||||
) : (
|
) : (
|
||||||
<Grid>
|
<ApplicationUpdate application={application} storeApplicationMetaData={storeApplicationMetaData} />
|
||||||
<Cell col={6} tablet={12}>
|
|
||||||
<StatefulTextfield
|
|
||||||
value={url}
|
|
||||||
label="URL"
|
|
||||||
type="url"
|
|
||||||
onBlur={e => storeApplicationMetaData(appName, 'url', e.target.value)}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<StatefulTextfield
|
|
||||||
value={description}
|
|
||||||
label="Description"
|
|
||||||
rows={5}
|
|
||||||
onBlur={e => storeApplicationMetaData(appName, 'description', e.target.value)}
|
|
||||||
/>
|
|
||||||
</Cell>
|
|
||||||
<Cell col={6} tablet={12}>
|
|
||||||
<MySelect
|
|
||||||
label="Icon"
|
|
||||||
options={icons.map(v => ({ key: v, label: v }))}
|
|
||||||
value={icon}
|
|
||||||
onChange={e => storeApplicationMetaData(appName, 'icon', e.target.value)}
|
|
||||||
filled
|
|
||||||
/>
|
|
||||||
<StatefulTextfield
|
|
||||||
value={color}
|
|
||||||
label="Color"
|
|
||||||
onBlur={e => storeApplicationMetaData(appName, 'color', e.target.value)}
|
|
||||||
/>
|
|
||||||
</Cell>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
46
frontend/src/component/application/application-update.jsx
Normal file
46
frontend/src/component/application/application-update.jsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Grid, Cell } from 'react-mdl';
|
||||||
|
import StatefulTextfield from './stateful-textfield';
|
||||||
|
import icons from './icon-names';
|
||||||
|
import MySelect from '../common/select';
|
||||||
|
|
||||||
|
function ApplicationUpdate({ application, storeApplicationMetaData }) {
|
||||||
|
const { appName, icon, url, description } = application;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
<Cell col={12}>
|
||||||
|
<MySelect
|
||||||
|
label="Icon"
|
||||||
|
options={icons.map(v => ({ key: v, label: v }))}
|
||||||
|
value={icon || 'apps'}
|
||||||
|
onChange={e => storeApplicationMetaData(appName, 'icon', e.target.value)}
|
||||||
|
filled
|
||||||
|
/>
|
||||||
|
<StatefulTextfield
|
||||||
|
value={url}
|
||||||
|
label="Application URL"
|
||||||
|
placeholder="https://example.com"
|
||||||
|
type="url"
|
||||||
|
onBlur={e => storeApplicationMetaData(appName, 'url', e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<StatefulTextfield
|
||||||
|
value={description}
|
||||||
|
label="Description"
|
||||||
|
rows={2}
|
||||||
|
onBlur={e => storeApplicationMetaData(appName, 'description', e.target.value)}
|
||||||
|
/>
|
||||||
|
</Cell>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationUpdate.propTypes = {
|
||||||
|
application: PropTypes.object.isRequired,
|
||||||
|
storeApplicationMetaData: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ApplicationUpdate;
|
104
frontend/src/component/application/application-view.jsx
Normal file
104
frontend/src/component/application/application-view.jsx
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Grid, Cell, List, ListItem, ListItemContent, Switch } from 'react-mdl';
|
||||||
|
import { shorten } from '../common';
|
||||||
|
import { CREATE_FEATURE, CREATE_STRATEGY } from '../../permissions';
|
||||||
|
|
||||||
|
function ApplicationView({ seenToggles, hasPermission, strategies, instances, formatFullDateTime }) {
|
||||||
|
return (
|
||||||
|
<Grid style={{ margin: 0 }}>
|
||||||
|
<Cell col={6} tablet={4} phone={12} hidePhone>
|
||||||
|
<h6> Toggles</h6>
|
||||||
|
<hr />
|
||||||
|
<List>
|
||||||
|
{seenToggles.map(({ name, description, enabled, notFound }, i) =>
|
||||||
|
notFound ? (
|
||||||
|
<ListItem twoLine key={i}>
|
||||||
|
{hasPermission(CREATE_FEATURE) ? (
|
||||||
|
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
||||||
|
<Link to={`/features/create?name=${name}`}>{name}</Link>
|
||||||
|
</ListItemContent>
|
||||||
|
) : (
|
||||||
|
<ListItemContent icon={'report'} subtitle={'Missing'}>
|
||||||
|
{name}
|
||||||
|
</ListItemContent>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
) : (
|
||||||
|
<ListItem twoLine key={i}>
|
||||||
|
<ListItemContent
|
||||||
|
icon={
|
||||||
|
<span>
|
||||||
|
<Switch disabled checked={!!enabled} />
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
subtitle={shorten(description, 60)}
|
||||||
|
>
|
||||||
|
<Link to={`/features/view/${name}`}>{shorten(name, 50)}</Link>
|
||||||
|
</ListItemContent>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
</Cell>
|
||||||
|
<Cell col={6} tablet={4} phone={12}>
|
||||||
|
<h6>Implemented strategies</h6>
|
||||||
|
<hr />
|
||||||
|
<List>
|
||||||
|
{strategies.map(({ name, description, notFound }, i) =>
|
||||||
|
notFound ? (
|
||||||
|
<ListItem twoLine key={`${name}-${i}`}>
|
||||||
|
{hasPermission(CREATE_STRATEGY) ? (
|
||||||
|
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
||||||
|
<Link to={`/strategies/create?name=${name}`}>{name}</Link>
|
||||||
|
</ListItemContent>
|
||||||
|
) : (
|
||||||
|
<ListItemContent icon={'report'} subtitle={'Missing'}>
|
||||||
|
{name}
|
||||||
|
</ListItemContent>
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
) : (
|
||||||
|
<ListItem twoLine key={`${name}-${i}`}>
|
||||||
|
<ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
|
||||||
|
<Link to={`/strategies/view/${name}`}>{shorten(name, 50)}</Link>
|
||||||
|
</ListItemContent>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
</Cell>
|
||||||
|
<Cell col={12} tablet={12}>
|
||||||
|
<h6>{instances.length} Instances registered</h6>
|
||||||
|
<hr />
|
||||||
|
<List>
|
||||||
|
{instances.map(({ instanceId, clientIp, lastSeen, sdkVersion }, i) => (
|
||||||
|
<ListItem key={i} twoLine>
|
||||||
|
<ListItemContent
|
||||||
|
icon="timeline"
|
||||||
|
subtitle={
|
||||||
|
<span>
|
||||||
|
{clientIp} last seen at <small>{formatFullDateTime(lastSeen)}</small>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{instanceId} {sdkVersion ? `(${sdkVersion})` : ''}
|
||||||
|
</ListItemContent>
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Cell>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationView.propTypes = {
|
||||||
|
instances: PropTypes.array.isRequired,
|
||||||
|
seenToggles: PropTypes.array.isRequired,
|
||||||
|
strategies: PropTypes.array.isRequired,
|
||||||
|
hasPermission: PropTypes.func.isRequired,
|
||||||
|
formatFullDateTime: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ApplicationView;
|
35
frontend/src/component/application/stateful-textfield.js
Normal file
35
frontend/src/component/application/stateful-textfield.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Textfield } from 'react-mdl';
|
||||||
|
|
||||||
|
function StatefulTextfield({ value, label, placeholder, rows, onBlur }) {
|
||||||
|
const [localValue, setLocalValue] = useState(value);
|
||||||
|
|
||||||
|
const onChange = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLocalValue(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Textfield
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
label={label}
|
||||||
|
placeholder={placeholder}
|
||||||
|
floatingLabel
|
||||||
|
rows={rows}
|
||||||
|
value={localValue}
|
||||||
|
onChange={onChange}
|
||||||
|
onBlur={onBlur}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatefulTextfield.propTypes = {
|
||||||
|
value: PropTypes.string,
|
||||||
|
label: PropTypes.string,
|
||||||
|
placeholder: PropTypes.string,
|
||||||
|
rows: PropTypes.number,
|
||||||
|
onBlur: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StatefulTextfield;
|
@ -6,7 +6,6 @@ import { Icon, FABButton, Menu, MenuItem, Card, CardActions, List } from 'react-
|
|||||||
import Feature from './feature-list-item-component';
|
import Feature from './feature-list-item-component';
|
||||||
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
||||||
import SearchField from '../common/search-field';
|
import SearchField from '../common/search-field';
|
||||||
import styles from './feature.scss';
|
|
||||||
import { CREATE_FEATURE } from '../../permissions';
|
import { CREATE_FEATURE } from '../../permissions';
|
||||||
|
|
||||||
export default class FeatureListComponent extends React.Component {
|
export default class FeatureListComponent extends React.Component {
|
||||||
|
Loading…
Reference in New Issue
Block a user