From 14500cbae5c538ccb9262abe81dcc1658aa08486 Mon Sep 17 00:00:00 2001 From: Eric Nguyen Date: Thu, 7 Mar 2024 19:36:23 +0100 Subject: [PATCH] refactor(frontend): refactor pages to group common UI elements/data access (#427) * refactor: move pages to subdirectories * fix: temp fix for /projects pending * refactor: move to react router 6.4 APIs - Move to react router 6.4 APIs with createBrowserRouter, loaders, ... - Add PageNotFound and ProjectNotFound pages - Nest Project routes with a Project Page * chore: add return type to /parsing_error + client gen * refactor: group common styling elements to project page * style: format python * fix: fix small issues * refactor: simplify app bar state * fix: fix typo --- backend/editor/api.py | 12 +- backend/editor/models/node_models.py | 11 ++ backend/openapi/openapi.json | 38 +++- taxonomy-editor-frontend/src/App.tsx | 136 ++++++-------- taxonomy-editor-frontend/src/client/index.ts | 1 + .../src/client/models/ErrorNode.ts | 12 ++ .../src/client/services/DefaultService.ts | 5 +- .../src/components/ResponsiveAppBar.tsx | 27 ++- .../src/components/WarningParsingErrors.tsx | 59 ++++--- .../src/pages/PageNotFound.tsx | 22 +++ .../src/pages/RootLayout.tsx | 11 ++ .../src/pages/editentry/index.tsx | 164 ----------------- .../src/pages/go-to-project/index.tsx | 17 +- .../src/pages/home/index.tsx | 16 +- .../src/pages/project/ProjectNotFound.tsx | 18 ++ .../editentry/AccumulateAllComponents.tsx | 1 - .../editentry/LanguageSelectionDialog.tsx | 0 .../editentry/ListAllEntryProperties.tsx | 0 .../editentry/ListAllNonEntryInfo.tsx | 0 .../editentry/ListEntryChildren.tsx | 0 .../editentry/ListEntryParents.tsx | 0 .../editentry/ListTranslations.tsx | 0 .../editentry/TranslationTags.css | 0 .../editentry/TranslationTags.tsx | 0 .../src/pages/project/editentry/index.tsx | 121 +++++++++++++ .../src/pages/{ => project}/errors/index.tsx | 17 +- .../src/pages/{ => project}/export/index.tsx | 48 +---- .../src/pages/project/index.tsx | 90 ++++++++++ .../pages/{ => project}/root-nodes/index.tsx | 166 +++++++----------- .../{ => project}/search/SearchResults.tsx | 0 .../src/pages/project/search/index.tsx | 99 +++++++++++ .../src/pages/search/index.tsx | 142 --------------- .../src/pages/startproject/index.tsx | 15 +- 33 files changed, 618 insertions(+), 630 deletions(-) create mode 100644 taxonomy-editor-frontend/src/client/models/ErrorNode.ts create mode 100644 taxonomy-editor-frontend/src/pages/PageNotFound.tsx create mode 100644 taxonomy-editor-frontend/src/pages/RootLayout.tsx delete mode 100644 taxonomy-editor-frontend/src/pages/editentry/index.tsx create mode 100644 taxonomy-editor-frontend/src/pages/project/ProjectNotFound.tsx rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/AccumulateAllComponents.tsx (99%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/LanguageSelectionDialog.tsx (100%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/ListAllEntryProperties.tsx (100%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/ListAllNonEntryInfo.tsx (100%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/ListEntryChildren.tsx (100%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/ListEntryParents.tsx (100%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/ListTranslations.tsx (100%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/TranslationTags.css (100%) rename taxonomy-editor-frontend/src/pages/{ => project}/editentry/TranslationTags.tsx (100%) create mode 100644 taxonomy-editor-frontend/src/pages/project/editentry/index.tsx rename taxonomy-editor-frontend/src/pages/{ => project}/errors/index.tsx (90%) rename taxonomy-editor-frontend/src/pages/{ => project}/export/index.tsx (85%) create mode 100644 taxonomy-editor-frontend/src/pages/project/index.tsx rename taxonomy-editor-frontend/src/pages/{ => project}/root-nodes/index.tsx (62%) rename taxonomy-editor-frontend/src/pages/{ => project}/search/SearchResults.tsx (100%) create mode 100644 taxonomy-editor-frontend/src/pages/project/search/index.tsx delete mode 100644 taxonomy-editor-frontend/src/pages/search/index.tsx diff --git a/backend/editor/api.py b/backend/editor/api.py index d49b56b7..be270795 100644 --- a/backend/editor/api.py +++ b/backend/editor/api.py @@ -38,7 +38,7 @@ from .exceptions import GithubBranchExistsError, GithubUploadError # Data model imports -from .models.node_models import EntryNodeCreate, Footer, Header, NodeType +from .models.node_models import EntryNodeCreate, ErrorNode, Footer, Header, NodeType from .models.project_models import Project, ProjectEdit, ProjectStatus from .scheduler import scheduler_lifespan @@ -311,7 +311,7 @@ async def find_footer(response: Response, branch: str, taxonomy_name: str): @app.get("/{taxonomy_name}/{branch}/parsing_errors") -async def find_all_errors(request: Request, branch: str, taxonomy_name: str): +async def find_all_errors(branch: str, taxonomy_name: str) -> ErrorNode: """ Get all errors within taxonomy """ @@ -358,7 +358,11 @@ async def export_to_github( @app.post("/{taxonomy_name}/{branch}/import") async def import_from_github( - request: Request, branch: str, taxonomy_name: str, background_tasks: BackgroundTasks + request: Request, + response: Response, + branch: str, + taxonomy_name: str, + background_tasks: BackgroundTasks, ): """ Get taxonomy from Product Opener GitHub repository @@ -377,6 +381,8 @@ async def import_from_github( raise HTTPException(status_code=409, detail="branch_name: Branch name should be unique!") status = await taxonomy.import_taxonomy(description, ownerName, background_tasks) + # TODO: temporary fix - https://github.com/openfoodfacts/taxonomy-editor/issues/401 + response.headers["Connection"] = "close" return status diff --git a/backend/editor/models/node_models.py b/backend/editor/models/node_models.py index b8b84e3f..67a3b1ad 100644 --- a/backend/editor/models/node_models.py +++ b/backend/editor/models/node_models.py @@ -1,9 +1,11 @@ """ Required pydantic models for API """ + from enum import Enum from .base_models import BaseModel +from .types.datetime import DateTime class NodeType(str, Enum): @@ -24,3 +26,12 @@ class Footer(BaseModel): class EntryNodeCreate(BaseModel): name: str main_language_code: str + + +class ErrorNode(BaseModel): + id: str + taxonomy_name: str + branch_name: str + created_at: DateTime + warnings: list[str] + errors: list[str] diff --git a/backend/openapi/openapi.json b/backend/openapi/openapi.json index 41ad4ef0..919f61ee 100644 --- a/backend/openapi/openapi.json +++ b/backend/openapi/openapi.json @@ -935,7 +935,11 @@ "responses": { "200": { "description": "Successful Response", - "content": { "application/json": { "schema": {} } } + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorNode" } + } + } }, "422": { "description": "Validation Error", @@ -1192,6 +1196,38 @@ "required": ["name", "main_language_code"], "title": "EntryNodeCreate" }, + "ErrorNode": { + "properties": { + "id": { "type": "string", "title": "Id" }, + "taxonomy_name": { "type": "string", "title": "Taxonomy Name" }, + "branch_name": { "type": "string", "title": "Branch Name" }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "warnings": { + "items": { "type": "string" }, + "type": "array", + "title": "Warnings" + }, + "errors": { + "items": { "type": "string" }, + "type": "array", + "title": "Errors" + } + }, + "type": "object", + "required": [ + "id", + "taxonomy_name", + "branch_name", + "created_at", + "warnings", + "errors" + ], + "title": "ErrorNode" + }, "Footer": { "properties": {}, "type": "object", "title": "Footer" }, "HTTPValidationError": { "properties": { diff --git a/taxonomy-editor-frontend/src/App.tsx b/taxonomy-editor-frontend/src/App.tsx index 4302955c..d947b0d2 100644 --- a/taxonomy-editor-frontend/src/App.tsx +++ b/taxonomy-editor-frontend/src/App.tsx @@ -1,18 +1,20 @@ -import { useState, useCallback } from "react"; -import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createTheme, CssBaseline, ThemeProvider } from "@mui/material"; -import ResponsiveAppBar from "./components/ResponsiveAppBar"; -import RootNodes from "./pages/root-nodes"; -import EditEntry from "./pages/editentry"; -import ExportTaxonomy from "./pages/export"; -import GoToProject from "./pages/go-to-project"; -import Home from "./pages/home"; -import SearchNode from "./pages/search"; -import StartProject from "./pages/startproject"; -import Errors from "./pages/errors"; +import { RootNodesWrapper } from "./pages/project/root-nodes"; +import { EditEntryWrapper } from "./pages/project/editentry"; +import { ExportTaxonomyWrapper } from "./pages/project/export"; +import { GoToProject } from "./pages/go-to-project"; +import { Home } from "./pages/home"; +import { SearchNodeWrapper } from "./pages/project/search"; +import { StartProject } from "./pages/startproject"; +import { Errors } from "./pages/project/errors"; +import { ProjectPage, projectLoader } from "./pages/project"; +import { ProjectNotFound } from "./pages/project/ProjectNotFound"; +import { PageNotFound } from "./pages/PageNotFound"; +import { RootLayout } from "./pages/RootLayout"; const theme = createTheme({ typography: { @@ -24,80 +26,56 @@ const theme = createTheme({ }, }); -const queryClient = new QueryClient(); - -function App() { - const [navLinks, setNavLinks] = useState< - Array<{ translationKey: string; url: string }> - >([]); - - const resetNavLinks = useCallback(() => setNavLinks([]), []); - - const addTaxonomyBranchNavLinks = useCallback( - ({ - taxonomyName, - branchName, - }: { - taxonomyName: string; - branchName: string; - }) => { - const urlPrefix = `${taxonomyName}/${branchName}/`; - - const newNavLinks = [ - { url: urlPrefix + "entry", translationKey: "Nodes" }, - { url: urlPrefix + "search", translationKey: "Search" }, - { url: urlPrefix + "export", translationKey: "Export" }, - { url: urlPrefix + "errors", translationKey: "Errors" }, - ]; +const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, +}); - setNavLinks(newNavLinks); - }, - [] - ); +const router = createBrowserRouter([ + { + element: , + errorElement: , + children: [ + { path: "/", element: }, + { path: "startproject", element: }, + { path: "gotoproject", element: }, + { + path: ":taxonomyName/:branchName", + element: , + loader: projectLoader(queryClient), + errorElement: , + children: [ + { + path: "export", + element: , + }, + { + path: "entry", + element: , + }, + { + path: "entry/:id", + element: , + }, + { + path: "search", + element: , + }, + { + path: "errors", + element: , + }, + ], + }, + ], + }, +]); +function App() { return ( - - - - } - /> - } - /> - } - /> - - } - /> - } - /> - } - /> - } - /> - } - /> - - + ); diff --git a/taxonomy-editor-frontend/src/client/index.ts b/taxonomy-editor-frontend/src/client/index.ts index b80a11d2..460760fa 100644 --- a/taxonomy-editor-frontend/src/client/index.ts +++ b/taxonomy-editor-frontend/src/client/index.ts @@ -9,6 +9,7 @@ export type { OpenAPIConfig } from "./core/OpenAPI"; export type { Body_upload_taxonomy__taxonomy_name___branch__upload_post } from "./models/Body_upload_taxonomy__taxonomy_name___branch__upload_post"; export type { EntryNodeCreate } from "./models/EntryNodeCreate"; +export type { ErrorNode } from "./models/ErrorNode"; export type { Footer } from "./models/Footer"; export type { Header } from "./models/Header"; export type { HTTPValidationError } from "./models/HTTPValidationError"; diff --git a/taxonomy-editor-frontend/src/client/models/ErrorNode.ts b/taxonomy-editor-frontend/src/client/models/ErrorNode.ts new file mode 100644 index 00000000..fe40bd52 --- /dev/null +++ b/taxonomy-editor-frontend/src/client/models/ErrorNode.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ErrorNode = { + id: string; + taxonomy_name: string; + branch_name: string; + created_at: string; + warnings: Array; + errors: Array; +}; diff --git a/taxonomy-editor-frontend/src/client/services/DefaultService.ts b/taxonomy-editor-frontend/src/client/services/DefaultService.ts index 2e042b88..2e49bfc4 100644 --- a/taxonomy-editor-frontend/src/client/services/DefaultService.ts +++ b/taxonomy-editor-frontend/src/client/services/DefaultService.ts @@ -4,6 +4,7 @@ /* eslint-disable */ import type { Body_upload_taxonomy__taxonomy_name___branch__upload_post } from "../models/Body_upload_taxonomy__taxonomy_name___branch__upload_post"; import type { EntryNodeCreate } from "../models/EntryNodeCreate"; +import type { ErrorNode } from "../models/ErrorNode"; import type { Footer } from "../models/Footer"; import type { Header } from "../models/Header"; import type { Project } from "../models/Project"; @@ -641,13 +642,13 @@ export class DefaultService { * Get all errors within taxonomy * @param branch * @param taxonomyName - * @returns any Successful Response + * @returns ErrorNode Successful Response * @throws ApiError */ public static findAllErrorsTaxonomyNameBranchParsingErrorsGet( branch: string, taxonomyName: string - ): CancelablePromise { + ): CancelablePromise { return __request(OpenAPI, { method: "GET", url: "/{taxonomy_name}/{branch}/parsing_errors", diff --git a/taxonomy-editor-frontend/src/components/ResponsiveAppBar.tsx b/taxonomy-editor-frontend/src/components/ResponsiveAppBar.tsx index 034e5bbc..bc3f2c1c 100644 --- a/taxonomy-editor-frontend/src/components/ResponsiveAppBar.tsx +++ b/taxonomy-editor-frontend/src/components/ResponsiveAppBar.tsx @@ -1,5 +1,5 @@ -import { useState, useRef } from "react"; -import { Link } from "react-router-dom"; +import { useState, useRef, useMemo } from "react"; +import { Link, useParams, Params } from "react-router-dom"; import AppBar from "@mui/material/AppBar"; import Box from "@mui/material/Box"; @@ -17,11 +17,26 @@ import SettingsIcon from "@mui/icons-material/Settings"; import { useTranslation } from "react-i18next"; import logoUrl from "@/assets/logosmall.jpg"; -type ResponsiveAppBarProps = { - displayedPages: Array<{ translationKey: string; url: string }>; +const getDisplayedPages = ( + params: Params +): Array<{ translationKey: string; url: string }> => { + if (!params.taxonomyName || !params.branchName) { + return []; + } + + const navUrlPrefix = `${params.taxonomyName}/${params.branchName}/`; + return [ + { url: navUrlPrefix + "entry", translationKey: "Nodes" }, + { url: navUrlPrefix + "search", translationKey: "Search" }, + { url: navUrlPrefix + "export", translationKey: "Export" }, + { url: navUrlPrefix + "errors", translationKey: "Errors" }, + ]; }; -const ResponsiveAppBar = ({ displayedPages }: ResponsiveAppBarProps) => { +export const ResponsiveAppBar = () => { + const params = useParams(); + const displayedPages = useMemo(() => getDisplayedPages(params), [params]); + const { t } = useTranslation(); const menuAnchorRef = useRef(); const [isMenuOpen, setIsMenuOpen] = useState(false); @@ -181,5 +196,3 @@ const ResponsiveAppBar = ({ displayedPages }: ResponsiveAppBarProps) => { ); }; - -export default ResponsiveAppBar; diff --git a/taxonomy-editor-frontend/src/components/WarningParsingErrors.tsx b/taxonomy-editor-frontend/src/components/WarningParsingErrors.tsx index 125f54c6..1596d753 100644 --- a/taxonomy-editor-frontend/src/components/WarningParsingErrors.tsx +++ b/taxonomy-editor-frontend/src/components/WarningParsingErrors.tsx @@ -1,7 +1,6 @@ -import React from "react"; -import Alert from "@mui/material/Alert"; -import AlertTitle from "@mui/material/AlertTitle"; -import useFetch from "./useFetch"; +import { Alert, AlertTitle } from "@mui/material"; +import { DefaultService } from "@/client"; +import { useQuery } from "@tanstack/react-query"; interface CustomAlertProps { severity: "error" | "warning" | "info" | "success"; @@ -10,11 +9,8 @@ interface CustomAlertProps { } interface WarningParsingErrorsProps { - baseUrl: string; -} - -interface ParsingErrorsType { - errors: string[]; + taxonomyName: string; + branchName: string; } const CustomAlert: React.FC = ({ @@ -32,27 +28,34 @@ const CustomAlert: React.FC = ({ // warning users the taxonomy had parsing errors, so should not edit it export const WarningParsingErrors: React.FC = ({ - baseUrl, + taxonomyName, + branchName, }) => { - const { data: parsingErrors, isPending: isPendingParsingErrors } = - useFetch(`${baseUrl}parsing_errors`); - if (!isPendingParsingErrors) { + const { data: errorNode } = useQuery({ + queryKey: [ + "findAllErrorsTaxonomyNameBranchParsingErrorsGet", + taxonomyName, + branchName, + ], + queryFn: async () => { + return await DefaultService.findAllErrorsTaxonomyNameBranchParsingErrorsGet( + branchName, + taxonomyName + ); + }, + }); + + if (errorNode && errorNode?.errors.length !== 0) { return ( - <> - {parsingErrors && parsingErrors.errors.length !== 0 && ( - - )} - + ); - } else { - return null; } -}; -export default WarningParsingErrors; + return null; +}; diff --git a/taxonomy-editor-frontend/src/pages/PageNotFound.tsx b/taxonomy-editor-frontend/src/pages/PageNotFound.tsx new file mode 100644 index 00000000..4924b781 --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/PageNotFound.tsx @@ -0,0 +1,22 @@ +import { ResponsiveAppBar } from "@/components/ResponsiveAppBar"; +import { Box, Button, Typography } from "@mui/material"; +import { Link } from "react-router-dom"; + +export const PageNotFound = () => { + return ( + <> + + + Page not found + + + + ); +}; diff --git a/taxonomy-editor-frontend/src/pages/RootLayout.tsx b/taxonomy-editor-frontend/src/pages/RootLayout.tsx new file mode 100644 index 00000000..0c09e33b --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/RootLayout.tsx @@ -0,0 +1,11 @@ +import { ResponsiveAppBar } from "@/components/ResponsiveAppBar"; +import { Outlet } from "react-router-dom"; + +export const RootLayout = () => { + return ( + <> + + + + ); +}; diff --git a/taxonomy-editor-frontend/src/pages/editentry/index.tsx b/taxonomy-editor-frontend/src/pages/editentry/index.tsx deleted file mode 100644 index 7cbd537f..00000000 --- a/taxonomy-editor-frontend/src/pages/editentry/index.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import { useState, useEffect } from "react"; -import { Link, useParams } from "react-router-dom"; - -import { Typography, Stack, IconButton, Button, Box } from "@mui/material"; -import Dialog from "@mui/material/Dialog"; -import DialogActions from "@mui/material/DialogActions"; -import DialogContent from "@mui/material/DialogContent"; -import DialogContentText from "@mui/material/DialogContentText"; -import DialogTitle from "@mui/material/DialogTitle"; -import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; -import AccumulateAllComponents from "./AccumulateAllComponents"; - -import { createBaseURL } from "@/utils"; -import { greyHexCode } from "@/constants"; -import WarningParsingErrors from "@/components/WarningParsingErrors"; - -type EditEntryProps = { - addNavLinks: ({ - branchName, - taxonomyName, - }: { - branchName: string; - taxonomyName: string; - }) => void; - taxonomyName: string; - branchName: string; - id: string; -}; - -const EditEntry = ({ - addNavLinks, - taxonomyName, - branchName, - id, -}: EditEntryProps) => { - const [openDeleteDialog, setOpenDeleteDialog] = useState(false); - const [openSuccessDialog, setOpenSuccessDialog] = useState(false); - - const baseUrl: string = createBaseURL(taxonomyName, branchName); - - useEffect( - function defineMainNavLinks() { - addNavLinks({ branchName, taxonomyName }); - }, - [taxonomyName, branchName, addNavLinks] - ); - - const handleDeleteNode = (baseUrl: string) => { - const data = { id: id }; - fetch(baseUrl + "nodes", { - method: "DELETE", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - }).then(() => { - setOpenDeleteDialog(false); - setOpenSuccessDialog(true); - }); - }; - - return ( - <> - - - {/* Renders id of current node */} - - - - You are now editing "{id}" - - setOpenDeleteDialog(true)} - > - - - - - - {/* Renders node info based on id */} - - - {/* Dialog box for confirmation of deletion of node */} - - Delete a node - - - Are you sure you want to delete this node? - - - - - - - - - {/* Dialog box for acknowledgement of deletion of node */} - - - Your edits have been saved! - - - - The node {id} has been successfully deleted. - - - - - - - - - ); -}; - -type EditEntryWrapperProps = { - addNavLinks: ({ - branchName, - taxonomyName, - }: { - branchName: string; - taxonomyName: string; - }) => void; -}; - -const EditEntryWrapper = ({ addNavLinks }: EditEntryWrapperProps) => { - const { taxonomyName, branchName, id } = useParams(); - - if (!taxonomyName || !branchName || !id) - return ( - - Oops, something went wrong! Please try again later. - - ); - - return ( - - ); -}; - -export default EditEntryWrapper; diff --git a/taxonomy-editor-frontend/src/pages/go-to-project/index.tsx b/taxonomy-editor-frontend/src/pages/go-to-project/index.tsx index 885c5db7..155d25ae 100644 --- a/taxonomy-editor-frontend/src/pages/go-to-project/index.tsx +++ b/taxonomy-editor-frontend/src/pages/go-to-project/index.tsx @@ -21,11 +21,7 @@ type ProjectType = { errors_count: number; }; -type Props = { - clearNavBarLinks: () => void; -}; - -const GoToProject = ({ clearNavBarLinks }: Props) => { +export const GoToProject = () => { const [projectData, setProjectData] = useState([]); const navigate = useNavigate(); @@ -66,13 +62,6 @@ const GoToProject = ({ clearNavBarLinks }: Props) => { setProjectData(newProjects); }, [data]); - useEffect( - function cleanMainNavLinks() { - clearNavBarLinks(); - }, - [clearNavBarLinks] - ); - if (isError) { return ( @@ -97,7 +86,7 @@ const GoToProject = ({ clearNavBarLinks }: Props) => { alignItems="center" justifyContent="center" > - + List of current projects { ); }; - -export default GoToProject; diff --git a/taxonomy-editor-frontend/src/pages/home/index.tsx b/taxonomy-editor-frontend/src/pages/home/index.tsx index 7bb2bd6a..792d0fca 100644 --- a/taxonomy-editor-frontend/src/pages/home/index.tsx +++ b/taxonomy-editor-frontend/src/pages/home/index.tsx @@ -1,4 +1,3 @@ -import { useEffect } from "react"; import { Link } from "react-router-dom"; import Button from "@mui/material/Button"; @@ -10,18 +9,7 @@ import Container from "@mui/material/Container"; import logoUrl from "@/assets/logo.png"; import classificationImgUrl from "@/assets/classification.png"; -type Props = { - clearNavBarLinks: () => void; -}; - -const Home = ({ clearNavBarLinks }: Props) => { - useEffect( - function cleanMainNavLinks() { - clearNavBarLinks(); - }, - [clearNavBarLinks] - ); - +export const Home = () => { return ( { ); }; - -export default Home; diff --git a/taxonomy-editor-frontend/src/pages/project/ProjectNotFound.tsx b/taxonomy-editor-frontend/src/pages/project/ProjectNotFound.tsx new file mode 100644 index 00000000..80f2015b --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/project/ProjectNotFound.tsx @@ -0,0 +1,18 @@ +import { Box, Button, Typography } from "@mui/material"; +import { Link } from "react-router-dom"; + +export const ProjectNotFound = () => { + return ( + + This project does not exist + + + ); +}; diff --git a/taxonomy-editor-frontend/src/pages/editentry/AccumulateAllComponents.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/AccumulateAllComponents.tsx similarity index 99% rename from taxonomy-editor-frontend/src/pages/editentry/AccumulateAllComponents.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/AccumulateAllComponents.tsx index 737f7a16..dda73a59 100644 --- a/taxonomy-editor-frontend/src/pages/editentry/AccumulateAllComponents.tsx +++ b/taxonomy-editor-frontend/src/pages/project/editentry/AccumulateAllComponents.tsx @@ -24,7 +24,6 @@ const AccumulateAllComponents = ({ id, taxonomyName, branchName }) => { const urlPrefix = `/${taxonomyName}/${branchName}`; const isEntry = getNodeType(id) === "entry"; - /* eslint no-unused-vars: ["error", { varsIgnorePattern: "^__" }] */ const { data: node, isPending, diff --git a/taxonomy-editor-frontend/src/pages/editentry/LanguageSelectionDialog.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/LanguageSelectionDialog.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/LanguageSelectionDialog.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/LanguageSelectionDialog.tsx diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListAllEntryProperties.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/ListAllEntryProperties.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/ListAllEntryProperties.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/ListAllEntryProperties.tsx diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListAllNonEntryInfo.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/ListAllNonEntryInfo.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/ListAllNonEntryInfo.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/ListAllNonEntryInfo.tsx diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListEntryChildren.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/ListEntryChildren.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/ListEntryChildren.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/ListEntryChildren.tsx diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListEntryParents.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/ListEntryParents.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/ListEntryParents.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/ListEntryParents.tsx diff --git a/taxonomy-editor-frontend/src/pages/editentry/ListTranslations.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/ListTranslations.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/ListTranslations.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/ListTranslations.tsx diff --git a/taxonomy-editor-frontend/src/pages/editentry/TranslationTags.css b/taxonomy-editor-frontend/src/pages/project/editentry/TranslationTags.css similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/TranslationTags.css rename to taxonomy-editor-frontend/src/pages/project/editentry/TranslationTags.css diff --git a/taxonomy-editor-frontend/src/pages/editentry/TranslationTags.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/TranslationTags.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/editentry/TranslationTags.tsx rename to taxonomy-editor-frontend/src/pages/project/editentry/TranslationTags.tsx diff --git a/taxonomy-editor-frontend/src/pages/project/editentry/index.tsx b/taxonomy-editor-frontend/src/pages/project/editentry/index.tsx new file mode 100644 index 00000000..d57c26f0 --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/project/editentry/index.tsx @@ -0,0 +1,121 @@ +import { useState } from "react"; +import { Link, useParams } from "react-router-dom"; + +import { Typography, Stack, IconButton, Button, Box } from "@mui/material"; +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogContentText from "@mui/material/DialogContentText"; +import DialogTitle from "@mui/material/DialogTitle"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; +import AccumulateAllComponents from "./AccumulateAllComponents"; + +import { createBaseURL } from "@/utils"; +import { greyHexCode } from "@/constants"; + +type EditEntryProps = { + taxonomyName: string; + branchName: string; + id: string; +}; + +const EditEntry = ({ taxonomyName, branchName, id }: EditEntryProps) => { + const [openDeleteDialog, setOpenDeleteDialog] = useState(false); + const [openSuccessDialog, setOpenSuccessDialog] = useState(false); + + const baseUrl: string = createBaseURL(taxonomyName, branchName); + + const handleDeleteNode = (baseUrl: string) => { + const data = { id: id }; + fetch(baseUrl + "nodes", { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + }).then(() => { + setOpenDeleteDialog(false); + setOpenSuccessDialog(true); + }); + }; + + return ( + + {/* Renders id of current node */} + + + + You are now editing "{id}" + + setOpenDeleteDialog(true)} + > + + + + + + {/* Renders node info based on id */} + + + {/* Dialog box for confirmation of deletion of node */} + + Delete a node + + + Are you sure you want to delete this node? + + + + + + + + + {/* Dialog box for acknowledgement of deletion of node */} + + + Your edits have been saved! + + + + The node {id} has been successfully deleted. + + + + + + + + ); +}; + +export const EditEntryWrapper = () => { + const { taxonomyName, branchName, id } = useParams(); + + if (!taxonomyName || !branchName || !id) + return ( + + Oops, something went wrong! Please try again later. + + ); + + return ( + + ); +}; diff --git a/taxonomy-editor-frontend/src/pages/errors/index.tsx b/taxonomy-editor-frontend/src/pages/project/errors/index.tsx similarity index 90% rename from taxonomy-editor-frontend/src/pages/errors/index.tsx rename to taxonomy-editor-frontend/src/pages/project/errors/index.tsx index 9129e1c1..5ac58042 100644 --- a/taxonomy-editor-frontend/src/pages/errors/index.tsx +++ b/taxonomy-editor-frontend/src/pages/project/errors/index.tsx @@ -21,7 +21,7 @@ interface ErrorParams { branchName: string; } -const Errors = ({ addNavLinks }) => { +export const Errors = () => { const { taxonomyName, branchName } = useParams() as unknown as ErrorParams; const baseUrl = createBaseURL(taxonomyName, branchName); const [errors, setErrors] = useState([]); @@ -42,15 +42,6 @@ const Errors = ({ addNavLinks }) => { setErrors(newErrors); }, [errorData]); - useEffect( - function defineMainNavLinks() { - if (!branchName || !taxonomyName) return; - - addNavLinks({ branchName, taxonomyName }); - }, - [taxonomyName, branchName, addNavLinks] - ); - if (isError) { return ( @@ -74,9 +65,7 @@ const Errors = ({ addNavLinks }) => { } return ( - + @@ -142,5 +131,3 @@ const Errors = ({ addNavLinks }) => { ); }; - -export default Errors; diff --git a/taxonomy-editor-frontend/src/pages/export/index.tsx b/taxonomy-editor-frontend/src/pages/project/export/index.tsx similarity index 85% rename from taxonomy-editor-frontend/src/pages/export/index.tsx rename to taxonomy-editor-frontend/src/pages/project/export/index.tsx index 788e2cfe..b764b484 100644 --- a/taxonomy-editor-frontend/src/pages/export/index.tsx +++ b/taxonomy-editor-frontend/src/pages/project/export/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; import { useParams } from "react-router-dom"; import { @@ -109,34 +109,16 @@ const ExportTaxonomyToGithub = ({ }; type ExportTaxonomyProps = { - addNavLinks: ({ - branchName, - taxonomyName, - }: { - branchName: string; - taxonomyName: string; - }) => void; taxonomyName: string; branchName: string; }; -const ExportTaxonomy = ({ - addNavLinks, - taxonomyName, - branchName, -}: ExportTaxonomyProps) => { +const ExportTaxonomy = ({ taxonomyName, branchName }: ExportTaxonomyProps) => { const [isDownloadingFile, setIsDownloadingFile] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const baseURL = createBaseURL(taxonomyName, branchName); - useEffect( - function defineMainNavLinks() { - addNavLinks({ branchName, taxonomyName }); - }, - [taxonomyName, branchName, addNavLinks] - ); - const handleDownload = () => { setIsDownloadingFile(true); setErrorMessage(""); @@ -180,10 +162,7 @@ const ExportTaxonomy = ({ alignItems="center" justifyContent="center" > - + Export Taxonomy void; -}; - -const ExportTaxonomyWrapper = ({ addNavLinks }: ExportTaxonomyWrapperProps) => { +export const ExportTaxonomyWrapper = () => { const { taxonomyName, branchName } = useParams(); if (!taxonomyName || !branchName) @@ -247,12 +216,5 @@ const ExportTaxonomyWrapper = ({ addNavLinks }: ExportTaxonomyWrapperProps) => { ); - return ( - - ); + return ; }; -export default ExportTaxonomyWrapper; diff --git a/taxonomy-editor-frontend/src/pages/project/index.tsx b/taxonomy-editor-frontend/src/pages/project/index.tsx new file mode 100644 index 00000000..a8b6078b --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/project/index.tsx @@ -0,0 +1,90 @@ +import { + LoaderFunction, + Outlet, + useLoaderData, + useParams, +} from "react-router-dom"; +import { DefaultService, Project, ProjectStatus } from "@/client"; +import { QueryClient, useQuery } from "@tanstack/react-query"; +import { Box, CircularProgress, Typography } from "@mui/material"; +import { WarningParsingErrors } from "@/components/WarningParsingErrors"; + +interface ProjectParams { + taxonomyName: string; + branchName: string; +} + +const getProjectQuery = (taxonomyName: string, branchName: string) => ({ + queryKey: [ + "getProjectInfoTaxonomyNameBranchProjectGet", + taxonomyName, + branchName, + ], + queryFn: async () => { + return await DefaultService.getProjectInfoTaxonomyNameBranchProjectGet( + branchName, + taxonomyName + ); + }, +}); + +export const projectLoader = + (queryClient: QueryClient): LoaderFunction => + async ({ params }) => { + // https://github.com/remix-run/react-router/issues/8200#issuecomment-962520661 + const { taxonomyName, branchName } = params as unknown as ProjectParams; + const projectQuery = getProjectQuery(taxonomyName, branchName); + return queryClient.ensureQueryData(projectQuery); + }; + +const ProjectLoading = () => { + return ( + + + + Taxonomy parsing may take several minutes, depending on the complexity + of the taxonomy being imported. + + + ); +}; + +export const ProjectPage = () => { + const initialData = useLoaderData() as Project; + const { taxonomyName, branchName } = useParams() as unknown as ProjectParams; + const { data: project } = useQuery({ + ...getProjectQuery(taxonomyName, branchName), + initialData, + refetchInterval: (query) => { + return query.state.status === "success" && + query.state.data?.status === ProjectStatus.LOADING + ? 2000 + : false; + }, + }); + + return project.status === ProjectStatus.LOADING ? ( + + ) : ( + <> + + + + + + ); +}; diff --git a/taxonomy-editor-frontend/src/pages/root-nodes/index.tsx b/taxonomy-editor-frontend/src/pages/project/root-nodes/index.tsx similarity index 62% rename from taxonomy-editor-frontend/src/pages/root-nodes/index.tsx rename to taxonomy-editor-frontend/src/pages/project/root-nodes/index.tsx index 0c6a3bc3..e5a00809 100644 --- a/taxonomy-editor-frontend/src/pages/root-nodes/index.tsx +++ b/taxonomy-editor-frontend/src/pages/project/root-nodes/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; import { useParams } from "react-router-dom"; import { @@ -28,26 +28,14 @@ import { ProjectStatus, } from "@/backend-types/types"; import NodesTableBody from "@/components/NodesTableBody"; -import WarningParsingErrors from "@/components/WarningParsingErrors"; import { useQuery } from "@tanstack/react-query"; type RootNodesProps = { - addNavLinks: ({ - taxonomyName, - branchName, - }: { - taxonomyName: string; - branchName: string; - }) => void; taxonomyName: string; branchName: string; }; -const RootNodes = ({ - addNavLinks, - taxonomyName, - branchName, -}: RootNodesProps) => { +const RootNodes = ({ taxonomyName, branchName }: RootNodesProps) => { const [openCreateNodeDialog, setOpenCreateNodeDialog] = useState(false); const [openCreateNodeSuccessSnackbar, setCreateNodeOpenSuccessSnackbar] = useState(false); @@ -107,13 +95,6 @@ const RootNodes = ({ nodeIds = nodes.map((node) => node[0].id); } - useEffect( - function defineMainNavLinks() { - addNavLinks({ branchName, taxonomyName }); - }, - [taxonomyName, branchName, addNavLinks] - ); - const handleCloseAddDialog = () => { setOpenCreateNodeDialog(false); }; @@ -160,71 +141,68 @@ const RootNodes = ({ } return ( - <> - -
- - Root Nodes: - - - -
- - - Taxonomy Name - - - Branch Name - - - - - - {toTitleCase(taxonomyName ?? "")} - - - - {branchName} - - -
-
+ + + Root Nodes: + - - Number of root nodes in taxonomy: {nodes.length} - + + + + + Taxonomy Name + + + Branch Name + + + + + + {toTitleCase(taxonomyName ?? "")} + + + + {branchName} + + +
+
+ + + Number of root nodes in taxonomy: {nodes.length} + - {/* Table for listing all nodes in taxonomy */} - - - - - - - Nodes - - { - setOpenCreateNodeDialog(true); - }} - > - - - + {/* Table for listing all nodes in taxonomy */} + +
+ + + - Action + Nodes - - - -
-
- + { + setOpenCreateNodeDialog(true); + }} + > + + + + + Action + + + + + + {/* Dialog box for adding nodes */} @@ -255,21 +233,11 @@ const RootNodes = ({ The node has been successfully added! - +
); }; -type RootNodesWrapperProps = { - addNavLinks: ({ - taxonomyName, - branchName, - }: { - taxonomyName: string; - branchName: string; - }) => void; -}; - -const RootNodesWrapper = ({ addNavLinks }: RootNodesWrapperProps) => { +export const RootNodesWrapper = () => { const { taxonomyName, branchName } = useParams(); if (!taxonomyName || !branchName) return ( @@ -278,13 +246,5 @@ const RootNodesWrapper = ({ addNavLinks }: RootNodesWrapperProps) => {
); - return ( - - ); + return ; }; - -export default RootNodesWrapper; diff --git a/taxonomy-editor-frontend/src/pages/search/SearchResults.tsx b/taxonomy-editor-frontend/src/pages/project/search/SearchResults.tsx similarity index 100% rename from taxonomy-editor-frontend/src/pages/search/SearchResults.tsx rename to taxonomy-editor-frontend/src/pages/project/search/SearchResults.tsx diff --git a/taxonomy-editor-frontend/src/pages/project/search/index.tsx b/taxonomy-editor-frontend/src/pages/project/search/index.tsx new file mode 100644 index 00000000..4c4af7e7 --- /dev/null +++ b/taxonomy-editor-frontend/src/pages/project/search/index.tsx @@ -0,0 +1,99 @@ +import { useState } from "react"; +import { useParams } from "react-router-dom"; + +import { + Typography, + Box, + TextField, + Grid, + IconButton, + InputAdornment, +} from "@mui/material"; +import SearchIcon from "@mui/icons-material/Search"; + +import SearchResults from "./SearchResults"; +import { ENTER_KEYCODE } from "@/constants"; +import classificationImgUrl from "@/assets/classification.png"; + +type SearchNodeProps = { + taxonomyName: string; + branchName: string; +}; + +const SearchNode = ({ taxonomyName, branchName }: SearchNodeProps) => { + const [searchInput, setSearchInput] = useState(""); + const [queryFetchString, setQueryFetchString] = useState(""); + + return ( + + + Search + +
{ + event.preventDefault(); + setQueryFetchString(searchInput.trim()); + }} + > + + + + + + ), + }} + onKeyDown={(e) => { + if (e.keyCode === ENTER_KEYCODE && searchInput.length !== 0) { + setQueryFetchString(searchInput.trim()); + } + }} + onChange={(event) => { + setSearchInput(event.target.value); + }} + value={searchInput} + /> + +
+ {queryFetchString !== "" && ( + + )} +
+ ); +}; + +export const SearchNodeWrapper = () => { + const { taxonomyName, branchName } = useParams(); + + if (!taxonomyName || !branchName) + return ( + + Oops, something went wrong! Please try again later. + + ); + + return ; +}; diff --git a/taxonomy-editor-frontend/src/pages/search/index.tsx b/taxonomy-editor-frontend/src/pages/search/index.tsx deleted file mode 100644 index b15f1cef..00000000 --- a/taxonomy-editor-frontend/src/pages/search/index.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; - -import { - Typography, - Box, - TextField, - Grid, - IconButton, - InputAdornment, -} from "@mui/material"; -import SearchIcon from "@mui/icons-material/Search"; - -import SearchResults from "./SearchResults"; -import { ENTER_KEYCODE } from "@/constants"; -import WarningParsingErrors from "@/components/WarningParsingErrors"; -import { createBaseURL } from "@/utils"; -import classificationImgUrl from "@/assets/classification.png"; - -type SearchNodeProps = { - addNavLinks: ({ - branchName, - taxonomyName, - }: { - branchName: string; - taxonomyName: string; - }) => void; - taxonomyName: string; - branchName: string; -}; - -const SearchNode = ({ - addNavLinks, - taxonomyName, - branchName, -}: SearchNodeProps) => { - const [searchInput, setSearchInput] = useState(""); - const [queryFetchString, setQueryFetchString] = useState(""); - - useEffect( - function defineMainNavLinks() { - addNavLinks({ branchName, taxonomyName }); - }, - [taxonomyName, branchName, addNavLinks] - ); - - const baseUrl = createBaseURL(taxonomyName, branchName); - - return ( - <> - - - - Search - -
{ - event.preventDefault(); - setQueryFetchString(searchInput.trim()); - }} - > - - - - - - ), - }} - onKeyDown={(e) => { - if (e.keyCode === ENTER_KEYCODE && searchInput.length !== 0) { - setQueryFetchString(searchInput.trim()); - } - }} - onChange={(event) => { - setSearchInput(event.target.value); - }} - value={searchInput} - /> - -
- {queryFetchString !== "" && ( - - )} -
- - ); -}; - -type SearchNodeWrapperProps = { - addNavLinks: ({ - branchName, - taxonomyName, - }: { - branchName: string; - taxonomyName: string; - }) => void; -}; - -const SearchNodeWrapper = ({ addNavLinks }: SearchNodeWrapperProps) => { - const { taxonomyName, branchName } = useParams(); - - if (!taxonomyName || !branchName) - return ( - - Oops, something went wrong! Please try again later. - - ); - - return ( - - ); -}; - -export default SearchNodeWrapper; diff --git a/taxonomy-editor-frontend/src/pages/startproject/index.tsx b/taxonomy-editor-frontend/src/pages/startproject/index.tsx index 470f77bc..552826e1 100644 --- a/taxonomy-editor-frontend/src/pages/startproject/index.tsx +++ b/taxonomy-editor-frontend/src/pages/startproject/index.tsx @@ -22,7 +22,7 @@ import { createBaseURL, toSnakeCase } from "@/utils"; const branchNameRegEx = /[^a-z0-9_]+/; -const StartProject = ({ clearNavBarLinks }) => { +export const StartProject = () => { const [ownerName, setOwnerName] = useState(""); const [taxonomyName, setTaxonomyName] = useState(""); const [description, setDescription] = useState(""); @@ -39,13 +39,6 @@ const StartProject = ({ clearNavBarLinks }) => { const [branchName, setBranchName] = useState(findDefaultBranchName()); - useEffect( - function cleanMainNavLinks() { - clearNavBarLinks(); - }, - [clearNavBarLinks] - ); - useEffect(() => { setBranchName(findDefaultBranchName()); }, [ownerName, taxonomyName, findDefaultBranchName]); @@ -73,8 +66,8 @@ const StartProject = ({ clearNavBarLinks }) => { }) .catch(() => { setErrorMessage(errorMessage); - }) - .finally(() => setLoading(false)); + setLoading(false); + }); }; const handleCloseErrorSnackbar = () => { @@ -218,5 +211,3 @@ const StartProject = ({ clearNavBarLinks }) => { ); }; - -export default StartProject;