diff --git a/frontend/components/Admin/Status.js b/frontend/components/Admin/Status.js
index cacd42f8..f10aef93 100644
--- a/frontend/components/Admin/Status.js
+++ b/frontend/components/Admin/Status.js
@@ -13,6 +13,7 @@ export default function Status() {
const dispatch = useDispatch();
const token = useSelector(state => state.user.token);
const { status, loading } = useStatus(token, dispatch);
+
if (status) {
return (
@@ -91,7 +92,7 @@ export default function Status() {
}
}
-const useStatus = (token, dispatch) => {
+export const useStatus = (token, dispatch) => {
const { data, error } = useSWR(
[`${publicRuntimeConfig.backend}/admin`, token, dispatch],
fetcher
@@ -117,7 +118,7 @@ const fetcher = (url, token, dispatch) =>
if (error.response && error.response.status === 401) {
// Handle 401 Unauthorized error by signing out and redirecting to the login page
dispatch(logoutUser()); // Dispatch the logout action to sign out the user
- window.location.href = '/login'; // Redirect to the login page
+ // window.location.href = '/login'; // Redirect to the login page
} else {
// Handle other errors
error.customMessage = 'Error fetching status';
diff --git a/frontend/components/Viewing/Collection/Members.js b/frontend/components/Viewing/Collection/Members.js
index 96136ca8..ca058796 100644
--- a/frontend/components/Viewing/Collection/Members.js
+++ b/frontend/components/Viewing/Collection/Members.js
@@ -21,6 +21,10 @@ import lookupRole from '../../../namespace/lookupRole';
import Link from 'next/link';
import { addError } from '../../../redux/actions';
import { processUrl } from '../../Admin/Registries';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faTrash, faUnlink } from '@fortawesome/free-solid-svg-icons';
+import { getAfterThirdSlash } from '../ViewHeader';
+import Status, { useStatus } from '../../Admin/Status';
/* eslint sonarjs/cognitive-complexity: "off" */
@@ -77,25 +81,32 @@ export default function Members(properties) {
limit: ' LIMIT 10000 '
};
+ const { adminStatus, loading, error } = useStatus(token, dispatch);
+
if (token) {
parameters.graphs = privateGraph
+ } else {
}
const searchQuery = preparedSearch || typeFilter !== 'Show Only Root Objects';
let query = searchQuery ? getCollectionMembersSearch : getCollectionMembers;
- const { members, mutate } = useMembers(query, parameters, token, dispatch);
+ const { members, mutate } = privateGraph
+ ? useMembers(query, parameters, token, dispatch)
+ : useMembers(query, parameters, dispatch);
+
const { count: totalMemberCount } = useCount(
CountMembersTotal,
{ ...parameters, search: '' },
- token,
+ privateGraph ? token : undefined, // Pass token only if privateGraph is true
dispatch
);
+
const { count: currentMemberCount } = useCount(
searchQuery ? CountMembersTotal : CountMembers,
parameters,
- token,
+ privateGraph ? token : undefined, // Pass token only if privateGraph is true
dispatch
);
@@ -137,6 +148,7 @@ export default function Members(properties) {
setSort={setSort}
defaultSortOption={defaultSortOption}
setDefaultSortOption={setDefaultSortOption}
+ uri={properties.uri}
/>
);
@@ -264,7 +276,7 @@ function MemberTable(properties) {
customSearch={properties.customSearch}
hideFilter={true}
searchable={[]}
- headers={['Name', 'Identifier', 'Type', 'Description']}
+ headers={['Name', 'Identifier', 'Type', 'Description', 'Remove']}
sortOptions={sortOptions}
sortMethods={sortMethods}
defaultSortOption={properties.defaultSortOption}
@@ -280,6 +292,55 @@ function MemberTable(properties) {
textArea.innerHTML = member.displayId;
}
+ const objectUriParts = getAfterThirdSlash(properties.uri);
+ const objectUri = `${publicRuntimeConfig.backend}/${objectUriParts}`;
+
+ const icon = compareUri(member.uri, `/${objectUriParts}`);
+
+ const handleIconClick = (member) => {
+ if (icon && icon === faTrash) {
+ handleDelete(member);
+ } else if (icon && icon === faUnlink) {
+ handleUnlink(member);
+ }
+ };
+
+ const handleDelete = (member) => {
+ if (member.uri && window.confirm("Would you like to remove this item from the collection?")) {
+ axios.get(`${publicRuntimeConfig.backend}${member.uri}/remove`, {
+ headers: {
+ "Accept": "text/plain; charset=UTF-8",
+ "X-authorization": token
+ }
+ })
+ .then(response => {
+ window.location.reload();
+ })
+ .catch(error => {
+ console.error('Error removing item:', error);
+ });
+ }
+ };
+
+ const handleUnlink = (member) => {
+ if (member.uri && window.confirm("Would you like to unlink this item from the collection?")) {
+ axios.post(`${objectUri}/removeMembership`, {
+ "member": `${publicRuntimeConfig.backend}${member.uri}`
+ }, {
+ headers: {
+ "Accept": "text/plain; charset=UTF-8",
+ "X-authorization": token
+ }
+ })
+ .then(response => {
+ window.location.reload();
+ })
+ .catch(error => {
+ console.error('Error removing item:', error);
+ });
+ }
+ };
+
return (
@@ -296,9 +357,13 @@ function MemberTable(properties) {
|
{getType(member)} |
{member.description} |
+ handleIconClick(member)}>
+
+ |
);
}}
+
/>
);
}
@@ -319,6 +384,26 @@ function getType(member) {
return memberType;
}
+function compareUri(memberUri, baseUri) {
+ const userUriPrefix = '/user/';
+ const publicUriPrefix = '/public/';
+
+ if (memberUri.startsWith(userUriPrefix)) {
+ // Check if member.uri matches properties.uri for the first 3 slashes
+ const matchUri = baseUri.split('/').slice(0, 4).join('/');
+ if (memberUri.startsWith(matchUri)) {
+ return faTrash;
+ }
+ } else if (memberUri.startsWith(publicUriPrefix)) {
+ // Check if member.uri matches properties.uri for the first 2 slashes
+ const matchUri = baseUri.split('/').slice(0, 3).join('/');
+ if (memberUri.startsWith(matchUri)) {
+ return faTrash;
+ }
+ }
+ return faUnlink;
+}
+
const createUrl = (query, options) => {
query = loadTemplate(query, options);
return `${publicRuntimeConfig.backend}/sparql?query=${encodeURIComponent(
@@ -378,10 +463,10 @@ const fetcher = (url, token, dispatch) =>
// Check if the user is logged in by looking for 'userToken' in local storage
if (!localStorage.getItem('userToken')) {
// User is not logged in, redirect to the login page
- window.location.href = '/login';
+ // window.location.href = '/login';
}
}
-
+
// Dispatch the error regardless of whether the user is logged in
dispatch(addError(error));
});
diff --git a/frontend/components/Viewing/MetadataInfo.js b/frontend/components/Viewing/MetadataInfo.js
index c07d7fc0..3c875d62 100644
--- a/frontend/components/Viewing/MetadataInfo.js
+++ b/frontend/components/Viewing/MetadataInfo.js
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import Link from 'next/link';
import { useSelector } from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faPlus } from '@fortawesome/free-solid-svg-icons';
+import { faPlus, faTrash, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import RenderIcon from './PageJSON/Rendering/RenderIcon';
import styles from '../../styles/view.module.css';
import { useTheme } from '../Admin/Theme';
@@ -14,13 +14,67 @@ import { getAfterThirdSlash } from './ViewHeader';
const { publicRuntimeConfig } = getConfig();
export default function MetadataInfo({ title, link, label, icon, specific, uri }) {
- console.log(label);
const { theme } = useTheme();
const [isHovered, setIsHovered] = useState(false);
const [isEditingSource, setIsEditingSource] = useState(false);
const [newSource, setNewSource] = useState('');
const token = useSelector(state => state.user.token);
+ const [editSourceIndex, setEditSourceIndex] = useState(null);
+ const [editedSource, setEditedSource] = useState('');
+ const [sources, setSources] = useState(processTitle(title)); // Assuming processTitle is your existing function
+
+
+ const handleEditSource = (index, source) => {
+ setEditSourceIndex(index);
+ setEditedSource(source);
+ };
+
+ const handleCancelEdit = () => {
+ setEditSourceIndex(null); // Reset edit mode
+ setEditedSource(''); // Clear the edited content
+ };
+
+
+ const handleSaveEdit = (index, source) => {
+ // Make an Axios POST request to save the edited source content
+ if (editedSource.trim() === '') {
+ // Handle the case where the edited source content is empty (optional)
+ alert('Source content cannot be empty.');
+ return; // Do not proceed with saving an empty source
+ }
+
+ const objectUriParts = getAfterThirdSlash(uri);
+ const objectUri = `${publicRuntimeConfig.backend}/${objectUriParts}`;
+
+ axios
+ .post(
+ `${objectUri}/edit/wasDerivedFrom`,
+ {
+ previous: source, // You may need to pass the source index for identification
+ object: editedSource, // The edited source content
+ },
+ {
+ headers: {
+ 'Accept': 'text/plain; charset=UTF-8',
+ 'X-authorization': token,
+ },
+ }
+ )
+ .then((response) => {
+ // Handle the successful response here, if needed
+ // You may want to update the source in your data or state
+ const updatedSources = [...sources];
+ updatedSources[index] = editedSource;
+ setSources(updatedSources);
+ setEditSourceIndex(null);
+ })
+ .catch((error) => {
+ console.error('Error saving edited source:', error);
+ // Handle the error, display an error message, etc.
+ });
+ };
+
const hoverStyle = {
backgroundColor: isHovered ? (theme?.hoverColor || '#00A1E4') : (theme?.themeParameters?.[0]?.value || styles.infoheader.backgroundColor)
};
@@ -29,8 +83,6 @@ export default function MetadataInfo({ title, link, label, icon, specific, uri }
const objectUriParts = getAfterThirdSlash(uri);
const objectUri = `${publicRuntimeConfig.backend}/${objectUriParts}`;
const editedText = newSource;
- console.log(newSource);
- console.log(objectUri);
axios.post(`${objectUri}/add/wasDerivedFrom`, {
object: editedText
@@ -41,16 +93,47 @@ export default function MetadataInfo({ title, link, label, icon, specific, uri }
}
})
.then(response => {
- // Handle response here
- // Refresh the page after successful update
- window.location.reload();
+ // Assuming response.data contains the updated list of sources
+ // Update your sources state with the new list
+ setSources([...sources, processTitle(response.data)]);
+ setIsEditingSource(false);
+ setNewSource('');
})
.catch(error => {
- // Handle error here
console.error('Error adding source:', error);
});
};
+
+ const handleDeleteSource = (event, sourceToDelete) => {
+ event.stopPropagation(); // Prevent event from propagating to parent elements
+
+ if (window.confirm("Do you want to delete this source?")) {
+ const objectUriParts = getAfterThirdSlash(uri);
+ const objectUri = `${publicRuntimeConfig.backend}/${objectUriParts}`;
+
+ // Find the index of the source to delete
+ const indexToDelete = sources.findIndex(source => source === sourceToDelete);
+
+ axios.post(`${objectUri}/remove/wasDerivedFrom`, { object: sourceToDelete }, {
+ headers: {
+ "Accept": "text/plain; charset=UTF-8",
+ "X-authorization": token
+ }
+ })
+ .then(response => {
+ const updatedSources = sources.filter((_, index) => index !== indexToDelete);
+ setSources(updatedSources);
+ })
+ .catch(error => {
+ console.error('Error deleting source:', error);
+ });
+ }
+ };
+
+
+
+
// Rendered label with plus icon
const renderedLabel = (
@@ -68,33 +151,91 @@ export default function MetadataInfo({ title, link, label, icon, specific, uri }
);
- // Check if title is a string and contains commas, then split into an array
- const sources = typeof title === 'string' && title.includes(',') ? title.split(',').map(s => s.trim()) : [title];
+ // // Check if title is a string and contains commas, then split into an array
+ // const sources = typeof title === 'string' && title.includes(',') ? title.split(',').map(s => s.trim()) : [title];
+
+ const urlRegex = /https?:\/\/[^ ,]+/g; // URL regex
+
+ function processTitle(title) {
+ if (typeof title !== 'string') return [title];
+
+ // Placeholder for commas inside URLs
+ const commaPlaceholder = '__COMMA__';
+
+ // Replace commas inside URLs with the placeholder
+ const titleWithPlaceholders = title.replace(urlRegex, url => url.replace(/,/g, commaPlaceholder));
+
+ // Split the title on commas
+ const parts = titleWithPlaceholders.split(',').map(part => part.trim());
+
+ // Restore commas in URLs
+ return parts.map(part => part.replace(new RegExp(commaPlaceholder, 'g'), ','));
+ }
- // Rendered content for the title with each source as a hyperlink
const renderedTitle = (
-
+
);
- // Rendered section including the header and the title (with or without link)
+
+
+
+
+
+
+ // Rendered section including the header and the title (without link)
const renderedSection = (
{renderedLabel}
+ {renderedTitle}
{isEditingSource ? (
-
-
);
return renderedSection;
-}
+}
\ No newline at end of file
diff --git a/frontend/components/Viewing/Shell.js b/frontend/components/Viewing/Shell.js
index 39c9ae8f..8fdfec9a 100644
--- a/frontend/components/Viewing/Shell.js
+++ b/frontend/components/Viewing/Shell.js
@@ -16,7 +16,6 @@ import MasterJSON from './PageJSON/MasterJSON';
// import { useDispatch } from 'react-redux';
export default function Shell(properties) {
- console.log(properties);
const plugins = properties.plugins;
const metadata = properties.metadata;
diff --git a/frontend/components/Viewing/SidePanel.js b/frontend/components/Viewing/SidePanel.js
index e2eb594e..d32004f9 100644
--- a/frontend/components/Viewing/SidePanel.js
+++ b/frontend/components/Viewing/SidePanel.js
@@ -38,9 +38,6 @@ const { publicRuntimeConfig } = getConfig();
* @param {Any} properties Information from the parent component.
*/
export default function SidePanel({ metadata, type, json, uri, plugins }) {
-
- console.log(metadata);
- console.log(plugins);
const [translation, setTranslation] = useState(0);
const [processedUrl, setProcessedUrl] = useState({ original: uri });
const dateCreated = metadata.createdDates.split(", ")[0].replace('T', ' ').replace('Z', '');
diff --git a/frontend/components/Viewing/ViewHeader.js b/frontend/components/Viewing/ViewHeader.js
index af67044f..8f7e59bd 100644
--- a/frontend/components/Viewing/ViewHeader.js
+++ b/frontend/components/Viewing/ViewHeader.js
@@ -16,7 +16,6 @@ import getConfig from "next/config";
const { publicRuntimeConfig } = getConfig();
export default function ViewHeader(properties) {
- console.log(properties);
var displayTitle = properties.type;
if (properties.type.includes('#')) {
displayTitle = properties.type.split('#')[1];
diff --git a/frontend/pages/[...view].js b/frontend/pages/[...view].js
index 3ced2d50..1bc7e3cb 100644
--- a/frontend/pages/[...view].js
+++ b/frontend/pages/[...view].js
@@ -32,8 +32,6 @@ export default function View({ data, error }) {
);
}, [metadata, token]);
- console.log(metadata);
-
// validate part
if (!url || !metadata)
return (
diff --git a/frontend/pages/submissions.js b/frontend/pages/submissions.js
index a5f85212..1f99e2fe 100644
--- a/frontend/pages/submissions.js
+++ b/frontend/pages/submissions.js
@@ -130,11 +130,7 @@ const options = [
];
const compareStrings = (string1, string2) => {
- if (string1 && string2) {
- return (string1.toLowerCase() > string2.toLowerCase() && 1) || -1;
- }
- return -1;
-
+ return (string1.toLowerCase() > string2.toLowerCase() && 1) || -1;
};
const sortMethods = {
@@ -222,7 +218,7 @@ const fetcher = (url, token, dispatch) =>
.catch(error => {
if (error.response && error.response.status === 401) {
dispatch(logoutUser()); // Dispatch the logout action to sign out the user
- window.location.href = '/login'; // Redirect to the login page
+ // window.location.href = '/login'; // Redirect to the login page
} else {
// Handle other errors
error.customMessage = 'Request(s) failed for submissions data. Check the URL to see which one failed';