Skip to content

Commit

Permalink
initial commit for groups
Browse files Browse the repository at this point in the history
  • Loading branch information
rmathis committed Jun 22, 2021
1 parent 8ed495a commit db73700
Show file tree
Hide file tree
Showing 16 changed files with 345 additions and 5 deletions.
15 changes: 15 additions & 0 deletions ui/public/assets/schema/groups/group.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"title": "label.group-name",
"description": "tooltip.group-name",
"type": "string",
"minLength": 1,
"maxLength": 255
}
}
}
4 changes: 3 additions & 1 deletion ui/src/app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Filter } from './metadata/Filter';
import { Contention } from './metadata/contention/ContentionContext';
import { SessionModal } from './core/user/SessionModal';
import Button from 'react-bootstrap/Button';
import { Groups } from './admin/Groups';


function App() {
Expand Down Expand Up @@ -76,8 +77,9 @@ function App() {
<Route path="/metadata/source/new" component={NewSource} />
<Route path="/metadata/provider/new" component={NewProvider} />
<Route path="/metadata/attributes" component={Attribute} />
<Route path={`/metadata/provider/:id/filter`} component={Filter} />
<Route path="/metadata/provider/:id/filter" component={Filter} />
<Route path="/metadata/:type/:id" component={Metadata} />
<Route path="/groups" component={Groups} />
<Route path="*">
<Redirect to="/dashboard" />
</Route>
Expand Down
32 changes: 32 additions & 0 deletions ui/src/app/admin/Groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { Switch, Route, useRouteMatch, Redirect } from 'react-router-dom';
import { GroupsProvider } from './hoc/GroupsProvider';
import { NewGroup } from './container/NewGroup';
import { EditGroup } from './container/EditGroup';
import { GroupsList } from './container/GroupsList';

export function Groups() {

let { path } = useRouteMatch();

return (
<>
<Switch>
<Route path={`${path}/list`} render={() =>
<GroupsProvider>
{(groups, onDelete) =>
<GroupsList groups={groups} onDelete={onDelete} />
}
</GroupsProvider>
} />
<Route path={`${path}/new`} render={() =>
<NewGroup />
} />
<Route path={`${path}/:id/edit`} render={() =>
<EditGroup />
} />
<Redirect exact path={`${path}`} to={`${path}/list`} />
</Switch>
</>
);
}
69 changes: 69 additions & 0 deletions ui/src/app/admin/component/GroupForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import Button from 'react-bootstrap/Button';
import Form from '@rjsf/bootstrap-4';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner, faSave } from '@fortawesome/free-solid-svg-icons';
import Translate from '../../i18n/components/translate';

import { useGroupUiSchema } from '../hooks';
import { fields, widgets } from '../../form/component';
import { templates } from '../../form/component';

function ErrorListTemplate() {
return (<></>);
}

export function GroupForm ({schema}) {

const [errors, setErrors] = React.useState([]);
const [loading, setLoading] = React.useState(false);
const [metadata, setMetadata] = React.useState({});

const save = () => { };
const cancel = () => { };
const onChange = () => { };

const uiSchema = useGroupUiSchema();

return (<>
<div className="container-fluid">
<div className="d-flex justify-content-end align-items-center">
<React.Fragment>
<Button variant="info" className="mr-2"
type="button"
onClick={() => save()}
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={() => cancel()} 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-12 order-2">
<Form formData={metadata}
noHtml5Validate={true}
onChange={(form) => onChange(form)}
schema={schema}
uiSchema={uiSchema}
FieldTemplate={templates.FieldTemplate}
ObjectFieldTemplate={templates.ObjectFieldTemplate}
ArrayFieldTemplate={templates.ArrayFieldTemplate}
fields={fields}
widgets={widgets}
liveValidate={true}
ErrorList={ErrorListTemplate}>
<></>
</Form>
</div>
</div>
</div>
</>)
}
/**/
5 changes: 5 additions & 0 deletions ui/src/app/admin/container/EditGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

export function EditGroup () {
return (<>Edit Group</>);
}
79 changes: 79 additions & 0 deletions ui/src/app/admin/container/GroupsList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';
import { faEdit, faPlusCircle, faTrash } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import Button from 'react-bootstrap/Button';
import { Link } from 'react-router-dom';

import { Translate } from '../../i18n/components/translate';
import { useTranslator } from '../../i18n/hooks';

import { DeleteConfirmation } from '../../core/components/DeleteConfirmation';

export function GroupsList({ groups, onDelete }) {

const translator = useTranslator();

const remove = (id) => {
onDelete(id);
}

return (
<DeleteConfirmation title={`message.delete-attribute-title`} body={`message.delete-attribute-body`}>
{(block) =>
<div className="container-fluid p-3">
<section className="section">
<div className="section-body border border-top-0 border-primary">
<div className="section-header bg-primary p-2 text-light">
<span className="lead">
<Translate value="label.groups-management">Groups Management</Translate>
</span>
</div>
<div className="p-3">
<div className="d-flex justify-content-end w-100">
<Link to="./new" className="btn btn-sm btn-success">
<FontAwesomeIcon icon={faPlusCircle} /> &nbsp;
<Translate value="action.add-new-group">Add new group</Translate>
</Link>
</div>
<div className="table-responsive mt-3">
<table className="table table-striped w-100 table-hover">
<thead>
<tr>
<th>
<Translate value="label.attribute-name">Group Name</Translate>
</th>
<th><span className="sr-only"><Translate value="label.actions">Actions</Translate></span></th>
</tr>
</thead>
<tbody>
{groups?.length && groups.map((group, i) =>
<tr key={i}>
<td>{group.name}</td>
<td className="text-right">
<Link to={`../attributes/${group.name}/edit`} className="btn btn-link text-primary">
<FontAwesomeIcon icon={faEdit} size="lg" />
<span className="sr-only">
<Translate value="action.edit">Edit</Translate>
</span>
</Link>
<Button variant="link" className="text-danger" onClick={() => block(() => remove(group.name))}>
<FontAwesomeIcon icon={faTrash} size="lg" />
<span className="sr-only">
<Translate value="action.delete">Delete</Translate>
</span>
</Button>
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
</section>
</div>
}
</DeleteConfirmation>
);
}
59 changes: 59 additions & 0 deletions ui/src/app/admin/container/NewGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';

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

import { GroupForm } from '../component/GroupForm';

export function NewGroup() {
const history = useHistory();

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

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

async function save(metadata) {
await post(``, metadata);
if (response.ok) {
gotoDetail({ refresh: true });
}
};

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

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

const [group, setGroup] = React.useState({});

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="display-6"><Translate value="label.new-group">Add a new group</Translate></span>
</div>
</div>
</div>
<div className="section-body p-4 border border-top-0 border-info">
<Schema path={`/assets/schema/groups/group.json`}>
{(schema) => <GroupForm group={group} schema={schema} /> }
</Schema>
</div>
</section>
</div>
);
}
35 changes: 35 additions & 0 deletions ui/src/app/admin/hoc/GroupsProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import { useGroups } from '../hooks';

export function GroupsProvider({ children }) {

const [groups, setGroups] = React.useState([
{
name: 'foo'
}
]);


const { get, del, response } = useGroups({
cachePolicy: 'no-cache'
});

async function loadGroups() {
const list = await get(``);
if (response.ok) {
setGroups(list);
}
}

async function removeGroup(id) {
await del(`/${id}`);
if (response.ok) {
loadGroups();
}
}

/*eslint-disable react-hooks/exhaustive-deps*/
// React.useEffect(() => { loadGroups() }, []);

return (<>{children(groups, removeGroup)}</>);
}
16 changes: 16 additions & 0 deletions ui/src/app/admin/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import useFetch from 'use-http';
import API_BASE_PATH from '../App.constant';

export function useGroups () {
return useFetch(`${API_BASE_PATH}/groups`);
}

export function useGroup() {
return useFetch(`${API_BASE_PATH}/group`);
}

export function useGroupUiSchema () {
return {

};
}
6 changes: 5 additions & 1 deletion ui/src/app/core/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Navbar from 'react-bootstrap/Navbar';
import Dropdown from 'react-bootstrap/Dropdown';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTh, faSignOutAlt, faPlusCircle, faCube, faCubes } from '@fortawesome/free-solid-svg-icons';
import { faTh, faSignOutAlt, faPlusCircle, faCube, faCubes, faUsersCog } from '@fortawesome/free-solid-svg-icons';

import Translate from '../../i18n/components/translate';
import { useTranslator } from '../../i18n/hooks';
Expand Down Expand Up @@ -41,6 +41,10 @@ export function Header () {
<FontAwesomeIcon icon={faCube} className="mr-2" />
<Translate value="action.custom-entity-attributes" />
</Dropdown.Item>
<Dropdown.Item as={Link} to="/groups" className="text-primary py-2">
<FontAwesomeIcon icon={faUsersCog} className="mr-2" />
<Translate value="action.groups" />
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
}
Expand Down
24 changes: 24 additions & 0 deletions ui/src/app/form/Schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import useFetch from 'use-http';

export function Schema({ path, children }) {

const [schema, setSchema] = React.useState({});


const { get, response } = useFetch(path, {
cachePolicy: 'no-cache'
});

async function loadSchema() {
const list = await get(``);
if (response.ok) {
setSchema(list);
}
}

/*eslint-disable react-hooks/exhaustive-deps*/
React.useEffect(() => { loadSchema() }, []);

return (<>{children(schema)}</>);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { useMetadataFilters } from '../../../hooks/api';
import { DeleteConfirmation } from '../../../component/DeleteConfirmation';
import { DeleteConfirmation } from '../../../../core/components/DeleteConfirmation';

export const MetadataFiltersContext = React.createContext();

Expand Down
Loading

0 comments on commit db73700

Please sign in to comment.