From db7370038e5b9b9aaeb70d5525049d7c110dc7c5 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 22 Jun 2021 10:21:40 -0700 Subject: [PATCH] initial commit for groups --- ui/public/assets/schema/groups/group.json | 15 ++++ ui/src/app/App.js | 4 +- ui/src/app/admin/Groups.js | 32 ++++++++ ui/src/app/admin/component/GroupForm.js | 69 ++++++++++++++++ ui/src/app/admin/container/EditGroup.js | 5 ++ ui/src/app/admin/container/GroupsList.js | 79 +++++++++++++++++++ ui/src/app/admin/container/NewGroup.js | 59 ++++++++++++++ ui/src/app/admin/hoc/GroupsProvider.js | 35 ++++++++ ui/src/app/admin/hooks.js | 16 ++++ .../components}/DeleteConfirmation.js | 0 .../components}/DeleteConfirmation.test.js | 0 ui/src/app/core/components/Header.js | 6 +- ui/src/app/form/Schema.js | 24 ++++++ .../filter/component/MetadataFilters.js | 2 +- ui/src/app/metadata/hooks/api.js | 2 +- .../metadata/view/MetadataAttributeList.js | 2 +- 16 files changed, 345 insertions(+), 5 deletions(-) create mode 100644 ui/public/assets/schema/groups/group.json create mode 100644 ui/src/app/admin/Groups.js create mode 100644 ui/src/app/admin/component/GroupForm.js create mode 100644 ui/src/app/admin/container/EditGroup.js create mode 100644 ui/src/app/admin/container/GroupsList.js create mode 100644 ui/src/app/admin/container/NewGroup.js create mode 100644 ui/src/app/admin/hoc/GroupsProvider.js create mode 100644 ui/src/app/admin/hooks.js rename ui/src/app/{metadata/component => core/components}/DeleteConfirmation.js (100%) rename ui/src/app/{metadata/component => core/components}/DeleteConfirmation.test.js (100%) create mode 100644 ui/src/app/form/Schema.js diff --git a/ui/public/assets/schema/groups/group.json b/ui/public/assets/schema/groups/group.json new file mode 100644 index 000000000..088d861f6 --- /dev/null +++ b/ui/public/assets/schema/groups/group.json @@ -0,0 +1,15 @@ +{ + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "title": "label.group-name", + "description": "tooltip.group-name", + "type": "string", + "minLength": 1, + "maxLength": 255 + } + } +} \ No newline at end of file diff --git a/ui/src/app/App.js b/ui/src/app/App.js index 097c0095d..58456350d 100644 --- a/ui/src/app/App.js +++ b/ui/src/app/App.js @@ -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() { @@ -76,8 +77,9 @@ function App() { - + + diff --git a/ui/src/app/admin/Groups.js b/ui/src/app/admin/Groups.js new file mode 100644 index 000000000..ec69c4f61 --- /dev/null +++ b/ui/src/app/admin/Groups.js @@ -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 ( + <> + + + + {(groups, onDelete) => + + } + + } /> + + + } /> + + + } /> + + + + ); +} \ No newline at end of file diff --git a/ui/src/app/admin/component/GroupForm.js b/ui/src/app/admin/component/GroupForm.js new file mode 100644 index 000000000..85a08d587 --- /dev/null +++ b/ui/src/app/admin/component/GroupForm.js @@ -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 (<> +
+
+ + + + +
+
+
+
+
onChange(form)} + schema={schema} + uiSchema={uiSchema} + FieldTemplate={templates.FieldTemplate} + ObjectFieldTemplate={templates.ObjectFieldTemplate} + ArrayFieldTemplate={templates.ArrayFieldTemplate} + fields={fields} + widgets={widgets} + liveValidate={true} + ErrorList={ErrorListTemplate}> + <> +
+
+
+
+ ) +} +/**/ \ No newline at end of file diff --git a/ui/src/app/admin/container/EditGroup.js b/ui/src/app/admin/container/EditGroup.js new file mode 100644 index 000000000..355a1d6e2 --- /dev/null +++ b/ui/src/app/admin/container/EditGroup.js @@ -0,0 +1,5 @@ +import React from 'react'; + +export function EditGroup () { + return (<>Edit Group); +} \ No newline at end of file diff --git a/ui/src/app/admin/container/GroupsList.js b/ui/src/app/admin/container/GroupsList.js new file mode 100644 index 000000000..023b86ff5 --- /dev/null +++ b/ui/src/app/admin/container/GroupsList.js @@ -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 ( + + {(block) => +
+
+
+
+ + Groups Management + +
+
+
+ +   + Add new group + +
+
+ + + + + + + + + {groups?.length && groups.map((group, i) => + + + + + )} + +
+ Group Name + Actions
{group.name} + + + + Edit + + + +
+
+
+
+
+
+ } +
+ ); +} \ No newline at end of file diff --git a/ui/src/app/admin/container/NewGroup.js b/ui/src/app/admin/container/NewGroup.js new file mode 100644 index 000000000..ab5e7d3ee --- /dev/null +++ b/ui/src/app/admin/container/NewGroup.js @@ -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 ( +
+ + `message.unsaved-editor` + } + /> +
+
+
+
+ Add a new group +
+
+
+
+ + {(schema) => } + +
+
+
+ ); +} \ No newline at end of file diff --git a/ui/src/app/admin/hoc/GroupsProvider.js b/ui/src/app/admin/hoc/GroupsProvider.js new file mode 100644 index 000000000..a9d47131b --- /dev/null +++ b/ui/src/app/admin/hoc/GroupsProvider.js @@ -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)}); +} \ No newline at end of file diff --git a/ui/src/app/admin/hooks.js b/ui/src/app/admin/hooks.js new file mode 100644 index 000000000..d071fb5e4 --- /dev/null +++ b/ui/src/app/admin/hooks.js @@ -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 { + + }; +} \ No newline at end of file diff --git a/ui/src/app/metadata/component/DeleteConfirmation.js b/ui/src/app/core/components/DeleteConfirmation.js similarity index 100% rename from ui/src/app/metadata/component/DeleteConfirmation.js rename to ui/src/app/core/components/DeleteConfirmation.js diff --git a/ui/src/app/metadata/component/DeleteConfirmation.test.js b/ui/src/app/core/components/DeleteConfirmation.test.js similarity index 100% rename from ui/src/app/metadata/component/DeleteConfirmation.test.js rename to ui/src/app/core/components/DeleteConfirmation.test.js diff --git a/ui/src/app/core/components/Header.js b/ui/src/app/core/components/Header.js index 54d0c402c..ca22fe12f 100644 --- a/ui/src/app/core/components/Header.js +++ b/ui/src/app/core/components/Header.js @@ -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'; @@ -41,6 +41,10 @@ export function Header () { + + + + } diff --git a/ui/src/app/form/Schema.js b/ui/src/app/form/Schema.js new file mode 100644 index 000000000..92aefe325 --- /dev/null +++ b/ui/src/app/form/Schema.js @@ -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)}); +} \ No newline at end of file diff --git a/ui/src/app/metadata/domain/filter/component/MetadataFilters.js b/ui/src/app/metadata/domain/filter/component/MetadataFilters.js index 13a22b53c..4e5ff8a98 100644 --- a/ui/src/app/metadata/domain/filter/component/MetadataFilters.js +++ b/ui/src/app/metadata/domain/filter/component/MetadataFilters.js @@ -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(); diff --git a/ui/src/app/metadata/hooks/api.js b/ui/src/app/metadata/hooks/api.js index d951b66a4..f628e847d 100644 --- a/ui/src/app/metadata/hooks/api.js +++ b/ui/src/app/metadata/hooks/api.js @@ -24,7 +24,7 @@ export function getMetadataPath(type) { } export function useNonAdminSources() { - return useFetch(`/${getMetadataPath('source')}/disabledNonAdmin`); + return useFetch(`${API_BASE_PATH}${getMetadataPath('source')}/disabledNonAdmin`); } export function getMetadataListPath(type) { diff --git a/ui/src/app/metadata/view/MetadataAttributeList.js b/ui/src/app/metadata/view/MetadataAttributeList.js index b72c3c0e7..bc912118a 100644 --- a/ui/src/app/metadata/view/MetadataAttributeList.js +++ b/ui/src/app/metadata/view/MetadataAttributeList.js @@ -8,7 +8,7 @@ import { Link } from 'react-router-dom'; import { Translate } from '../../i18n/components/translate'; import { useTranslator } from '../../i18n/hooks'; -import { DeleteConfirmation } from '../component/DeleteConfirmation'; +import { DeleteConfirmation } from '../../core/components/DeleteConfirmation'; export function MetadataAttributeList ({entities, onDelete}) {