Page MenuHomeSealhub

No OneTemporary

diff --git a/admin-panel/collections/collections.jsx b/admin-panel/collections/collections.jsx
index 6766a07..74292be 100644
--- a/admin-panel/collections/collections.jsx
+++ b/admin-panel/collections/collections.jsx
@@ -1,71 +1,79 @@
const React = require('react');
const { useEffect, useState } = React;
import { BrowserRouter as HashRouter, Route, Link } from 'react-router-dom';
import useCollections from './use-collections.js';
import CreateCollectionItem from './create-collection-item.js';
+import Loading from '../loading/loading';
function CollectionList({ match }) {
const collection_name = match.params.collection;
const collection = useCollections(collection_name);
const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(false);
useEffect(() => {
+ setLoading(true);
fetch(`/api/v1/collections/${collection_name}`)
.then(response => response.json())
- .then(response => setItems(response.items));
+ .then(response => {
+ setLoading(false);
+ setItems(response.items);
+ });
}, []);
- return (
+ return loading ? (
+ <Loading />
+ ) : (
<div>
<Link to={`/collections/${collection_name}/create`}>+create</Link>
{items.map((item, index) => (
<React.Fragment key={index}>
<div>{item.title}</div>
<Link
to={`/collections/${collection_name}/edit/${item.id}`}
>
edit
</Link>
</React.Fragment>
))}
</div>
);
}
function Collection({ match }) {
const collection_name = match.params.collection;
return (
<div>
<h2>{collection_name}</h2>
<Route exact path={match.path} component={CollectionList} />
</div>
);
}
function Collections({ match }) {
return (
<div>
Collections
<Route path={`${match.path}/:collection`} component={Collection} />
<Route
exact
path={`${match.path}/:collection/:mode`}
component={CreateCollectionItem}
/>
<Route
exact
path={`${match.path}/:collection/:mode/:id`}
component={CreateCollectionItem}
/>
<Route
exact
path={match.path}
component={() => <div>Pick a collection</div>}
/>
</div>
);
}
export default Collections;
diff --git a/admin-panel/collections/create-collection-item.js b/admin-panel/collections/create-collection-item.js
index 69a8faa..aacc0fb 100644
--- a/admin-panel/collections/create-collection-item.js
+++ b/admin-panel/collections/create-collection-item.js
@@ -1,104 +1,111 @@
import React, { useState, useEffect } from 'react';
import useCollections from './use-collections.js';
import FormControls from '../form-controls/form-controls.jsx';
+import Loading from '../loading/loading';
export default function({ match }) {
const { id, collection, mode } = match.params;
const _collection = useCollections(collection);
const [values, setValues] = useState({});
+ const [loading, setLoading] = useState(false);
useEffect(() => {
async function fetchData() {
+ setLoading(true);
const query = await fetch(
`/api/v1/collections/${collection}/${id}`
);
const data = await query.json();
setValues({ ...data });
+ setLoading(false);
}
if (mode === 'edit') {
fetchData();
}
}, []);
function setValue(key, value) {
setValues({ ...values, [key]: value });
}
if (!_collection) {
return <div>Loading...</div>;
}
async function save(event) {
event.preventDefault();
let api_endpoint = `/api/v1/collections/${collection}`;
if (mode === 'edit') {
api_endpoint += `/${id}`;
}
//Filter out unnecessary fields from values
const body = {};
Object.keys(values).forEach(key => {
if (_collection.fields[key]) {
body[key] = values[key];
}
});
-
+ setLoading(true);
const query = await fetch(api_endpoint, {
method: mode === 'edit' ? 'PUT' : 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const response = await query.json();
if (response.sealious_error) {
alert(response.message);
console.log(response);
} else {
alert(
`Successfully ${mode === 'edit' ? 'edited' : 'created'} ${
values.title
}`
);
+ setLoading(false);
document.location.hash = `/collections/${collection}`;
}
}
- return (
+ return loading ? (
+ <Loading />
+ ) : (
<div>
<h2>Metadata Editor</h2>
<form onSubmit={save}>
{JSON.stringify(values)}
<ul>
{Object.entries(_collection.fields).map(
([field_name, field]) => (
<label
htmlFor={field_name}
style={{ display: 'block' }}
key={field_name}
>
{field_name}
{FormControls[field.type.name]
? /* eslint-disable indent */
React.createElement(
FormControls[field.type.name],
{
...field,
onChange: value =>
setValue(field_name, value),
value: values[field_name] || '',
}
)
: /* eslint-enable indent */
`: ${field.type.name}`}
</label>
)
)}
</ul>
<input type="submit" />
</form>
</div>
);
}
diff --git a/admin-panel/collections/use-collections.js b/admin-panel/collections/use-collections.js
index 9d9b523..c6119a3 100644
--- a/admin-panel/collections/use-collections.js
+++ b/admin-panel/collections/use-collections.js
@@ -1,43 +1,44 @@
const { useState } = require('react');
let collections_cache = [];
let loaded = false;
let promise = null;
const url = '/api/v1/specifications';
export default function useCollections(collection_name) {
const [collections, setCollections] = useState(collections_cache);
+
if (promise) {
promise.then(setCollections);
} else {
if (loaded) {
setCollections(collections_cache);
} else {
promise = (async function() {
const response = await fetch(url);
const collections = (await response.json()).filter(
collection =>
![
'user-roles',
'users',
'sessions',
'anonymous-sessions',
'formatted-images',
'password-reset-intents',
'registration-intents',
].includes(collection.name)
);
setCollections(collections);
collections_cache = collections;
loaded = true;
return collections;
})();
}
}
return collection_name
? collections.filter(
- collection => collection.name == collection_name
+ collection => collection.name == collection_name
)[0]
: collections;
}
diff --git a/admin-panel/loading/_loading.scss b/admin-panel/loading/_loading.scss
new file mode 100644
index 0000000..ee4eb13
--- /dev/null
+++ b/admin-panel/loading/_loading.scss
@@ -0,0 +1,59 @@
+@keyframes jump {
+ 0% {
+ transform: transl58eY(0);
+ }
+ 7% {
+ transform: scaleY(0.8) scaleX(1.2) translateY(2px);
+ }
+ 10% {
+ transform: scaleY(0.8) scaleX(1.2) translateY(2px);
+ }
+ 19% {
+ transform: scaleX(0.9) scaleY(1.1) translateY(-0.5rem);
+ }
+ 37% {
+ transform: translateY(-1rem);
+ }
+ 55% {
+ transform: scaleX(0.9) scaleY(1.1) translateY(-0.5rem);
+ }
+ 70% {
+ transform: scaleY(0.8) scaleX(1.2) translateY(2px);
+ }
+ 100% {
+ transform: translateY(0);
+ }
+}
+
+.loading-container {
+ text-align: center;
+ padding-top: 3rem;
+ padding-bottom: 3rem;
+ padding-left: 1rem;
+}
+
+.loading-box {
+ width: 1rem;
+ height: 1rem;
+ background-color: #624991;
+ display: inline-block;
+ vertical-align: bottom;
+ animation: jump 700ms;
+ animation-iteration-count: infinite;
+}
+
+.loading-shadow {
+ display: inline-block;
+ margin-right: 1rem;
+ width: 1rem;
+ margin-left: -1rem;
+ width: 1rem;
+ box-shadow: 0px 4px 2px 1px rgba(0, 0, 0, 0.16);
+}
+
+.loading-text {
+ margin-top: 1rem;
+ font-size: 1rem;
+ line-height: 2rem;
+ color: hsl(89, 46%, 48%);
+}
diff --git a/admin-panel/loading/loading.jsx b/admin-panel/loading/loading.jsx
new file mode 100644
index 0000000..9fb2c07
--- /dev/null
+++ b/admin-panel/loading/loading.jsx
@@ -0,0 +1,28 @@
+import React from 'react';
+
+const Loading = function(props) {
+ const delay = 65;
+ const boxes_amnt = Math.floor(Math.random() * 4) + 4;
+ const boxes = [];
+ for (var i = 0; i < boxes_amnt; i++) {
+ boxes.push(
+ <div
+ className={'loading-box ' + props.boxClass}
+ style={{ animationDelay: delay * i + 'ms' }}
+ />
+ );
+ boxes.push(<div className="loading-shadow" />);
+ }
+ let loading = null;
+ if (props.text) {
+ loading = <div className="loading-text">{props.text}</div>;
+ }
+ return (
+ <div className="loading-container">
+ <div>{boxes}</div>
+ {loading}
+ </div>
+ );
+};
+
+export default Loading;
diff --git a/admin-panel/styles.scss b/admin-panel/styles.scss
index f268717..f211e7f 100644
--- a/admin-panel/styles.scss
+++ b/admin-panel/styles.scss
@@ -1,22 +1,23 @@
@import './navbar/navbar';
@import './sidebar/sidebar';
+@import './loading/loading';
@import './webfonts.scss';
html {
font-size: 1.5em;
font-family: 'IBM Plex Sans';
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
li {
list-style: none;
}
.app-sealpage {
display: flex;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 8, 05:43 (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1034062
Default Alt Text
(8 KB)

Event Timeline