diff --git a/ui/src/app/App.js b/ui/src/app/App.js
index f9acbb044..c979b0201 100644
--- a/ui/src/app/App.js
+++ b/ui/src/app/App.js
@@ -14,6 +14,7 @@ import Footer from './core/components/Footer';
import Dashboard from './dashboard/container/Dashboard';
import Header from './core/components/Header';
import { UserProvider } from './core/user/UserContext';
+import { Metadata } from './metadata/Metadata';
function App() {
return (
@@ -28,6 +29,7 @@ function App() {
+
@@ -40,7 +42,7 @@ function App() {
/*
-
+
diff --git a/ui/src/app/admin/container/UserManagement.js b/ui/src/app/admin/container/UserManagement.js
index 0cf2c8fb1..d06a6e0dd 100644
--- a/ui/src/app/admin/container/UserManagement.js
+++ b/ui/src/app/admin/container/UserManagement.js
@@ -99,23 +99,23 @@ export default function UserManagement({ users, roles, onDelete, onSetRole }) {
{{ user.emailAddress }} |
+ className="sr-only">User role
|
- |
*/
\ No newline at end of file
diff --git a/ui/src/app/core/components/FormattedDate.js b/ui/src/app/core/components/FormattedDate.js
index 7a18594a2..ea01aa1e8 100644
--- a/ui/src/app/core/components/FormattedDate.js
+++ b/ui/src/app/core/components/FormattedDate.js
@@ -1,8 +1,8 @@
import React from 'react';
import { format } from 'date-fns';
-export default function FormattedDate ({ date }) {
- const formatted = React.useMemo(() => format(new Date(date), 'MMM Lo, Y'), [date]);
+export default function FormattedDate ({ date, time = false }) {
+ const formatted = React.useMemo(() => format(new Date(date), `MMM Lo, Y${time ? ' HH:mm:ss' : ''}`), [date, time]);
return (<>{ formatted }>);
}
\ No newline at end of file
diff --git a/ui/src/app/dashboard/container/Dashboard.js b/ui/src/app/dashboard/container/Dashboard.js
index 9580c2857..dc1274e98 100644
--- a/ui/src/app/dashboard/container/Dashboard.js
+++ b/ui/src/app/dashboard/container/Dashboard.js
@@ -44,15 +44,13 @@ export function Dashboard () {
-
-
-
-
-
-
-
-
+
+
+
+
+
+
);
diff --git a/ui/src/app/dashboard/container/ProvidersTab.js b/ui/src/app/dashboard/container/ProvidersTab.js
index 929c0e3e1..88ba28c5a 100644
--- a/ui/src/app/dashboard/container/ProvidersTab.js
+++ b/ui/src/app/dashboard/container/ProvidersTab.js
@@ -1,15 +1,14 @@
import React from 'react';
-import useFetch from 'use-http';
-import Translate from '../../i18n/components/translate';
-import API_BASE_PATH from '../../App.constant';
+import { useMetadataEntities } from '../../metadata/hooks/api';
+import Translate from '../../i18n/components/translate';
import ProviderList from '../../metadata/provider/component/ProviderList';
export function ProvidersTab () {
const [providers, setProviders] = React.useState([]);
- const { get, response } = useFetch(`${API_BASE_PATH}/MetadataResolvers`, {})
+ const { get, response } = useMetadataEntities('provider');
async function loadProviders() {
const providers = await get('/')
diff --git a/ui/src/app/dashboard/container/SourcesTab.js b/ui/src/app/dashboard/container/SourcesTab.js
index d047d54c6..8b55a0da8 100644
--- a/ui/src/app/dashboard/container/SourcesTab.js
+++ b/ui/src/app/dashboard/container/SourcesTab.js
@@ -4,12 +4,13 @@ import Translate from '../../i18n/components/translate';
import API_BASE_PATH from '../../App.constant';
import SourceList from '../../metadata/source/component/SourceList';
+import { useMetadataEntities } from '../../metadata/hooks/api';
export function SourcesTab () {
const [sources, setSources] = React.useState([]);
- const { get, del, response } = useFetch(`${API_BASE_PATH}/EntityDescriptors`, {})
+ const { get, del, response } = useMetadataEntities('source');
async function loadSources() {
const sources = await get('/')
diff --git a/ui/src/app/metadata/Metadata.js b/ui/src/app/metadata/Metadata.js
new file mode 100644
index 000000000..99deee5fd
--- /dev/null
+++ b/ui/src/app/metadata/Metadata.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Switch, Route, useRouteMatch } from 'react-router-dom';
+import { MetadataOptions } from './component/MetadataOptions';
+import { MetadataDetail } from './component/MetadataDetail';
+import { MetadataHistory } from './component/MetadataHistory';
+import { MetadataEditor } from './editor/MetadataEditor';
+import { MetadataSelector } from './hoc/MetadataSelector';
+import MetadataSchema from './hoc/MetadataSchema';
+
+export function Metadata () {
+
+ let { path } = useRouteMatch();
+
+ return (
+
+
+
+
+
+
+
+ } />
+
+
+
+
+ } />
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/ui/src/app/metadata/component/MetadataConfiguration.js b/ui/src/app/metadata/component/MetadataConfiguration.js
new file mode 100644
index 000000000..64fde5e01
--- /dev/null
+++ b/ui/src/app/metadata/component/MetadataConfiguration.js
@@ -0,0 +1,7 @@
+import React from 'react';
+
+export function MetadataConfiguration () {
+ return (
+ <>>
+ );
+}
\ No newline at end of file
diff --git a/ui/src/app/metadata/component/MetadataDetail.js b/ui/src/app/metadata/component/MetadataDetail.js
new file mode 100644
index 000000000..d685727b8
--- /dev/null
+++ b/ui/src/app/metadata/component/MetadataDetail.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import Translate from '../../i18n/components/translate';
+
+import { MetadataObjectContext } from '../hoc/MetadataSelector';
+
+export function MetadataDetail ({ children }) {
+
+ const metadata = React.useContext(MetadataObjectContext);
+
+ return (
+
+
+
+
+ { children }
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/ui/src/app/metadata/component/MetadataHeader.js b/ui/src/app/metadata/component/MetadataHeader.js
new file mode 100644
index 000000000..d9fcdc3da
--- /dev/null
+++ b/ui/src/app/metadata/component/MetadataHeader.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import FormattedDate from '../../core/components/FormattedDate';
+
+import Translate from '../../i18n/components/translate';
+
+export function MetadataHeader ({ model, current = true, enabled = true }) {
+ return (
+
+
+
+
+ Saved:
+
+
+
+
+ By:
+ {model.createdBy }
+
+
+
+
+
+
+ Enabled
+
+
+
+ Current
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/ui/src/app/metadata/component/MetadataHistory.js b/ui/src/app/metadata/component/MetadataHistory.js
new file mode 100644
index 000000000..a372179cc
--- /dev/null
+++ b/ui/src/app/metadata/component/MetadataHistory.js
@@ -0,0 +1,13 @@
+import React from 'react';
+
+import Translate from '../../i18n/components/translate';
+
+export function MetadataHistory () {
+ return (
+ <>
+
+ version history
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/ui/src/app/metadata/component/MetadataOptions.js b/ui/src/app/metadata/component/MetadataOptions.js
new file mode 100644
index 000000000..853e660cb
--- /dev/null
+++ b/ui/src/app/metadata/component/MetadataOptions.js
@@ -0,0 +1,56 @@
+import { faArrowDown, faHistory } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import React from 'react';
+import { Link, useParams } from 'react-router-dom';
+
+import Translate from '../../i18n/components/translate';
+
+import { MetadataObjectContext } from '../hoc/MetadataSelector';
+import { MetadataHeader } from './MetadataHeader';
+
+export function MetadataOptions () {
+
+ const metadata = React.useContext(MetadataObjectContext);
+
+ const { type, id } = useParams();
+
+ return (
+ <>
+
+ [{type}] configuration
+
+
+
+
+
+
+
+ Version History
+
+ {type === 'provider' &&
+
+
+ Filters
+
+ }
+
+ {/*
*/}
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/ui/src/app/metadata/editor/MetadataEditor.js b/ui/src/app/metadata/editor/MetadataEditor.js
new file mode 100644
index 000000000..8e0e4aece
--- /dev/null
+++ b/ui/src/app/metadata/editor/MetadataEditor.js
@@ -0,0 +1,26 @@
+import React from 'react';
+
+import Translate from '../../i18n/components/translate';
+
+import { MetadataObjectContext, MetadataTypeContext } from '../hoc/MetadataSelector';
+
+export function MetadataEditor () {
+
+ const metadata = React.useContext(MetadataObjectContext);
+ const type = React.useContext(MetadataTypeContext);
+
+ return (
+
+
+
+
+
+
+ Edit metadata { type } - {metadata.serviceProviderName || metadata.name}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/ui/src/app/metadata/hoc/MetadataSchema.js b/ui/src/app/metadata/hoc/MetadataSchema.js
new file mode 100644
index 000000000..dd0da1b85
--- /dev/null
+++ b/ui/src/app/metadata/hoc/MetadataSchema.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { useParams } from 'react-router';
+import { useMetadataSchema, getSchemaPath } from '../hooks/api';
+
+export const MetadataSchemaContext = React.createContext();
+
+
+export function MetadataSchema({ children }) {
+
+ let { type } = useParams();
+
+ const { get, response } = useMetadataSchema();
+
+ const [schema, setSchema] = React.useState([]);
+
+ async function loadSchema(t) {
+ const source = await get(`/${getSchemaPath(t)}`)
+ if (response.ok) {
+ setSchema(source);
+ }
+ }
+
+ React.useEffect(() => { loadSchema(type) }, [type]);
+
+ return (
+ <>
+ {type &&
+
+ {children}
+
+ }
+ >
+ );
+}
+
+export default MetadataSchema;
\ No newline at end of file
diff --git a/ui/src/app/metadata/hoc/MetadataSelector.js b/ui/src/app/metadata/hoc/MetadataSelector.js
new file mode 100644
index 000000000..f7cd954a1
--- /dev/null
+++ b/ui/src/app/metadata/hoc/MetadataSelector.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import { useParams } from 'react-router';
+import { useMetadataEntity } from '../hooks/api';
+
+export const MetadataTypeContext = React.createContext();
+export const MetadataObjectContext = React.createContext();
+
+
+export function MetadataSelector ({ children }) {
+
+ let { type, id } = useParams();
+
+ const { get, response } = useMetadataEntity(type);
+
+ const [metadata, setMetadata] = React.useState([]);
+
+ async function loadMetadata(id) {
+ const source = await get(`/${id}`)
+ if (response.ok) {
+ setMetadata(source);
+ }
+ }
+
+ React.useEffect(() => { loadMetadata(id) }, [id]);
+
+ return (
+ <>
+ {type &&
+
+ {metadata && metadata.version &&
+ {children}
+ }
+
+ }
+ >
+ );
+}
+
+export default MetadataSelector;
\ No newline at end of file
diff --git a/ui/src/app/metadata/hooks/api.js b/ui/src/app/metadata/hooks/api.js
new file mode 100644
index 000000000..3402e7eb8
--- /dev/null
+++ b/ui/src/app/metadata/hooks/api.js
@@ -0,0 +1,33 @@
+import useFetch from 'use-http';
+
+import API_BASE_PATH from '../../App.constant';
+
+const paths = {
+ source: 'EntityDescriptor',
+ provider: 'MetadataResolver'
+};
+
+const schema = {
+ source: 'MetadataSources'
+}
+
+export function getMetadataPath(type) {
+ return `/${paths[type]}`;
+}
+
+export function getSchemaPath(type) {
+ return `/${schema[type]}`;
+}
+
+export function useMetadataEntities(type = 'source') {
+ return useFetch(`${API_BASE_PATH}${getMetadataPath(type)}s`);
+}
+
+export function useMetadataEntity(type = 'source') {
+ return useFetch(`${API_BASE_PATH}${getMetadataPath(type)}`);
+}
+
+
+export function useMetadataSchema() {
+ return useFetch(`${API_BASE_PATH}/ui`);
+}
diff --git a/ui/src/app/metadata/provider/component/ProviderList.js b/ui/src/app/metadata/provider/component/ProviderList.js
index cea52f41b..788162645 100644
--- a/ui/src/app/metadata/provider/component/ProviderList.js
+++ b/ui/src/app/metadata/provider/component/ProviderList.js
@@ -27,34 +27,34 @@ export default function ProviderList({ entities, onDelete }) {
- { idx + 1 }
+ { idx + 1 }
-
+
-
+
{ /*
- {{ i + 1 }}
- —
+ {{ i + 1 }}
+ —
-
-
+
-
-
+
*/ }
@@ -95,7 +95,7 @@ export default function ProviderList({ entities, onDelete }) {
|
-
+
Incomplete Form
@@ -112,7 +112,7 @@ export default function ProviderList({ entities, onDelete }) {
-
|
- {source.serviceProviderName }
+ {source.serviceProviderName }
|
{source.entityId}
@@ -108,7 +108,7 @@ export default function SourceList({ entities, onDelete }) {
|
-
+
Incomplete Form
@@ -125,7 +125,7 @@ export default function SourceList({ entities, onDelete }) {
- | |