diff --git a/taxonomy-editor-frontend/package-lock.json b/taxonomy-editor-frontend/package-lock.json index b92d3921..54bab462 100644 --- a/taxonomy-editor-frontend/package-lock.json +++ b/taxonomy-editor-frontend/package-lock.json @@ -18,6 +18,7 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", + "iso-639-1": "^2.1.15", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^11.18.3", @@ -9971,6 +9972,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/iso-639-1": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.15.tgz", + "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -24735,6 +24744,11 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "iso-639-1": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-2.1.15.tgz", + "integrity": "sha512-7c7mBznZu2ktfvyT582E2msM+Udc1EjOyhVRE/0ZsjD9LBtWSm23h3PtiRh2a35XoUsTQQjJXaJzuLjXsOdFDg==" + }, "istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", diff --git a/taxonomy-editor-frontend/package.json b/taxonomy-editor-frontend/package.json index cf25706a..ec268298 100644 --- a/taxonomy-editor-frontend/package.json +++ b/taxonomy-editor-frontend/package.json @@ -13,6 +13,7 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@testing-library/user-event": "^13.5.0", + "iso-639-1": "^2.1.15", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^11.18.3", diff --git a/taxonomy-editor-frontend/src/App.js b/taxonomy-editor-frontend/src/App.js index 2eae8802..019411fe 100644 --- a/taxonomy-editor-frontend/src/App.js +++ b/taxonomy-editor-frontend/src/App.js @@ -2,6 +2,7 @@ import { createTheme, CssBaseline, ThemeProvider } from "@mui/material"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import ResponsiveAppBar from "./components/ResponsiveAppBar"; import Entry from "./pages/allentries"; +import EditEntry from "./pages/editentry"; import Home from './pages/home'; const theme = createTheme({ @@ -20,6 +21,7 @@ function App() { } /> } /> + } /> diff --git a/taxonomy-editor-frontend/src/components/useFetch.jsx b/taxonomy-editor-frontend/src/components/useFetch.jsx index 299d20ad..55257d39 100644 --- a/taxonomy-editor-frontend/src/components/useFetch.jsx +++ b/taxonomy-editor-frontend/src/components/useFetch.jsx @@ -28,8 +28,7 @@ const reducer = (state, action) => { } const useFetch = (url) => { - const [fetchInfo, dispatch] = useReducer(reducer, initialState) - + const [fetchInfo, dispatch] = useReducer(reducer, initialState) useEffect(() => { const abortCont = new AbortController(); diff --git a/taxonomy-editor-frontend/src/pages/allentries/index.jsx b/taxonomy-editor-frontend/src/pages/allentries/index.jsx index 56bbbbab..3db9463a 100644 --- a/taxonomy-editor-frontend/src/pages/allentries/index.jsx +++ b/taxonomy-editor-frontend/src/pages/allentries/index.jsx @@ -66,7 +66,7 @@ const Entry = () => { - diff --git a/taxonomy-editor-frontend/src/pages/editentry/AccumulateAllComponents.jsx b/taxonomy-editor-frontend/src/pages/editentry/AccumulateAllComponents.jsx new file mode 100644 index 00000000..185b7bbd --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/editentry/AccumulateAllComponents.jsx @@ -0,0 +1,53 @@ +import { Box } from "@mui/material"; +import { useEffect, useState } from "react"; +import useFetch from "../../components/useFetch"; +import { createURL } from "./createURL"; +import ListAllOtherProperties from "./ListAllOtherProperties"; +import ListAllProperties from "./ListAllProperties"; +import ListTranslations from "./ListTranslations"; + +// Used for rendering node information +// If node is an "entry": Relations, translations, comments and properties are rendered +// If node is an "stopword/synonym": Stopwords/synonyms, language and comments are rendered +// If node is "header/footer": Comments are rendered + +const AccumulateAllComponents = ({ id }) => { + const { url, isEntry } = createURL(id); + const [nodeObject, setNodeObject] = useState(null); // Storing node information + const { data: node, isPending, isError, isSuccess, errorMessage } = useFetch(url); + + // Setting state of node after fetch + useEffect(() => { + setNodeObject(node?.[0]); + }, [node]) + + if (isError) { + return ( + + {isError &&
{errorMessage}
} +
+ ) + } + if (isPending) { + return ( + + {isPending &&
Loading...
} +
+ ) + } + return ( + + {/* Based on isEntry, respective components are rendered */} + { isEntry ? + + { !!nodeObject && + <> + } + : + <> + } + + ); +} + +export default AccumulateAllComponents; \ No newline at end of file diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListAllOtherProperties.jsx b/taxonomy-editor-frontend/src/pages/editentry/ListAllOtherProperties.jsx new file mode 100644 index 00000000..07545ac5 --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/editentry/ListAllOtherProperties.jsx @@ -0,0 +1,24 @@ +import { Box, Typography } from "@mui/material"; + +// Parent component used for rendering info +// on a stopword, synonym, header or footer + +const ListAllOtherProperties = ({ nodeObject, id, setNodeObject }) => { + + return ( + + Comments + + {id.startsWith('stopword') ? + + Stopwords + : + + Synonyms + } + + + ); +} + +export default ListAllOtherProperties; \ No newline at end of file diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListAllProperties.jsx b/taxonomy-editor-frontend/src/pages/editentry/ListAllProperties.jsx new file mode 100644 index 00000000..52e58428 --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/editentry/ListAllProperties.jsx @@ -0,0 +1,14 @@ +import { Typography, Box } from "@mui/material"; + +// Sub-component used for rendering comments and properties of an "entry" + +const ListAllProperties = ({ nodeObject, setNodeObject }) => { + return ( + + Comments + Properties + + ); +} + +export default ListAllProperties; \ No newline at end of file diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListTranslations.jsx b/taxonomy-editor-frontend/src/pages/editentry/ListTranslations.jsx new file mode 100644 index 00000000..bcb7a430 --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/editentry/ListTranslations.jsx @@ -0,0 +1,95 @@ +import { Typography, Paper, TextField, Stack, Box } from "@mui/material"; +import ISO6391 from 'iso-639-1'; + +// Sub-component for rendering translation of an "entry" + +const ListTranslations = ({ nodeObject, setNodeObject }) => { + let renderedTranslations = {} + + Object.keys(nodeObject).forEach((key) => { + + // Get all tags and its corresponding language code + // Tagids need to be recomputed, so shouldn't be rendered + // Main language isn't considered, since it's rendered separately + + if (key.startsWith('tags') && + !key.endsWith(nodeObject.main_language) && + !key.includes('ids')) { + + // Slice the language code + let languageCode = key.slice(-2); + renderedTranslations[languageCode] = nodeObject[key] + } + }) + + // Helper function used for changing state + function changeData(key, index, value) { + key = 'tags_' + key; + const duplicateData = {...nodeObject}; + duplicateData[key][index] = value; + setNodeObject(duplicateData); + } + + return ( + + {/* Title */} + Translations + {/* Main Language */} + + { ISO6391.getName(nodeObject.main_language) } + + {/* Render main language tags */} + + { + nodeObject["tags_"+nodeObject['main_language']].map((tag, index) => { + return ( + // TODO: Key to be replaced by a UUID + + { + changeData(nodeObject['main_language'], index, event.target.value) + }} + value={tag} + variant="outlined" /> + + ) + }) + } + + + {/* All other languages */} + { + Object.entries(renderedTranslations).map( ([lang, value]) => { + return ( + + + {ISO6391.getName(lang)} + + {/* Render all related tags */} + { + value.map((tag, index) => { + return ( + + { + changeData(lang, index, event.target.value) + }} + value={tag} + variant="outlined" /> + + ) + }) + } + + ) + } ) + } + + ); +} + +export default ListTranslations; \ No newline at end of file diff --git a/taxonomy-editor-frontend/src/pages/editentry/createURL.jsx b/taxonomy-editor-frontend/src/pages/editentry/createURL.jsx new file mode 100644 index 00000000..7f06a59a --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/editentry/createURL.jsx @@ -0,0 +1,18 @@ +import { API_URL } from "../../constants.js" + +export function createURL(id) { + + // Finding URL to send requests + let url = API_URL; + let isEntry = false; + + // ID's can look like: __header__, __footer__, synomym:0, stopword:0 + // For an entry, id looks like en:yogurts + if (id.startsWith('__header__')) { url += 'header/' } + else if (id.startsWith('__footer__')) { url += 'footer/' } + else if (id.startsWith('synonym')) { url += `synonym/${id}/` } + else if (id.startsWith('stopword')) { url += `stopword/${id}/` } + else { url += `entry/${id}/`; isEntry = true; } + + return {url, isEntry} +} \ No newline at end of file diff --git a/taxonomy-editor-frontend/src/pages/editentry/index.jsx b/taxonomy-editor-frontend/src/pages/editentry/index.jsx new file mode 100644 index 00000000..3500a058 --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/editentry/index.jsx @@ -0,0 +1,19 @@ +import { Typography } from "@mui/material"; +import { useParams } from "react-router-dom"; +import AccumulateAllComponents from "./AccumulateAllComponents"; + +const EditEntry = () => { + const { id } = useParams(); + return ( +
+
+ + You are now editing "{id}" + +
+ +
+ ); +} + +export default EditEntry; \ No newline at end of file