diff --git a/frontend/components/Admin/Registries.js b/frontend/components/Admin/Registries.js index b7f0bfc9..5e40027d 100644 --- a/frontend/components/Admin/Registries.js +++ b/frontend/components/Admin/Registries.js @@ -32,6 +32,7 @@ export default function Registries() { return (
+ state.user.token); + const dispatch = useDispatch(); + + const url = `${publicRuntimeConfig.backend}/admin/deleteRegistry`; + + const handleFederate = async () => { + try { + await axios.post(`${publicRuntimeConfig.backend}/admin/federate`, { + administratorEmail: inputTwo, + webOfRegistries: inputOne + }, { + headers: { + 'Accept': 'application/json', + 'X-authorization': token + } + }); + // Add additional logic here if needed after successful POST + } catch (error) { + console.error('Error with federate: ', error); + // Handle errors here + } + }; + + const handleRetrieve = async () => { + try { + const response = await axios.post(`${publicRuntimeConfig.backend}/admin/retrieveFromWebOfRegistries`, {}, { + headers: { + 'Accept': 'application/json', + 'X-authorization': token + } + }); + + if (response.data && Array.isArray(response.data.registries)) { + // Assuming 'registries' is the correct key in response and it's an array of registry objects + mutate([ + `${publicRuntimeConfig.backend}/admin/registries`, + token, + dispatch + ]); + } + } catch (error) { + console.error('Error with retrieving from Web Of Registries: ', error); + // Handle errors here + } + }; + + return ( +
+ setInputOne(e.target.value)} + placeholder="Web of Registries URL" + /> + setInputTwo(e.target.value)} + placeholder="Administrator Email" + /> + + +
+ ); +} + const deleteRegistry = async (uri, token, dispatch) => { const url = `${publicRuntimeConfig.backend}/admin/deleteRegistry`; const headers = { diff --git a/frontend/components/Error/ErrorClearer.js b/frontend/components/Error/ErrorClearer.js index fccd3d00..42137c47 100644 --- a/frontend/components/Error/ErrorClearer.js +++ b/frontend/components/Error/ErrorClearer.js @@ -8,15 +8,23 @@ export default function ErrorClearer({ index, setIndex, size }) { const router = useRouter(); if (size === 'relog') { return ( -
{ - dispatch(clearErrors()); - dispatch(logoutUser()); - router.push('/'); - }} - > - Return to Login +
+
dispatch(clearErrors())} + > + Clear All +
+
{ + dispatch(clearErrors()); + dispatch(logoutUser()); + router.push('/'); + }} + > + Return to Login +
); } diff --git a/frontend/components/Viewing/Collection/Members.js b/frontend/components/Viewing/Collection/Members.js index 5d3cdeb5..0309cc88 100644 --- a/frontend/components/Viewing/Collection/Members.js +++ b/frontend/components/Viewing/Collection/Members.js @@ -19,7 +19,7 @@ import loadTemplate from '../../../sparql/tools/loadTemplate'; import { shortName } from '../../../namespace/namespace'; import lookupRole from '../../../namespace/lookupRole'; import Link from 'next/link'; -import { addError } from '../../../redux/actions'; +import { addError, logoutUser } from '../../../redux/actions'; import { processUrl, processUrlReverse } from '../../Admin/Registries'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrash, faUnlink } from '@fortawesome/free-solid-svg-icons'; @@ -95,18 +95,29 @@ export default function Members(properties) { ? useMembers(query, parameters, token, dispatch) : useMembers(query, parameters, dispatch); - const { count: totalMemberCount } = useCount( + const { count: totalMemberCount } = privateGraph + ? useCount( CountMembersTotal, { ...parameters, search: '' }, privateGraph ? token : undefined, // Pass token only if privateGraph is true dispatch - ); + ) + : useCount( + CountMembersTotal, + { ...parameters, search: '' }, + dispatch); - const { count: currentMemberCount } = useCount( + const { count: currentMemberCount } = privateGraph + ? useCount( searchQuery ? CountMembersTotal : CountMembers, parameters, privateGraph ? token : undefined, // Pass token only if privateGraph is true dispatch + ) + : useCount( + searchQuery ? CountMembersTotal : CountMembers, + parameters, + dispatch ); useEffect(() => { @@ -116,11 +127,17 @@ export default function Members(properties) { } }, [properties.refreshMembers, mutate]); - const { filters } = useFilters( + const { filters } = privateGraph + ? useFilters( getTypesRoles, { uri: properties.uri }, token, dispatch + ) + : useFilters( + getTypesRoles, + { uri: properties.uri }, + dispatch ); const outOfBoundsHandle = offset => { @@ -245,9 +262,10 @@ function FilterHeader(properties) { function MemberTable(properties) { const [processedMembers, setProcessedMembers] = useState([]); - + const isPublicCollection = properties.uri.includes("/public/"); const token = useSelector(state => state.user.token); const dispatch = useDispatch(); + useEffect(() => { async function processMembers() { if (properties.members) { @@ -278,6 +296,12 @@ function MemberTable(properties) { Number(properties.totalMembers).toLocaleString() + ')'; } + + const headers = ['Name', 'Identifier', 'Type', 'Description']; + if (!isPublicCollection) { + headers.push('Remove'); + } + return (
{ if (member.uri && window.confirm("Would you like to remove this item from the collection?")) { @@ -375,9 +399,11 @@ function MemberTable(properties) { - + {!isPublicCollection && ( + + )} ); }} @@ -431,7 +457,13 @@ const createUrl = (query, options) => { const useCount = (query, options, token, dispatch) => { const url = createUrl(query, options, token); - const { data, error } = useSWR([url, token, dispatch], fetcher); + let data, error; + + if (typeof token === 'string') { + ({ data, error } = useSWR([url, token, dispatch], fetcher)); + } else { + ({ data, error } = useSWR([url, dispatch], fetcher)); + } let processedData = data ? processResults(data)[0].count : undefined; return { @@ -441,10 +473,15 @@ const useCount = (query, options, token, dispatch) => { const useMembers = (query, options, token, dispatch) => { const url = createUrl(query, options); - const { data, error, mutate } = useSWR([url, token, dispatch], fetcher); + let data, error, mutate; - let processedData = data ? processResults(data) : undefined; + if (typeof token === 'string') { + ({ data, error, mutate } = useSWR([url, token, dispatch], fetcher)); + } else { + ({ data, error, mutate } = useSWR([url, dispatch], fetcher)); + } + let processedData = data ? processResults(data) : undefined; return { members: processedData, mutate @@ -453,12 +490,20 @@ const useMembers = (query, options, token, dispatch) => { const useFilters = (query, options, token, dispatch) => { const url = createUrl(query, options); - const { data, error } = useSWR([url, token, dispatch], fetcher); + + let data, error, mutate; + + if (typeof token === 'string') { + ({ data, error, mutate } = useSWR([url, token, dispatch], fetcher)); + } else { + ({ data, error, mutate } = useSWR([url, dispatch], fetcher)); + } let processedData = data ? processResults(data) : undefined; return { - filters: processedData + filters: processedData, + mutate }; }; @@ -479,12 +524,13 @@ const fetcher = (url, token, dispatch) => // Check if the error is 401 Unauthorized or 400 Bad Request if (error.response && (error.response.status === 401 || error.response.status === 400)) { // Check if the user is logged in by looking for 'userToken' in local storage - console.log(localStorage); - console.log(error.response); if (!localStorage.getItem('userToken')) { console.log('Missing user token'); // User is not logged in, redirect to the login page - // window.location.href = '/login'; + dispatch(logoutUser()); + window.location.href = '/login'; + } else { + console.error(error); } } diff --git a/frontend/components/Viewing/ViewHeader.js b/frontend/components/Viewing/ViewHeader.js index 4fd82be7..fa6babeb 100644 --- a/frontend/components/Viewing/ViewHeader.js +++ b/frontend/components/Viewing/ViewHeader.js @@ -7,6 +7,9 @@ import React, { useRef } from 'react'; import { useSelector } from 'react-redux'; import { useState } from 'react'; +import SearchHeader from '../Search/SearchHeader/SearchHeader'; +import ResultTable from '../Search/StandardSearch/ResultTable/ResultTable'; + import axios from 'axios'; import Link from 'next/link'; @@ -71,6 +74,12 @@ export default function ViewHeader(properties) { }) .then(response => { console.log(response.data); + return ( +
+ + +
+ ) }) .catch(error => { console.error('Error fetching twins: ', error); diff --git a/frontend/pages/[...view].js b/frontend/pages/[...view].js index 1bc7e3cb..7f22a5bd 100644 --- a/frontend/pages/[...view].js +++ b/frontend/pages/[...view].js @@ -11,6 +11,7 @@ import Shell from '../components/Viewing/Shell'; import getMetadata from '../sparql/getMetadata'; import getQueryResponse from '../sparql/tools/getQueryResponse'; import { addError } from '../redux/actions'; +import styles from '../styles/view.module.css'; export default function View({ data, error }) { const dispatch = useDispatch(); @@ -22,29 +23,65 @@ export default function View({ data, error }) { const token = useSelector(state => state.user.token); const url = view ? view.join('/') : ''; const [metadata, setMetadata] = useState(); - const uri = `https://synbiohub.org/${url}`; + const [urlExists, setUrlExists] = useState(true); // New state for URL existence + const backenduri = `${publicRuntimeConfig.backend}/${url}`; + + const centerStyle = { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + height: '100vh', // This makes sure it takes the full viewport height + marginTop: '-10vh', + }; useEffect(() => { - if (!metadata) - getQueryResponse(dispatch, getMetadata, { uri: uri }, token).then( - metadata => setMetadata(metadata) - ); - }, [metadata, token]); + // Check if URL exists + axios.get(backenduri) + .then(response => { + // URL exists + setUrlExists(true); + if (!metadata) + getQueryResponse(dispatch, getMetadata, { uri: uri }, token).then( + metadata => setMetadata(metadata) + ); + }) + .catch(error => { + // URL does not exist + setUrlExists(false); + }); + }, [uri, metadata, token]); + + // Render based on URL existence + if (!url || !urlExists) { + return ( + +
+ { /* Logo Here */} + Logo - // validate part - if (!url || !metadata) + { /* Page Not Found Message */} +
+

Page Not Found

+

The requested URL {url && {`/${url}`}} was not found on this server.

+
+
+
+ ); + } else if (!metadata) { return ( ); - else if (metadata.length === 0) + } else if (metadata.length === 0) { return (
Page not found
); + } return ( diff --git a/frontend/public/commitHash.txt b/frontend/public/commitHash.txt index 2b644740..f10078bd 100644 --- a/frontend/public/commitHash.txt +++ b/frontend/public/commitHash.txt @@ -1 +1 @@ -04406ac7fab2d01ace9ce75e3006610919ce8c86 \ No newline at end of file +e29b11b511fdde65acd45d974167b43607b1a9a8 \ No newline at end of file diff --git a/frontend/public/images/sniffa.gif b/frontend/public/images/sniffa.gif new file mode 100644 index 00000000..2a7391b2 Binary files /dev/null and b/frontend/public/images/sniffa.gif differ diff --git a/frontend/public/images/widesniffa.gif b/frontend/public/images/widesniffa.gif new file mode 100644 index 00000000..2d72a2af Binary files /dev/null and b/frontend/public/images/widesniffa.gif differ diff --git a/frontend/styles/view.module.css b/frontend/styles/view.module.css index 430cf4c4..f2e627ce 100644 --- a/frontend/styles/view.module.css +++ b/frontend/styles/view.module.css @@ -1095,4 +1095,12 @@ cursor: pointer; } +.notFound { + display: 'flex'; + flex-direction: 'column'; + align-items: 'center'; + justify-content: 'center'; + height: '100vh'; +}; +
{getType(member)} {member.description} handleIconClick(member)}> - - handleIconClick(member)}> + +