Skip to content

Commit

Permalink
refactor(frontend): refactor pages to group common UI elements/data a…
Browse files Browse the repository at this point in the history
…ccess (#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
  • Loading branch information
eric-nguyen-cs authored Mar 7, 2024
1 parent 810a218 commit 14500cb
Show file tree
Hide file tree
Showing 33 changed files with 618 additions and 630 deletions.
12 changes: 9 additions & 3 deletions backend/editor/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
"""
Expand Down Expand Up @@ -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
Expand All @@ -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


Expand Down
11 changes: 11 additions & 0 deletions backend/editor/models/node_models.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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]
38 changes: 37 additions & 1 deletion backend/openapi/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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": {
Expand Down
136 changes: 57 additions & 79 deletions taxonomy-editor-frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -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: <RootLayout />,
errorElement: <PageNotFound />,
children: [
{ path: "/", element: <Home /> },
{ path: "startproject", element: <StartProject /> },
{ path: "gotoproject", element: <GoToProject /> },
{
path: ":taxonomyName/:branchName",
element: <ProjectPage />,
loader: projectLoader(queryClient),
errorElement: <ProjectNotFound />,
children: [
{
path: "export",
element: <ExportTaxonomyWrapper />,
},
{
path: "entry",
element: <RootNodesWrapper />,
},
{
path: "entry/:id",
element: <EditEntryWrapper />,
},
{
path: "search",
element: <SearchNodeWrapper />,
},
{
path: "errors",
element: <Errors />,
},
],
},
],
},
]);

function App() {
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<CssBaseline />
<Router>
<ResponsiveAppBar displayedPages={navLinks} />
<Routes>
<Route
path="/"
element={<Home clearNavBarLinks={resetNavLinks} />}
/>
<Route
path="startproject"
element={<StartProject clearNavBarLinks={resetNavLinks} />}
/>
<Route
path="gotoproject"
element={<GoToProject clearNavBarLinks={resetNavLinks} />}
/>
<Route
path=":taxonomyName/:branchName/export"
element={
<ExportTaxonomy addNavLinks={addTaxonomyBranchNavLinks} />
}
/>
<Route
path=":taxonomyName/:branchName/entry"
element={<RootNodes addNavLinks={addTaxonomyBranchNavLinks} />}
/>
<Route
path=":taxonomyName/:branchName/entry/:id"
element={<EditEntry addNavLinks={addTaxonomyBranchNavLinks} />}
/>
<Route
path=":taxonomyName/:branchName/search"
element={<SearchNode addNavLinks={addTaxonomyBranchNavLinks} />}
/>
<Route
path=":taxonomyName/:branchName/errors"
element={<Errors addNavLinks={addTaxonomyBranchNavLinks} />}
/>
</Routes>
</Router>
<RouterProvider router={router} />
</ThemeProvider>
</QueryClientProvider>
);
Expand Down
1 change: 1 addition & 0 deletions taxonomy-editor-frontend/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
12 changes: 12 additions & 0 deletions taxonomy-editor-frontend/src/client/models/ErrorNode.ts
Original file line number Diff line number Diff line change
@@ -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<string>;
errors: Array<string>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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<any> {
): CancelablePromise<ErrorNode> {
return __request(OpenAPI, {
method: "GET",
url: "/{taxonomy_name}/{branch}/parsing_errors",
Expand Down
27 changes: 20 additions & 7 deletions taxonomy-editor-frontend/src/components/ResponsiveAppBar.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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<string>
): 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);
Expand Down Expand Up @@ -181,5 +196,3 @@ const ResponsiveAppBar = ({ displayedPages }: ResponsiveAppBarProps) => {
</AppBar>
);
};

export default ResponsiveAppBar;
Loading

0 comments on commit 14500cb

Please sign in to comment.