Skip to content

Commit

Permalink
Added initial UI for property list
Browse files Browse the repository at this point in the history
Former-commit-id: 099ccb8
  • Loading branch information
rmathis committed Aug 4, 2022
1 parent 146b97b commit cdc4207
Show file tree
Hide file tree
Showing 12 changed files with 462 additions and 1 deletion.
1 change: 1 addition & 0 deletions ui/public/assets/data/properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
30 changes: 30 additions & 0 deletions ui/public/assets/schema/properties/property.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"type": "object",
"required": [
"property",
"value"
],
"properties": {
"property": {
"title": "label.property-key",
"description": "tooltip.property-key",
"type": "string",
"minLength": 1,
"maxLength": 255
},
"description": {
"title": "label.property-descr",
"description": "tooltip.property-descr",
"type": "string",
"minLength": 1,
"maxLength": 255
},
"value": {
"title": "label.property-value",
"description": "tooltip.property-value",
"type": "string",
"minLength": 1,
"maxLength": 255
}
}
}
6 changes: 6 additions & 0 deletions ui/src/app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { Roles } from './admin/Roles';
import { Groups } from './admin/Groups';
import { BASE_PATH } from './App.constant';
import { ProtectRoute } from './core/components/ProtectRoute';
import { Properties } from './admin/Properties';


function App() {
Expand Down Expand Up @@ -108,6 +109,11 @@ function App() {
<Groups />
</ProtectRoute>
} />
<Route path="/properties" render={() =>
<ProtectRoute redirectTo="/dashboard">
<Properties />
</ProtectRoute>
} />
<Route path="*">
<Redirect to="/dashboard" />
</Route>
Expand Down
34 changes: 34 additions & 0 deletions ui/src/app/admin/Properties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Switch, Route, useRouteMatch, Redirect } from 'react-router-dom';
import { PropertiesProvider } from './hoc/PropertiesProvider';
import { NewProperty } from './container/NewProperty';
import { EditProperty } from './container/EditProperty';
import { PropertyList } from './container/PropertyList';

export function Properties() {

let { path, url } = useRouteMatch();

return (
<>
<Switch>
<Route path={`${path}/list`} render={() =>
<PropertiesProvider>
{(properties, onDelete) =>
<PropertyList properties={properties} onDelete={onDelete} />
}
</PropertiesProvider>
} />
<Route path={`${path}/new`} render={() =>
<NewProperty />
} />
<Route path={`${path}/:id/edit`} render={() =>
<EditProperty />
} />
<Route path={`${path}`} exact render={() =>
<Redirect to={`${url}/list`} />
} />
</Switch>
</>
);
}
56 changes: 56 additions & 0 deletions ui/src/app/admin/component/PropertyForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import Button from 'react-bootstrap/Button';
import Form from '../../form/Form';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner, faSave } from '@fortawesome/free-solid-svg-icons';
import Translate from '../../i18n/components/translate';

import { usePropertyUiSchema } from '../hooks';
import { FormContext, setFormDataAction, setFormErrorAction } from '../../form/FormManager';

export function PropertyForm({ property = {}, errors = [], loading = false, schema, onSave, onCancel }) {

const { dispatch } = React.useContext(FormContext);
const onChange = ({ formData, errors }) => {
dispatch(setFormDataAction(formData));
dispatch(setFormErrorAction(errors));
};

const uiSchema = usePropertyUiSchema();

return (<>
<div className="container-fluid">
<div className="d-flex justify-content-end align-items-center">
<React.Fragment>
<Button variant="info" className="me-2"
type="button"
onClick={() => onSave(property)}
disabled={errors.length > 0 || loading}
aria-label="Save changes to the metadata source. You will return to the dashboard">
<FontAwesomeIcon icon={loading ? faSpinner : faSave} pulse={loading} />&nbsp;
<Translate value="action.save">Save</Translate>
</Button>
<Button variant="secondary"
type="button"
onClick={() => onCancel()} aria-label="Cancel changes, go back to dashboard">
<Translate value="action.cancel">Cancel</Translate>
</Button>
</React.Fragment>
</div>
<hr />
<div className="row">
<div className="col-12 col-lg-6 order-2">
<Form formData={property}
noHtml5Validate={true}
onChange={(form) => onChange(form)}
schema={schema}
uiSchema={uiSchema}
liveValidate={true}>
<></>
</Form>
</div>
</div>
</div>
</>)
}
/**/
92 changes: 92 additions & 0 deletions ui/src/app/admin/container/EditProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from 'react';

import { Prompt, useHistory } from 'react-router-dom';
import { useParams } from 'react-router-dom';
import Translate from '../../i18n/components/translate';
import { useProperties } from '../hooks';
import { Schema } from '../../form/Schema';
import { FormManager } from '../../form/FormManager';

import { PropertyForm } from '../component/PropertyForm';
import { PropertyProvider } from '../hoc/PropertyProvider';
import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';
import { BASE_PATH } from '../../App.constant';

export function EditProperty() {

const { id } = useParams();

const notifier = useNotificationDispatcher();
const translator = useTranslator();

const history = useHistory();

const { put, response, loading } = useProperties();

const [blocking, setBlocking] = React.useState(false);

async function save(property) {
let toast;
const resp = await put(`/${property.resourceId}`, property);
if (response.ok) {
gotoDetail({ refresh: true });
toast = createNotificationAction(`Updated property successfully.`, NotificationTypes.SUCCESS);
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
};

const cancel = () => {
gotoDetail();
};

const gotoDetail = (state = null) => {
setBlocking(false);
history.push(`/properties`, state);
};

return (
<div className="container-fluid p-3">
<Prompt
when={blocking}
message={location =>
`message.unsaved-editor`
}
/>
<section className="section" tabIndex="0">
<div className="section-header bg-info p-2 text-white">
<div className="row justify-content-between">
<div className="col-md-12">
<span className="lead"><Translate value="label.edit-property">Edit property</Translate></span>
</div>
</div>
</div>
<div className="section-body p-4 border border-top-0 border-info">
<PropertyProvider id={id}>
{(property) =>
<Schema path={`/${BASE_PATH}assets/schema/properties/property.json`}>
{(schema) =>
<>{property &&
<FormManager initial={property}>
{(data, errors) =>
<PropertyForm
property={data}
errors={errors}
schema={schema}
loading={loading}
onSave={(data) => save(data)}
onCancel={() => cancel()} />}
</FormManager>
}</>}
</Schema>
}
</PropertyProvider>
</div>
</section>
</div>
);
}
80 changes: 80 additions & 0 deletions ui/src/app/admin/container/NewProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';

import { Prompt, useHistory } from 'react-router-dom';
import Translate from '../../i18n/components/translate';
import { useProperties } from '../hooks';
import { Schema } from '../../form/Schema';
import { FormManager } from '../../form/FormManager';
import { PropertyForm } from '../component/PropertyForm';

import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';
import { BASE_PATH } from '../../App.constant';

export function NewProperty() {
const history = useHistory();
const notifier = useNotificationDispatcher();
const translator = useTranslator();

const { post, response, loading } = useProperties({});

const [blocking, setBlocking] = React.useState(false);

async function save(property) {
let toast;
const resp = await post(``, property);
if (response.ok) {
gotoDetail({ refresh: true });
toast = createNotificationAction(`Added property successfully.`, NotificationTypes.SUCCESS);
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
};

const cancel = () => {
gotoDetail();
};

const gotoDetail = (state = null) => {
setBlocking(false);
history.push(`/properties`, state);
};

return (
<div className="container-fluid p-3">
<Prompt
when={blocking}
message={location =>
`message.unsaved-editor`
}
/>
<section className="section" tabIndex="0">
<div className="section-header bg-info p-2 text-white">
<div className="row justify-content-between">
<div className="col-md-12">
<span className="lead"><Translate value="label.new-property">Add a new property</Translate></span>
</div>
</div>
</div>
<div className="section-body p-4 border border-top-0 border-info">
<Schema path={`/${BASE_PATH}assets/schema/properties/property.json`}>
{(schema) =>
<FormManager initial={{}}>
{(data, errors) =>
<PropertyForm
property={data}
errors={errors}
schema={schema}
loading={loading}
onSave={(data) => save(data)}
onCancel={() => cancel()} />}
</FormManager>}
</Schema>
</div>
</section>
</div>
);
}
Loading

0 comments on commit cdc4207

Please sign in to comment.