From 2be08a47ad97c1d88d01e7de18e220f515987cde Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Fri, 2 Dec 2022 18:25:40 +0100 Subject: [PATCH 001/500] spike styled components --- apps/editor/src/app/app.tsx | 5 +- apps/editor/src/screens/index.ts | 1 + apps/editor/src/screens/projects-screen.tsx | 65 +++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100755 apps/editor/src/screens/projects-screen.tsx diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index 0a28d8a42..6ad49098c 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -20,7 +20,7 @@ import { } from "../constants"; import { setUpEventHandling } from "../event-handling"; import type { RootStore } from "../models"; -import { EditorScreen } from "../screens"; +import { EditorScreen, ProjectsScreen } from "../screens"; import { setupRootStore, StoreProvider } from "./root-store"; if (isFromWHO()) { @@ -76,7 +76,8 @@ function App(): JSX.Element { - } /> + } /> + } /> )} diff --git a/apps/editor/src/screens/index.ts b/apps/editor/src/screens/index.ts index 7a4d05b32..1ad0db62d 100644 --- a/apps/editor/src/screens/index.ts +++ b/apps/editor/src/screens/index.ts @@ -1 +1,2 @@ export * from "./editor-screen"; +export * from "./projects-screen"; diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/projects-screen.tsx new file mode 100755 index 000000000..5ee9c14da --- /dev/null +++ b/apps/editor/src/screens/projects-screen.tsx @@ -0,0 +1,65 @@ +import { + Box, + FlexRow, + Screen, + Text, + useIsDraggedOver, +} from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import styled from "styled-components"; + +import { Menu } from "../components/editor"; + +export const ProjectsScreen: React.FC = observer(() => { + const [isDraggedOver, { onDrop, ...dragListeners }] = useIsDraggedOver(); + + const Navbar = styled(FlexRow)` + position: relative; + overflow: hidden; + padding: 3rem; + background: #ff0000; + `; + + const Searchbar = styled(Text)` + font-size: 2rem; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + `; + + const BoxRight = styled(Box)` + font-size: 2rem; + position: absolute; + top: 50%; + left: calc(100% - 3rem); + transform: translate(-100%, -50%); + background: #00ff00; + `; + + const MyMenu = styled(Menu)` + margin: 0px; + `; + + const TextRight = styled(Text)` + font-size: 2rem; + position: absolute; + top: 50%; + left: calc(100% - 3rem); + transform: translate(-100%, -50%); + `; + + return ( + + + Test + + + + + + ); +}); + +export default ProjectsScreen; From fb591651977de0c6588c274252be931e9be39856 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Tue, 6 Dec 2022 16:16:43 +0100 Subject: [PATCH 002/500] feat: creating first Components for UI --- .../menu/dataset-navbar/dataset-navbar.tsx | 18 ++++ .../components/menu/dataset-navbar/index.ts | 1 + apps/editor/src/screens/projects-screen.tsx | 87 +++++++++---------- .../src/lib/components/icon/icons/index.ts | 1 + .../src/lib/components/icon/icons/select.svg | 3 + 5 files changed, 62 insertions(+), 48 deletions(-) create mode 100644 apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx create mode 100644 apps/editor/src/components/menu/dataset-navbar/index.ts create mode 100644 libs/ui-shared/src/lib/components/icon/icons/select.svg diff --git a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx new file mode 100644 index 000000000..59fd72f1c --- /dev/null +++ b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx @@ -0,0 +1,18 @@ +import { SquareButton } from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import styled from "styled-components"; + +const MyButton = styled(SquareButton)` + padding: 10px; +`; + +export const DatasetNavbar = observer<{ + inSelectMode?: boolean; + toggleSelectMode: boolean; +}>(({ inSelectMode, toggleSelectMode }) => + inSelectMode ? ( + + ) : ( + + ), +); diff --git a/apps/editor/src/components/menu/dataset-navbar/index.ts b/apps/editor/src/components/menu/dataset-navbar/index.ts new file mode 100644 index 000000000..81b0108f8 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-navbar/index.ts @@ -0,0 +1 @@ +export * from "./dataset-navbar"; diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/projects-screen.tsx index 5ee9c14da..8b12a7c3c 100755 --- a/apps/editor/src/screens/projects-screen.tsx +++ b/apps/editor/src/screens/projects-screen.tsx @@ -1,63 +1,54 @@ -import { - Box, - FlexRow, - Screen, - Text, - useIsDraggedOver, -} from "@visian/ui-shared"; +import { Box, Modal, Screen, useIsDraggedOver } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; -import React from "react"; +import React, { useState } from "react"; import styled from "styled-components"; -import { Menu } from "../components/editor"; +import { DatasetNavbar } from "../components/menu/dataset-navbar"; export const ProjectsScreen: React.FC = observer(() => { const [isDraggedOver, { onDrop, ...dragListeners }] = useIsDraggedOver(); - - const Navbar = styled(FlexRow)` - position: relative; - overflow: hidden; - padding: 3rem; - background: #ff0000; - `; - - const Searchbar = styled(Text)` - font-size: 2rem; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - `; - - const BoxRight = styled(Box)` - font-size: 2rem; - position: absolute; - top: 50%; - left: calc(100% - 3rem); - transform: translate(-100%, -50%); - background: #00ff00; - `; - - const MyMenu = styled(Menu)` - margin: 0px; + const [inSelectMode, setInSelectMode] = useState(false); + + const Main = styled(Box)` + display: flex; + justify-content: center; + height: 100%; + padding-top: 5rem; + padding-bottom: 5rem; + padding-left: 10rem; + padding-right: 10rem; `; - const TextRight = styled(Text)` - font-size: 2rem; - position: absolute; - top: 50%; - left: calc(100% - 3rem); - transform: translate(-100%, -50%); + const TestModal = styled(Modal)` + vertical-align: middle; + width: 100%; `; return ( - - Test - - - - +
+ + } + > +
+

+ blasd asd as d asd asd asd as d as das da s das dsa das as d asd + as d asd a sd asd a sd asd as da sd asd a +

+
+

bla

+

bla

+

bla

+
+
); }); diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index b42ad8297..68aeb1d29 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -7,6 +7,7 @@ export { ReactComponent as boundedSmartBrush } from "./bounded-smart-brush.svg"; export { ReactComponent as boundedSmartEraser } from "./bounded-smart-eraser.svg"; export { ReactComponent as check } from "./check.svg"; export { ReactComponent as circle } from "./circle.svg"; +export { ReactComponent as select } from "./select.svg"; export { ReactComponent as clearScan } from "./clear-scan.svg"; export { ReactComponent as clearSlice } from "./clear-slice.svg"; export { ReactComponent as columnView } from "./column-view.svg"; diff --git a/libs/ui-shared/src/lib/components/icon/icons/select.svg b/libs/ui-shared/src/lib/components/icon/icons/select.svg new file mode 100644 index 000000000..bf147f758 --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/select.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From b1812691e57cfde6e392254b1d764e35240baee6 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Tue, 6 Dec 2022 20:14:38 +0100 Subject: [PATCH 003/500] feat: Add navbar buttons #4 --- apps/editor/src/app/app.tsx | 4 +- .../menu/dataset-navbar/dataset-navbar.tsx | 45 +++++++++++++----- ...projects-screen.tsx => dataset-screen.tsx} | 46 +++++++++---------- apps/editor/src/screens/index.ts | 2 +- .../src/lib/components/icon/icons/exit.svg | 4 ++ .../src/lib/components/icon/icons/index.ts | 1 + 6 files changed, 62 insertions(+), 40 deletions(-) rename apps/editor/src/screens/{projects-screen.tsx => dataset-screen.tsx} (56%) create mode 100644 libs/ui-shared/src/lib/components/icon/icons/exit.svg diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index 6ad49098c..28bd962dc 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -20,7 +20,7 @@ import { } from "../constants"; import { setUpEventHandling } from "../event-handling"; import type { RootStore } from "../models"; -import { EditorScreen, ProjectsScreen } from "../screens"; +import { DatasetScreen, EditorScreen } from "../screens"; import { setupRootStore, StoreProvider } from "./root-store"; if (isFromWHO()) { @@ -76,7 +76,7 @@ function App(): JSX.Element { - } /> + } /> } /> diff --git a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx index 59fd72f1c..3df99d9de 100644 --- a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx +++ b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx @@ -1,18 +1,39 @@ -import { SquareButton } from "@visian/ui-shared"; -import { observer } from "mobx-react-lite"; +import { ButtonParam, SquareButton } from "@visian/ui-shared"; import styled from "styled-components"; -const MyButton = styled(SquareButton)` - padding: 10px; +const StyledButton = styled(SquareButton)` + margin-left: 10px; `; -export const DatasetNavbar = observer<{ - inSelectMode?: boolean; - toggleSelectMode: boolean; -}>(({ inSelectMode, toggleSelectMode }) => +const StyledButtonParam = styled(ButtonParam)` + margin: 0px; + width: auto; +`; + +// eslint-disable-next-line react/destructuring-assignment +export const DatasetNavbar = ({ + inSelectMode, + toggleSelectMode, +}: { + inSelectMode: any; + toggleSelectMode: any; +}) => inSelectMode ? ( - + <> + + + + + + ) : ( - - ), -); + + ); diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx similarity index 56% rename from apps/editor/src/screens/projects-screen.tsx rename to apps/editor/src/screens/dataset-screen.tsx index 8b12a7c3c..8faaf168a 100755 --- a/apps/editor/src/screens/projects-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -5,24 +5,24 @@ import styled from "styled-components"; import { DatasetNavbar } from "../components/menu/dataset-navbar"; -export const ProjectsScreen: React.FC = observer(() => { - const [isDraggedOver, { onDrop, ...dragListeners }] = useIsDraggedOver(); - const [inSelectMode, setInSelectMode] = useState(false); +const Main = styled(Box)` + display: flex; + justify-content: center; + height: 100%; + padding-top: 5rem; + padding-bottom: 5rem; + padding-left: 10rem; + padding-right: 10rem; +`; - const Main = styled(Box)` - display: flex; - justify-content: center; - height: 100%; - padding-top: 5rem; - padding-bottom: 5rem; - padding-left: 10rem; - padding-right: 10rem; - `; +const TestModal = styled(Modal)` + vertical-align: middle; + width: 100%; +`; - const TestModal = styled(Modal)` - vertical-align: middle; - width: 100%; - `; +export const DatasetScreen: React.FC = observer(() => { + const [isDraggedOver, { onDrop, ...dragListeners }] = useIsDraggedOver(); + const [inSelectMode, setInSelectMode] = useState(false); return ( @@ -34,23 +34,19 @@ export const ProjectsScreen: React.FC = observer(() => { headerChildren={ { + setInSelectMode((prev) => !prev); + }} /> } >
-

- blasd asd as d asd asd asd as d as das da s das dsa das as d asd - as d asd a sd asd a sd asd as da sd asd a -

+

blasd asd

-

bla

-

bla

-

bla

); }); -export default ProjectsScreen; +export default DatasetScreen; diff --git a/apps/editor/src/screens/index.ts b/apps/editor/src/screens/index.ts index 1ad0db62d..350d74fa8 100644 --- a/apps/editor/src/screens/index.ts +++ b/apps/editor/src/screens/index.ts @@ -1,2 +1,2 @@ export * from "./editor-screen"; -export * from "./projects-screen"; +export * from "./dataset-screen"; diff --git a/libs/ui-shared/src/lib/components/icon/icons/exit.svg b/libs/ui-shared/src/lib/components/icon/icons/exit.svg new file mode 100644 index 000000000..a4e050eaa --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/exit.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index 68aeb1d29..54b354f3d 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -5,6 +5,7 @@ export { ReactComponent as arrowUp } from "./arrow-up.svg"; export { ReactComponent as backspace } from "./backspace.svg"; export { ReactComponent as boundedSmartBrush } from "./bounded-smart-brush.svg"; export { ReactComponent as boundedSmartEraser } from "./bounded-smart-eraser.svg"; +export { ReactComponent as exit } from "./exit.svg"; export { ReactComponent as check } from "./check.svg"; export { ReactComponent as circle } from "./circle.svg"; export { ReactComponent as select } from "./select.svg"; From a3fe17c1eeee9dd14adef4eb1400487103438661 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Wed, 7 Dec 2022 10:38:32 +0100 Subject: [PATCH 004/500] feat: add basic list of files #4 --- .../dataset-file-list/dataset-file-list.tsx | 23 +++++++++++ .../menu/dataset-file-list/file-item.tsx | 38 +++++++++++++++++++ .../menu/dataset-file-list/index.ts | 1 + .../menu/dataset-navbar/dataset-navbar.tsx | 12 +++++- apps/editor/src/screens/dataset-moc.tsx | 22 +++++++++++ apps/editor/src/screens/dataset-screen.tsx | 29 ++++++++++++-- .../icon/icons/checkbox-checked.svg | 1 + .../icon/icons/checkbox-unchecked.svg | 1 + .../src/lib/components/icon/icons/index.ts | 2 + 9 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx create mode 100644 apps/editor/src/components/menu/dataset-file-list/file-item.tsx create mode 100644 apps/editor/src/components/menu/dataset-file-list/index.ts create mode 100755 apps/editor/src/screens/dataset-moc.tsx create mode 100644 libs/ui-shared/src/lib/components/icon/icons/checkbox-checked.svg create mode 100644 libs/ui-shared/src/lib/components/icon/icons/checkbox-unchecked.svg diff --git a/apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx b/apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx new file mode 100644 index 000000000..3da984906 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx @@ -0,0 +1,23 @@ +import { FileItem } from "./file-item"; + +export const DatasetFileList = ({ + inSelectMode, + dataset, + setSelection, +}: { + inSelectMode: boolean; + dataset: any; + setSelection: any; +}) => { + const fileList = dataset.map((file: any) => ( + setSelection(file.id, !file.isSelected)} + key={file.id} + /> + )); + + // eslint-disable-next-line react/jsx-no-useless-fragment + return
{fileList}
; +}; diff --git a/apps/editor/src/components/menu/dataset-file-list/file-item.tsx b/apps/editor/src/components/menu/dataset-file-list/file-item.tsx new file mode 100644 index 000000000..651c8edfe --- /dev/null +++ b/apps/editor/src/components/menu/dataset-file-list/file-item.tsx @@ -0,0 +1,38 @@ +import { ListItem, SquareButton } from "@visian/ui-shared"; +import styled from "styled-components"; + +const Spacer = styled.div` + width: 10px; +`; + +const InvisibleButton = styled(SquareButton)` + border: none; + padding: 12px; +`; + +export const FileItem = ({ + inSelectMode, + file, + toggleSelection, +}: { + inSelectMode: boolean; + file: any; + toggleSelection: any; +}) => { + console.log(file.isSelected); + return ( + + {inSelectMode && ( + <> + + + + )} +

{file.name}

+
+ ); +}; diff --git a/apps/editor/src/components/menu/dataset-file-list/index.ts b/apps/editor/src/components/menu/dataset-file-list/index.ts new file mode 100644 index 000000000..249e96370 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-file-list/index.ts @@ -0,0 +1 @@ +export * from "./dataset-file-list"; diff --git a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx index 3df99d9de..bc2553244 100644 --- a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx +++ b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx @@ -14,20 +14,28 @@ const StyledButtonParam = styled(ButtonParam)` export const DatasetNavbar = ({ inSelectMode, toggleSelectMode, + toggleSelectAll, }: { inSelectMode: any; toggleSelectMode: any; + toggleSelectAll: any; }) => inSelectMode ? ( <> - + toggleSelectAll(true)} + /> { + toggleSelectMode(); + toggleSelectAll(false); + }} /> ) : ( diff --git a/apps/editor/src/screens/dataset-moc.tsx b/apps/editor/src/screens/dataset-moc.tsx new file mode 100755 index 000000000..2fb19123e --- /dev/null +++ b/apps/editor/src/screens/dataset-moc.tsx @@ -0,0 +1,22 @@ +export const datasetMoc = [ + { + id: 0, + name: "File 1", + isSelected: false, + }, + { + id: 1, + name: "File 2", + isSelected: false, + }, + { + id: 2, + name: "File 3", + isSelected: false, + }, + { + id: 3, + name: "File 4", + isSelected: false, + }, +]; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 8faaf168a..d7ba4e239 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -3,7 +3,9 @@ import { observer } from "mobx-react-lite"; import React, { useState } from "react"; import styled from "styled-components"; +import { DatasetFileList } from "../components/menu/dataset-file-list"; import { DatasetNavbar } from "../components/menu/dataset-navbar"; +import { datasetMoc } from "./dataset-moc"; const Main = styled(Box)` display: flex; @@ -23,6 +25,24 @@ const TestModal = styled(Modal)` export const DatasetScreen: React.FC = observer(() => { const [isDraggedOver, { onDrop, ...dragListeners }] = useIsDraggedOver(); const [inSelectMode, setInSelectMode] = useState(false); + const [dataset, setDataset] = useState(datasetMoc); + + const setSelection = (id: number, selection: boolean) => { + setDataset((prevDataset) => + prevDataset.map((file) => + file.id === id ? { ...file, isSelected: selection } : file, + ), + ); + }; + + const toggleSelectAll = (select: boolean) => { + setDataset((prevDataset) => + prevDataset.map((file) => ({ + ...file, + isSelected: select, + })), + ); + }; return ( @@ -37,12 +57,15 @@ export const DatasetScreen: React.FC = observer(() => { toggleSelectMode={() => { setInSelectMode((prev) => !prev); }} + toggleSelectAll={toggleSelectAll} /> } > -
-

blasd asd

-
+
diff --git a/libs/ui-shared/src/lib/components/icon/icons/checkbox-checked.svg b/libs/ui-shared/src/lib/components/icon/icons/checkbox-checked.svg new file mode 100644 index 000000000..46b13c5eb --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/checkbox-checked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/checkbox-unchecked.svg b/libs/ui-shared/src/lib/components/icon/icons/checkbox-unchecked.svg new file mode 100644 index 000000000..4b2a4cd7e --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/checkbox-unchecked.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index 54b354f3d..e640b6ff9 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -58,3 +58,5 @@ export { ReactComponent as undo } from "./undo.svg"; export { ReactComponent as upArrow } from "./up-arrow.svg"; export { ReactComponent as whoAI } from "./who-ai.svg"; export { ReactComponent as xSmall } from "./x-small.svg"; +export { ReactComponent as checked } from "./checkbox-checked.svg"; +export { ReactComponent as unchecked } from "./checkbox-unchecked.svg"; From 97763f6f22d5907067be222d9a33471ee1b98ecd Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Thu, 8 Dec 2022 15:01:53 +0100 Subject: [PATCH 005/500] refactor: renaming and refactoring of dataset-screen #4 --- .../editor/src/components/menu/data-types.tsx | 14 ++++ .../dataset-document-list.tsx | 29 ++++++++ .../menu/dataset-document-list/file-item.tsx | 39 ++++++++++ .../menu/dataset-document-list/index.ts | 1 + .../dataset-file-list/dataset-file-list.tsx | 23 ------ .../menu/dataset-file-list/file-item.tsx | 38 ---------- .../menu/dataset-file-list/index.ts | 1 - .../menu/dataset-navbar/dataset-navbar.tsx | 14 ++-- apps/editor/src/screens/dataset-moc.tsx | 16 ++--- apps/editor/src/screens/dataset-screen.tsx | 72 +++++++++++++------ 10 files changed, 148 insertions(+), 99 deletions(-) create mode 100644 apps/editor/src/components/menu/data-types.tsx create mode 100644 apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx create mode 100644 apps/editor/src/components/menu/dataset-document-list/file-item.tsx create mode 100644 apps/editor/src/components/menu/dataset-document-list/index.ts delete mode 100644 apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx delete mode 100644 apps/editor/src/components/menu/dataset-file-list/file-item.tsx delete mode 100644 apps/editor/src/components/menu/dataset-file-list/index.ts diff --git a/apps/editor/src/components/menu/data-types.tsx b/apps/editor/src/components/menu/data-types.tsx new file mode 100644 index 000000000..63df4f67d --- /dev/null +++ b/apps/editor/src/components/menu/data-types.tsx @@ -0,0 +1,14 @@ +export type DocumentProp = { + isSelected: boolean; +}; + +export type DatasetProps = { + [id: string]: DocumentProp; +}; + +export type DocumentItem = { + id: string; + name: string; +}; + +export type Dataset = DocumentItem[]; diff --git a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx new file mode 100644 index 000000000..1e31cf05a --- /dev/null +++ b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx @@ -0,0 +1,29 @@ +import { Dataset, DatasetProps, DocumentItem } from "../data-types"; +import { FileItem } from "./file-item"; + +export const DatasetDocumentList = ({ + inSelectMode, + dataset, + datasetProps, + setSelection, +}: { + inSelectMode: boolean; + dataset: Dataset; + datasetProps: DatasetProps; + setSelection: (id: string, selction: boolean) => void; +}) => { + const documentList = dataset.map((documentItem: DocumentItem) => ( + + setSelection(documentItem.id, !datasetProps[documentItem.id].isSelected) + } + key={documentItem.id} + /> + )); + + // eslint-disable-next-line react/jsx-no-useless-fragment + return
{documentList}
; +}; diff --git a/apps/editor/src/components/menu/dataset-document-list/file-item.tsx b/apps/editor/src/components/menu/dataset-document-list/file-item.tsx new file mode 100644 index 000000000..851d65307 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-document-list/file-item.tsx @@ -0,0 +1,39 @@ +import { ListItem, SquareButton } from "@visian/ui-shared"; +import styled from "styled-components"; + +import { DocumentItem, DocumentProp } from "../data-types"; + +const Spacer = styled.div` + width: 10px; +`; + +const InvisibleButton = styled(SquareButton)` + border: none; + padding: 12px; +`; + +export const FileItem = ({ + inSelectMode, + documentItem, + documentProp, + toggleSelection, +}: { + inSelectMode: boolean; + documentItem: DocumentItem; + documentProp: DocumentProp; + toggleSelection: () => void; +}) => ( + + {inSelectMode && ( + <> + + + + )} +

{documentItem.name}

+
+); diff --git a/apps/editor/src/components/menu/dataset-document-list/index.ts b/apps/editor/src/components/menu/dataset-document-list/index.ts new file mode 100644 index 000000000..29a49f5e7 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-document-list/index.ts @@ -0,0 +1 @@ +export * from "./dataset-document-list"; diff --git a/apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx b/apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx deleted file mode 100644 index 3da984906..000000000 --- a/apps/editor/src/components/menu/dataset-file-list/dataset-file-list.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { FileItem } from "./file-item"; - -export const DatasetFileList = ({ - inSelectMode, - dataset, - setSelection, -}: { - inSelectMode: boolean; - dataset: any; - setSelection: any; -}) => { - const fileList = dataset.map((file: any) => ( - setSelection(file.id, !file.isSelected)} - key={file.id} - /> - )); - - // eslint-disable-next-line react/jsx-no-useless-fragment - return
{fileList}
; -}; diff --git a/apps/editor/src/components/menu/dataset-file-list/file-item.tsx b/apps/editor/src/components/menu/dataset-file-list/file-item.tsx deleted file mode 100644 index 651c8edfe..000000000 --- a/apps/editor/src/components/menu/dataset-file-list/file-item.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { ListItem, SquareButton } from "@visian/ui-shared"; -import styled from "styled-components"; - -const Spacer = styled.div` - width: 10px; -`; - -const InvisibleButton = styled(SquareButton)` - border: none; - padding: 12px; -`; - -export const FileItem = ({ - inSelectMode, - file, - toggleSelection, -}: { - inSelectMode: boolean; - file: any; - toggleSelection: any; -}) => { - console.log(file.isSelected); - return ( - - {inSelectMode && ( - <> - - - - )} -

{file.name}

-
- ); -}; diff --git a/apps/editor/src/components/menu/dataset-file-list/index.ts b/apps/editor/src/components/menu/dataset-file-list/index.ts deleted file mode 100644 index 249e96370..000000000 --- a/apps/editor/src/components/menu/dataset-file-list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./dataset-file-list"; diff --git a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx index bc2553244..54d945359 100644 --- a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx +++ b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx @@ -5,7 +5,7 @@ const StyledButton = styled(SquareButton)` margin-left: 10px; `; -const StyledButtonParam = styled(ButtonParam)` +const StyledTextButton = styled(ButtonParam)` margin: 0px; width: auto; `; @@ -16,15 +16,17 @@ export const DatasetNavbar = ({ toggleSelectMode, toggleSelectAll, }: { - inSelectMode: any; - toggleSelectMode: any; - toggleSelectAll: any; + inSelectMode: boolean; + toggleSelectMode: () => void; + toggleSelectAll: (selection: boolean) => void; }) => inSelectMode ? ( <> - toggleSelectAll(true)} + handlePress={() => { + toggleSelectAll(true); + }} /> diff --git a/apps/editor/src/screens/dataset-moc.tsx b/apps/editor/src/screens/dataset-moc.tsx index 2fb19123e..ffff95425 100755 --- a/apps/editor/src/screens/dataset-moc.tsx +++ b/apps/editor/src/screens/dataset-moc.tsx @@ -1,22 +1,20 @@ -export const datasetMoc = [ +import { Dataset } from "../components/menu/data-types"; + +export const datasetMoc: Dataset = [ { - id: 0, + id: "0", name: "File 1", - isSelected: false, }, { - id: 1, + id: "1", name: "File 2", - isSelected: false, }, { - id: 2, + id: "2", name: "File 3", - isSelected: false, }, { - id: 3, + id: "3", name: "File 4", - isSelected: false, }, ]; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index d7ba4e239..3e2f48dbe 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,9 +1,14 @@ import { Box, Modal, Screen, useIsDraggedOver } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import styled from "styled-components"; -import { DatasetFileList } from "../components/menu/dataset-file-list"; +import { + Dataset, + DatasetProps, + DocumentItem, +} from "../components/menu/data-types"; +import { DatasetDocumentList } from "../components/menu/dataset-document-list"; import { DatasetNavbar } from "../components/menu/dataset-navbar"; import { datasetMoc } from "./dataset-moc"; @@ -17,37 +22,59 @@ const Main = styled(Box)` padding-right: 10rem; `; -const TestModal = styled(Modal)` +const DatasetModal = styled(Modal)` vertical-align: middle; width: 100%; `; export const DatasetScreen: React.FC = observer(() => { const [isDraggedOver, { onDrop, ...dragListeners }] = useIsDraggedOver(); + const [inSelectMode, setInSelectMode] = useState(false); - const [dataset, setDataset] = useState(datasetMoc); + const [dataset, setDataset] = useState([] as Dataset); + const [datasetProps, setDatasetProps] = useState({} as DatasetProps); + + // fetch dataset + useEffect(() => { + setDataset(datasetMoc); + }, []); + + // sync datasetProps with dataset + useEffect(() => { + setDatasetProps((prevDatasetProps) => { + const newDatasetProps: DatasetProps = {}; + dataset.forEach((documentItem: DocumentItem) => { + newDatasetProps[documentItem.id] = prevDatasetProps[ + documentItem.id + ] ?? { + isSelected: false, + }; + }); + return newDatasetProps; + }); + }, [dataset]); - const setSelection = (id: number, selection: boolean) => { - setDataset((prevDataset) => - prevDataset.map((file) => - file.id === id ? { ...file, isSelected: selection } : file, - ), - ); + const setSelection = (id: string, selection: boolean) => { + setDatasetProps((prevDatasetProps: DatasetProps) => ({ + ...prevDatasetProps, + [id]: { ...prevDatasetProps[id], isSelected: selection }, + })); }; - const toggleSelectAll = (select: boolean) => { - setDataset((prevDataset) => - prevDataset.map((file) => ({ - ...file, - isSelected: select, - })), - ); + const setSelectAll = (select: boolean) => { + setDatasetProps((prevDatasetProps: DatasetProps) => { + const newDatasetProps: DatasetProps = {}; + Object.keys(prevDatasetProps).forEach((id: string) => { + newDatasetProps[id] = { ...prevDatasetProps[id], isSelected: select }; + }); + return newDatasetProps; + }); }; return (
- { { - setInSelectMode((prev) => !prev); + setInSelectMode((prev: boolean) => !prev); }} - toggleSelectAll={toggleSelectAll} + toggleSelectAll={setSelectAll} /> } > - - +
); From b64d023213716130dcf3797b4764745c7ad57a1c Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Fri, 9 Dec 2022 11:20:24 +0100 Subject: [PATCH 006/500] #4 use webhook api --- apps/editor/src/screens/dataset-moc.json | 18 ++++++++++++ apps/editor/src/screens/dataset-moc.tsx | 34 ++++++++++------------ apps/editor/src/screens/dataset-screen.tsx | 4 +-- 3 files changed, 36 insertions(+), 20 deletions(-) create mode 100644 apps/editor/src/screens/dataset-moc.json diff --git a/apps/editor/src/screens/dataset-moc.json b/apps/editor/src/screens/dataset-moc.json new file mode 100644 index 000000000..c948ca102 --- /dev/null +++ b/apps/editor/src/screens/dataset-moc.json @@ -0,0 +1,18 @@ +[ + { + "id": "0", + "name": "File 1" + }, + { + "id": "1", + "name": "File 2" + }, + { + "id": "2", + "name": "File 3" + }, + { + "id": "3", + "name": "File 4" + } +] diff --git a/apps/editor/src/screens/dataset-moc.tsx b/apps/editor/src/screens/dataset-moc.tsx index ffff95425..29d89eccb 100755 --- a/apps/editor/src/screens/dataset-moc.tsx +++ b/apps/editor/src/screens/dataset-moc.tsx @@ -1,20 +1,18 @@ import { Dataset } from "../components/menu/data-types"; -export const datasetMoc: Dataset = [ - { - id: "0", - name: "File 1", - }, - { - id: "1", - name: "File 2", - }, - { - id: "2", - name: "File 3", - }, - { - id: "3", - name: "File 4", - }, -]; +const path = "https://webhook.site/faaa6131-98ba-4007-9623-e4af82e3580c"; + +export const getDataset = async () => { + let dataset: Dataset = []; + await fetch(path) + .then((response) => { + if (!response.ok) { + throw response; + } + return response.json(); + }) + .then((data) => { + dataset = data; + }); + return dataset; +}; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 3e2f48dbe..ab59c080f 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -10,7 +10,7 @@ import { } from "../components/menu/data-types"; import { DatasetDocumentList } from "../components/menu/dataset-document-list"; import { DatasetNavbar } from "../components/menu/dataset-navbar"; -import { datasetMoc } from "./dataset-moc"; +import { getDataset } from "./dataset-moc"; const Main = styled(Box)` display: flex; @@ -36,7 +36,7 @@ export const DatasetScreen: React.FC = observer(() => { // fetch dataset useEffect(() => { - setDataset(datasetMoc); + (async () => setDataset(await getDataset()))(); }, []); // sync datasetProps with dataset From 010ebc1864f5f704e83f7007423c39590c940f04 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Fri, 9 Dec 2022 13:19:49 +0100 Subject: [PATCH 007/500] #4 show annotations (not working) --- .../editor/src/components/menu/data-types.tsx | 6 ++ .../dataset-document-list.tsx | 15 ++++- .../document-list-item.tsx | 64 +++++++++++++++++++ .../menu/dataset-document-list/file-item.tsx | 39 ----------- .../menu/dataset-navbar/dataset-navbar.tsx | 15 ++--- apps/editor/src/screens/dataset-moc.json | 39 +++++++++-- apps/editor/src/screens/dataset-screen.tsx | 18 +++++- .../src/lib/components/icon/icons/chevron.svg | 3 + .../src/lib/components/icon/icons/index.ts | 1 + 9 files changed, 144 insertions(+), 56 deletions(-) create mode 100644 apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx delete mode 100644 apps/editor/src/components/menu/dataset-document-list/file-item.tsx create mode 100644 libs/ui-shared/src/lib/components/icon/icons/chevron.svg diff --git a/apps/editor/src/components/menu/data-types.tsx b/apps/editor/src/components/menu/data-types.tsx index 63df4f67d..2a0171da0 100644 --- a/apps/editor/src/components/menu/data-types.tsx +++ b/apps/editor/src/components/menu/data-types.tsx @@ -9,6 +9,12 @@ export type DatasetProps = { export type DocumentItem = { id: string; name: string; + annoations: Annotation[]; }; export type Dataset = DocumentItem[]; + +export type Annotation = { + id: string; + name: string; +}; diff --git a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx index 1e31cf05a..8f8097254 100644 --- a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx @@ -1,5 +1,14 @@ +import { List, stopPropagation } from "@visian/ui-shared"; +import styled from "styled-components"; + import { Dataset, DatasetProps, DocumentItem } from "../data-types"; -import { FileItem } from "./file-item"; +import { DocumentListItem } from "./document-list-item"; + +const DocumentList = styled(List)` + width: 100%; + height: 400px; + overflow-y: auto; +`; export const DatasetDocumentList = ({ inSelectMode, @@ -13,7 +22,7 @@ export const DatasetDocumentList = ({ setSelection: (id: string, selction: boolean) => void; }) => { const documentList = dataset.map((documentItem: DocumentItem) => ( - {documentList}; + return {documentList}; }; diff --git a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx new file mode 100644 index 000000000..627c42fa4 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx @@ -0,0 +1,64 @@ +import { List, ListItem, SquareButton } from "@visian/ui-shared"; +import { useState } from "react"; +import styled from "styled-components"; + +import { DocumentItem, DocumentProp } from "../data-types"; + +const Spacer = styled.div` + width: 10px; +`; + +const ExpandedSpacer = styled.div` + margin-right: auto; +`; + +const InvisibleButton = styled(SquareButton)` + border: none; + padding: 12px; +`; + +export const DocumentListItem = ({ + inSelectMode, + documentItem, + documentProp, + toggleSelection, +}: { + inSelectMode: boolean; + documentItem: DocumentItem; + documentProp: DocumentProp; + toggleSelection: () => void; +}) => { + const [showAnnotations, setShowAnnotations] = useState(false); + + const annotations = documentItem.annoations.map((annotation) => ( + {annotation.name} + )); + + return ( + <> + + {inSelectMode && ( + <> + + + + )} +

{documentItem.name}

+ + +
+ {annotations} + + ); +}; diff --git a/apps/editor/src/components/menu/dataset-document-list/file-item.tsx b/apps/editor/src/components/menu/dataset-document-list/file-item.tsx deleted file mode 100644 index 851d65307..000000000 --- a/apps/editor/src/components/menu/dataset-document-list/file-item.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { ListItem, SquareButton } from "@visian/ui-shared"; -import styled from "styled-components"; - -import { DocumentItem, DocumentProp } from "../data-types"; - -const Spacer = styled.div` - width: 10px; -`; - -const InvisibleButton = styled(SquareButton)` - border: none; - padding: 12px; -`; - -export const FileItem = ({ - inSelectMode, - documentItem, - documentProp, - toggleSelection, -}: { - inSelectMode: boolean; - documentItem: DocumentItem; - documentProp: DocumentProp; - toggleSelection: () => void; -}) => ( - - {inSelectMode && ( - <> - - - - )} -

{documentItem.name}

-
-); diff --git a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx index 54d945359..56bed0d70 100644 --- a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx +++ b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx @@ -13,20 +13,20 @@ const StyledTextButton = styled(ButtonParam)` // eslint-disable-next-line react/destructuring-assignment export const DatasetNavbar = ({ inSelectMode, + allSelected, toggleSelectMode, toggleSelectAll, }: { inSelectMode: boolean; + allSelected: boolean; toggleSelectMode: () => void; - toggleSelectAll: (selection: boolean) => void; + toggleSelectAll: () => void; }) => inSelectMode ? ( <> { - toggleSelectAll(true); - }} + labelTx={allSelected ? "Deselect All" : "Select All"} + handlePress={toggleSelectAll} /> @@ -34,10 +34,7 @@ export const DatasetNavbar = ({ { - toggleSelectMode(); - toggleSelectAll(false); - }} + onPointerDown={toggleSelectMode} /> ) : ( diff --git a/apps/editor/src/screens/dataset-moc.json b/apps/editor/src/screens/dataset-moc.json index c948ca102..e7bf68f87 100644 --- a/apps/editor/src/screens/dataset-moc.json +++ b/apps/editor/src/screens/dataset-moc.json @@ -1,18 +1,49 @@ [ { "id": "0", - "name": "File 1" + "name": "File 1", + "annotations": [ + { + "id": "0", + "name": "Annotaion 1" + }, + { + "id": "1", + "name": "Annotaion 2" + } + ] }, { "id": "1", - "name": "File 2" + "name": "File 2", + "annotations": [ + { + "id": "0", + "name": "Annotaion 1" + }, + { + "id": "1", + "name": "Annotaion 2" + }, + { + "id": "2", + "name": "Annotaion 123123" + } + ] }, { "id": "2", - "name": "File 3" + "name": "File 3", + "annotations": [] }, { "id": "3", - "name": "File 4" + "name": "File 4", + "annotations": [ + { + "id": "0", + "name": "Annotaion 1" + } + ] } ] diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index ab59c080f..9c05085b0 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -33,6 +33,7 @@ export const DatasetScreen: React.FC = observer(() => { const [inSelectMode, setInSelectMode] = useState(false); const [dataset, setDataset] = useState([] as Dataset); const [datasetProps, setDatasetProps] = useState({} as DatasetProps); + const [selectCount, setSelectCount] = useState(0); // fetch dataset useEffect(() => { @@ -55,6 +56,12 @@ export const DatasetScreen: React.FC = observer(() => { }, [dataset]); const setSelection = (id: string, selection: boolean) => { + if (datasetProps[id].isSelected !== selection) { + setSelectCount( + (prevCount) => + prevCount + (datasetProps[id].isSelected && !selection ? -1 : 1), + ); + } setDatasetProps((prevDatasetProps: DatasetProps) => ({ ...prevDatasetProps, [id]: { ...prevDatasetProps[id], isSelected: selection }, @@ -62,6 +69,7 @@ export const DatasetScreen: React.FC = observer(() => { }; const setSelectAll = (select: boolean) => { + setSelectCount(select ? dataset.length : 0); setDatasetProps((prevDatasetProps: DatasetProps) => { const newDatasetProps: DatasetProps = {}; Object.keys(prevDatasetProps).forEach((id: string) => { @@ -81,10 +89,18 @@ export const DatasetScreen: React.FC = observer(() => { headerChildren={ { + if (inSelectMode) { + setSelectAll(false); + } setInSelectMode((prev: boolean) => !prev); }} - toggleSelectAll={setSelectAll} + toggleSelectAll={() => + selectCount === dataset.length + ? setSelectAll(false) + : setSelectAll(true) + } /> } > diff --git a/libs/ui-shared/src/lib/components/icon/icons/chevron.svg b/libs/ui-shared/src/lib/components/icon/icons/chevron.svg new file mode 100644 index 000000000..7abcd5a82 --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/chevron.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index e640b6ff9..c726906c8 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -60,3 +60,4 @@ export { ReactComponent as whoAI } from "./who-ai.svg"; export { ReactComponent as xSmall } from "./x-small.svg"; export { ReactComponent as checked } from "./checkbox-checked.svg"; export { ReactComponent as unchecked } from "./checkbox-unchecked.svg"; +export { ReactComponent as chevron } from "./chevron.svg"; From ad7ecd4749875396dcc7e4fa81daa736cc9e4ee3 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Tue, 13 Dec 2022 12:04:45 +0100 Subject: [PATCH 008/500] #4 feat: toggleShowAnnotations --- .../editor/src/components/menu/data-types.tsx | 8 +- .../dataset-document-list.tsx | 10 +-- .../document-list-item.tsx | 33 +++++--- apps/editor/src/screens/dataset-moc.tsx | 8 +- apps/editor/src/screens/dataset-screen.tsx | 80 +++++++++---------- 5 files changed, 76 insertions(+), 63 deletions(-) diff --git a/apps/editor/src/components/menu/data-types.tsx b/apps/editor/src/components/menu/data-types.tsx index 2a0171da0..47449f957 100644 --- a/apps/editor/src/components/menu/data-types.tsx +++ b/apps/editor/src/components/menu/data-types.tsx @@ -1,15 +1,13 @@ export type DocumentProp = { isSelected: boolean; -}; - -export type DatasetProps = { - [id: string]: DocumentProp; + showAnnotations: boolean; }; export type DocumentItem = { id: string; name: string; - annoations: Annotation[]; + annotations: Annotation[]; + props: DocumentProp; }; export type Dataset = DocumentItem[]; diff --git a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx index 8f8097254..b9d3bbb36 100644 --- a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx @@ -1,7 +1,7 @@ import { List, stopPropagation } from "@visian/ui-shared"; import styled from "styled-components"; -import { Dataset, DatasetProps, DocumentItem } from "../data-types"; +import { Dataset, DocumentItem } from "../data-types"; import { DocumentListItem } from "./document-list-item"; const DocumentList = styled(List)` @@ -13,22 +13,22 @@ const DocumentList = styled(List)` export const DatasetDocumentList = ({ inSelectMode, dataset, - datasetProps, setSelection, + toggleShowAnnotations, }: { inSelectMode: boolean; dataset: Dataset; - datasetProps: DatasetProps; setSelection: (id: string, selction: boolean) => void; + toggleShowAnnotations: (id: string) => void; }) => { const documentList = dataset.map((documentItem: DocumentItem) => ( - setSelection(documentItem.id, !datasetProps[documentItem.id].isSelected) + setSelection(documentItem.id, !documentItem.props.isSelected) } + toggleShowAnnotations={() => toggleShowAnnotations(documentItem.id)} key={documentItem.id} /> )); diff --git a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx index 627c42fa4..595abfeab 100644 --- a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx @@ -1,8 +1,7 @@ import { List, ListItem, SquareButton } from "@visian/ui-shared"; -import { useState } from "react"; import styled from "styled-components"; -import { DocumentItem, DocumentProp } from "../data-types"; +import { DocumentItem } from "../data-types"; const Spacer = styled.div` width: 10px; @@ -17,21 +16,25 @@ const InvisibleButton = styled(SquareButton)` padding: 12px; `; +const AnnotationsListItem = styled(ListItem)` + margin-left: 30px; +`; + export const DocumentListItem = ({ inSelectMode, documentItem, - documentProp, toggleSelection, + toggleShowAnnotations, }: { inSelectMode: boolean; documentItem: DocumentItem; - documentProp: DocumentProp; toggleSelection: () => void; + toggleShowAnnotations: () => void; }) => { - const [showAnnotations, setShowAnnotations] = useState(false); - - const annotations = documentItem.annoations.map((annotation) => ( - {annotation.name} + const annotations = documentItem.annotations.map((annotation) => ( + + {annotation.name} + )); return ( @@ -40,7 +43,11 @@ export const DocumentListItem = ({ {inSelectMode && ( <> @@ -52,13 +59,15 @@ export const DocumentListItem = ({ - {annotations} + {documentItem.props.showAnnotations && {annotations}} ); }; diff --git a/apps/editor/src/screens/dataset-moc.tsx b/apps/editor/src/screens/dataset-moc.tsx index 29d89eccb..12b0a8c54 100755 --- a/apps/editor/src/screens/dataset-moc.tsx +++ b/apps/editor/src/screens/dataset-moc.tsx @@ -1,4 +1,4 @@ -import { Dataset } from "../components/menu/data-types"; +import { Dataset, DocumentItem } from "../components/menu/data-types"; const path = "https://webhook.site/faaa6131-98ba-4007-9623-e4af82e3580c"; @@ -11,6 +11,12 @@ export const getDataset = async () => { } return response.json(); }) + .then((data) => + data.map((document: DocumentItem) => ({ + ...document, + props: { isSelected: false, showAnnotations: false }, + })), + ) .then((data) => { dataset = data; }); diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 9c05085b0..2b6fa2481 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -3,11 +3,7 @@ import { observer } from "mobx-react-lite"; import React, { useEffect, useState } from "react"; import styled from "styled-components"; -import { - Dataset, - DatasetProps, - DocumentItem, -} from "../components/menu/data-types"; +import { Dataset, DocumentItem } from "../components/menu/data-types"; import { DatasetDocumentList } from "../components/menu/dataset-document-list"; import { DatasetNavbar } from "../components/menu/dataset-navbar"; import { getDataset } from "./dataset-moc"; @@ -32,7 +28,6 @@ export const DatasetScreen: React.FC = observer(() => { const [inSelectMode, setInSelectMode] = useState(false); const [dataset, setDataset] = useState([] as Dataset); - const [datasetProps, setDatasetProps] = useState({} as DatasetProps); const [selectCount, setSelectCount] = useState(0); // fetch dataset @@ -40,43 +35,48 @@ export const DatasetScreen: React.FC = observer(() => { (async () => setDataset(await getDataset()))(); }, []); - // sync datasetProps with dataset - useEffect(() => { - setDatasetProps((prevDatasetProps) => { - const newDatasetProps: DatasetProps = {}; - dataset.forEach((documentItem: DocumentItem) => { - newDatasetProps[documentItem.id] = prevDatasetProps[ - documentItem.id - ] ?? { - isSelected: false, + const setSelection = (id: string, selection: boolean) => { + setDataset((prevDataset: Dataset) => + prevDataset.map((document: DocumentItem) => { + if (document.id !== id) return document; + if (document.props.isSelected !== selection) { + setSelectCount( + (prevCount) => + prevCount + (document.props.isSelected && !selection ? -1 : 1), + ); + } + return { + ...document, + props: { ...document.props, isSelected: selection }, }; - }); - return newDatasetProps; - }); - }, [dataset]); + }), + ); + }; - const setSelection = (id: string, selection: boolean) => { - if (datasetProps[id].isSelected !== selection) { - setSelectCount( - (prevCount) => - prevCount + (datasetProps[id].isSelected && !selection ? -1 : 1), - ); - } - setDatasetProps((prevDatasetProps: DatasetProps) => ({ - ...prevDatasetProps, - [id]: { ...prevDatasetProps[id], isSelected: selection }, - })); + const setSelectAll = (selection: boolean) => { + setSelectCount(selection ? dataset.length : 0); + setDataset((prevDataset: Dataset) => + prevDataset.map((document: DocumentItem) => ({ + ...document, + props: { ...document.props, isSelected: selection }, + })), + ); }; - const setSelectAll = (select: boolean) => { - setSelectCount(select ? dataset.length : 0); - setDatasetProps((prevDatasetProps: DatasetProps) => { - const newDatasetProps: DatasetProps = {}; - Object.keys(prevDatasetProps).forEach((id: string) => { - newDatasetProps[id] = { ...prevDatasetProps[id], isSelected: select }; - }); - return newDatasetProps; - }); + const toggleShowAnnotations = (id: string) => { + setDataset((prevDataset: Dataset) => + prevDataset.map((document) => + document.id === id + ? { + ...document, + props: { + ...document.props, + showAnnotations: !document.props.showAnnotations, + }, + } + : document, + ), + ); }; return ( @@ -107,8 +107,8 @@ export const DatasetScreen: React.FC = observer(() => { From 3176d9b7288e493f674aeacf3d9b86717dc40aab Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Tue, 13 Dec 2022 12:32:15 +0100 Subject: [PATCH 009/500] #4 feat: implement delete button --- .../menu/dataset-navbar/dataset-navbar.tsx | 8 +++++++- apps/editor/src/screens/dataset-screen.tsx | 18 ++++++++++++++++-- .../{dataset-moc.tsx => hub-actions.tsx} | 6 +++++- 3 files changed, 28 insertions(+), 4 deletions(-) rename apps/editor/src/screens/{dataset-moc.tsx => hub-actions.tsx} (71%) diff --git a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx index 56bed0d70..684e1ab4c 100644 --- a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx +++ b/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx @@ -16,11 +16,13 @@ export const DatasetNavbar = ({ allSelected, toggleSelectMode, toggleSelectAll, + deleteSelectedDocuments, }: { inSelectMode: boolean; allSelected: boolean; toggleSelectMode: () => void; toggleSelectAll: () => void; + deleteSelectedDocuments: () => void; }) => inSelectMode ? ( <> @@ -29,7 +31,11 @@ export const DatasetNavbar = ({ handlePress={toggleSelectAll} /> - + { // fetch dataset useEffect(() => { - (async () => setDataset(await getDataset()))(); + (async () => setDataset(await getDatasetFormDatabase()))(); }, []); const setSelection = (id: string, selection: boolean) => { @@ -79,6 +82,16 @@ export const DatasetScreen: React.FC = observer(() => { ); }; + const deleteSelectedDocuments = () => { + setDataset((prevDataset) => + prevDataset.filter((document) => + document.props.isSelected + ? !deleteDocumentFromDatabase(document.id) + : true, + ), + ); + }; + return (
@@ -101,6 +114,7 @@ export const DatasetScreen: React.FC = observer(() => { ? setSelectAll(false) : setSelectAll(true) } + deleteSelectedDocuments={deleteSelectedDocuments} /> } > diff --git a/apps/editor/src/screens/dataset-moc.tsx b/apps/editor/src/screens/hub-actions.tsx similarity index 71% rename from apps/editor/src/screens/dataset-moc.tsx rename to apps/editor/src/screens/hub-actions.tsx index 12b0a8c54..fc2e5806b 100755 --- a/apps/editor/src/screens/dataset-moc.tsx +++ b/apps/editor/src/screens/hub-actions.tsx @@ -2,7 +2,8 @@ import { Dataset, DocumentItem } from "../components/menu/data-types"; const path = "https://webhook.site/faaa6131-98ba-4007-9623-e4af82e3580c"; -export const getDataset = async () => { +// fetches Dataset from Database +export const getDatasetFormDatabase = async () => { let dataset: Dataset = []; await fetch(path) .then((response) => { @@ -22,3 +23,6 @@ export const getDataset = async () => { }); return dataset; }; + +// returns true if Document is succsessfully deleted from Database +export const deleteDocumentFromDatabase = async (id: string) => true; From 79a88006c7fe54d0a48e5544e9f8df3ad7098d54 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Tue, 13 Dec 2022 14:04:30 +0100 Subject: [PATCH 010/500] #4 fix linter --- .../dataset-document-list/dataset-document-list.tsx | 6 +++--- .../dataset-document-list/document-list-item.tsx | 6 +++--- .../menu/dataset-navbar/dataset-navbar.tsx | 6 +++--- apps/editor/src/screens/dataset-screen.tsx | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx index b9d3bbb36..1c620f933 100644 --- a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx @@ -11,19 +11,19 @@ const DocumentList = styled(List)` `; export const DatasetDocumentList = ({ - inSelectMode, + isInSelectMode, dataset, setSelection, toggleShowAnnotations, }: { - inSelectMode: boolean; + isInSelectMode: boolean; dataset: Dataset; setSelection: (id: string, selction: boolean) => void; toggleShowAnnotations: (id: string) => void; }) => { const documentList = dataset.map((documentItem: DocumentItem) => ( setSelection(documentItem.id, !documentItem.props.isSelected) diff --git a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx index 595abfeab..dccbe40ed 100644 --- a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx @@ -21,12 +21,12 @@ const AnnotationsListItem = styled(ListItem)` `; export const DocumentListItem = ({ - inSelectMode, + isInSelectMode, documentItem, toggleSelection, toggleShowAnnotations, }: { - inSelectMode: boolean; + isInSelectMode: boolean; documentItem: DocumentItem; toggleSelection: () => void; toggleShowAnnotations: () => void; @@ -40,7 +40,7 @@ export const DocumentListItem = ({ return ( <> - {inSelectMode && ( + {isInSelectMode && ( <> void; toggleSelectAll: () => void; deleteSelectedDocuments: () => void; }) => - inSelectMode ? ( + isInSelectMode ? ( <> { - const [isDraggedOver, { onDrop, ...dragListeners }] = useIsDraggedOver(); + const [, { onDrop, ...dragListeners }] = useIsDraggedOver(); - const [inSelectMode, setInSelectMode] = useState(false); + const [isInSelectMode, setIsInSelectMode] = useState(false); const [dataset, setDataset] = useState([] as Dataset); const [selectCount, setSelectCount] = useState(0); @@ -101,13 +101,13 @@ export const DatasetScreen: React.FC = observer(() => { position="right" headerChildren={ { - if (inSelectMode) { + if (isInSelectMode) { setSelectAll(false); } - setInSelectMode((prev: boolean) => !prev); + setIsInSelectMode((prev: boolean) => !prev); }} toggleSelectAll={() => selectCount === dataset.length @@ -119,7 +119,7 @@ export const DatasetScreen: React.FC = observer(() => { } > Date: Tue, 3 Jan 2023 21:36:49 +0100 Subject: [PATCH 011/500] #4 small refactor --- apps/editor/src/assets/de.json | 10 ++- apps/editor/src/assets/en.json | 10 ++- .../dataset-document-list.tsx | 34 +++++----- .../document-list-item.tsx | 49 +++++++-------- .../menu}/dataset-moc.json | 0 .../components/menu/dataset-navbar/index.ts | 1 - .../dataset-navigationbar.tsx} | 15 +++-- .../menu/dataset-navigationbar/index.ts | 1 + .../menu}/hub-actions.tsx | 6 +- apps/editor/src/screens/dataset-screen.tsx | 63 +++++++------------ .../dataset-types.tsx} | 1 - .../src/lib/components/icon/icons/chevron.svg | 3 - .../src/lib/components/icon/icons/index.ts | 9 ++- 13 files changed, 95 insertions(+), 107 deletions(-) rename apps/editor/src/{screens => components/menu}/dataset-moc.json (100%) delete mode 100644 apps/editor/src/components/menu/dataset-navbar/index.ts rename apps/editor/src/components/menu/{dataset-navbar/dataset-navbar.tsx => dataset-navigationbar/dataset-navigationbar.tsx} (72%) create mode 100644 apps/editor/src/components/menu/dataset-navigationbar/index.ts rename apps/editor/src/{screens => components/menu}/hub-actions.tsx (73%) rename apps/editor/src/{components/menu/data-types.tsx => types/dataset-types.tsx} (90%) delete mode 100644 libs/ui-shared/src/lib/components/icon/icons/chevron.svg diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index c5caee130..2aa0334ec 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -258,5 +258,13 @@ "right-click": "Rechtsklick", "middle-click": "Mittelklick", "scroll-up": "Hoch Scrollen", - "scroll-down": "Herunter Scrollen" + "scroll-down": "Herunter Scrollen", + + "select-all": "Alles auswählen", + "deselect-all": "Auswahl aufheben", + "export-documents": "Dokumente exportieren", + "delete-documents": "Dokumente löschen", + "auto-annotate-documents": "Dokumente automatisch annotieren", + "exit-select-mode": "Auswahlmodus verlassen", + "select-mode": "Auswahlmodus" } diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 8fe51d912..4c81f16f0 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -258,5 +258,13 @@ "right-click": "Right Click", "middle-click": "Middle Click", "scroll-up": "Scroll Up", - "scroll-down": "Scroll Down" + "scroll-down": "Scroll Down", + + "select-all": "Select all", + "deselect-all": "Deselect all", + "export-documents": "Export documents", + "delete-documents": "Delete documents", + "auto-annotate-documents": "Auto-annotate documents", + "exit-select-mode": "Exit select mode", + "select-mode": "Select mode" } diff --git a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx index 1c620f933..e5dd0edca 100644 --- a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx @@ -1,7 +1,7 @@ import { List, stopPropagation } from "@visian/ui-shared"; import styled from "styled-components"; -import { Dataset, DocumentItem } from "../data-types"; +import { Dataset, DocumentItem } from "../../../types/dataset-types"; import { DocumentListItem } from "./document-list-item"; const DocumentList = styled(List)` @@ -14,25 +14,21 @@ export const DatasetDocumentList = ({ isInSelectMode, dataset, setSelection, - toggleShowAnnotations, }: { isInSelectMode: boolean; dataset: Dataset; setSelection: (id: string, selction: boolean) => void; - toggleShowAnnotations: (id: string) => void; -}) => { - const documentList = dataset.map((documentItem: DocumentItem) => ( - - setSelection(documentItem.id, !documentItem.props.isSelected) - } - toggleShowAnnotations={() => toggleShowAnnotations(documentItem.id)} - key={documentItem.id} - /> - )); - - // eslint-disable-next-line react/jsx-no-useless-fragment - return {documentList}; -}; +}) => ( + + {dataset.map((documentItem: DocumentItem) => ( + + setSelection(documentItem.id, !documentItem.props.isSelected) + } + key={documentItem.id} + /> + ))} + +); diff --git a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx index dccbe40ed..83cb0ca38 100644 --- a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx @@ -1,7 +1,8 @@ -import { List, ListItem, SquareButton } from "@visian/ui-shared"; +import { InvisibleButton, List, ListItem, Text } from "@visian/ui-shared"; +import { useCallback, useState } from "react"; import styled from "styled-components"; -import { DocumentItem } from "../data-types"; +import { DocumentItem } from "../../../types/dataset-types"; const Spacer = styled.div` width: 10px; @@ -11,63 +12,61 @@ const ExpandedSpacer = styled.div` margin-right: auto; `; -const InvisibleButton = styled(SquareButton)` - border: none; - padding: 12px; +const IconButton = styled(InvisibleButton)` + width: 30px; `; -const AnnotationsListItem = styled(ListItem)` +const AnnotationsList = styled(List)` margin-left: 30px; + width: calc(100% - 30px); `; export const DocumentListItem = ({ isInSelectMode, documentItem, toggleSelection, - toggleShowAnnotations, }: { isInSelectMode: boolean; documentItem: DocumentItem; toggleSelection: () => void; - toggleShowAnnotations: () => void; }) => { - const annotations = documentItem.annotations.map((annotation) => ( - - {annotation.name} - - )); + const [showAnnotations, setShowAnnotations] = useState(false); + + const toggleShowAnnotations = useCallback( + () => setShowAnnotations((prevShowAnnotations) => !prevShowAnnotations), + [], + ); return ( <> {isInSelectMode && ( <> - )} -

{documentItem.name}

+ {documentItem.name} -
- {documentItem.props.showAnnotations && {annotations}} + {showAnnotations && ( + + {documentItem.annotations.map((annotation) => ( + {annotation.name} + ))} + + )} ); }; diff --git a/apps/editor/src/screens/dataset-moc.json b/apps/editor/src/components/menu/dataset-moc.json similarity index 100% rename from apps/editor/src/screens/dataset-moc.json rename to apps/editor/src/components/menu/dataset-moc.json diff --git a/apps/editor/src/components/menu/dataset-navbar/index.ts b/apps/editor/src/components/menu/dataset-navbar/index.ts deleted file mode 100644 index 81b0108f8..000000000 --- a/apps/editor/src/components/menu/dataset-navbar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./dataset-navbar"; diff --git a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx similarity index 72% rename from apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx rename to apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx index 22a082ea8..4750593d5 100644 --- a/apps/editor/src/components/menu/dataset-navbar/dataset-navbar.tsx +++ b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx @@ -10,8 +10,7 @@ const StyledTextButton = styled(ButtonParam)` width: auto; `; -// eslint-disable-next-line react/destructuring-assignment -export const DatasetNavbar = ({ +export const DatasetNavigationbar = ({ isInSelectMode, allSelected, toggleSelectMode, @@ -27,26 +26,26 @@ export const DatasetNavbar = ({ isInSelectMode ? ( <> - + - + ) : ( ); diff --git a/apps/editor/src/components/menu/dataset-navigationbar/index.ts b/apps/editor/src/components/menu/dataset-navigationbar/index.ts new file mode 100644 index 000000000..653a01828 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-navigationbar/index.ts @@ -0,0 +1 @@ +export * from "./dataset-navigationbar"; diff --git a/apps/editor/src/screens/hub-actions.tsx b/apps/editor/src/components/menu/hub-actions.tsx similarity index 73% rename from apps/editor/src/screens/hub-actions.tsx rename to apps/editor/src/components/menu/hub-actions.tsx index fc2e5806b..1a0c85a03 100755 --- a/apps/editor/src/screens/hub-actions.tsx +++ b/apps/editor/src/components/menu/hub-actions.tsx @@ -1,6 +1,6 @@ -import { Dataset, DocumentItem } from "../components/menu/data-types"; +import { Dataset, DocumentItem } from "../../types/dataset-types"; -const path = "https://webhook.site/faaa6131-98ba-4007-9623-e4af82e3580c"; +const path = "https://webhook.site/b9c9c6a8-7804-4477-869e-dc6cec7ba8ba"; // fetches Dataset from Database export const getDatasetFormDatabase = async () => { @@ -15,7 +15,7 @@ export const getDatasetFormDatabase = async () => { .then((data) => data.map((document: DocumentItem) => ({ ...document, - props: { isSelected: false, showAnnotations: false }, + props: { isSelected: false }, })), ) .then((data) => { diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 030a8f640..abf15600c 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,24 +1,21 @@ import { Box, Modal, Screen, useIsDraggedOver } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import styled from "styled-components"; -import { Dataset, DocumentItem } from "../components/menu/data-types"; import { DatasetDocumentList } from "../components/menu/dataset-document-list"; -import { DatasetNavbar } from "../components/menu/dataset-navbar"; +import { DatasetNavigationbar } from "../components/menu/dataset-navigationbar"; import { deleteDocumentFromDatabase, getDatasetFormDatabase, -} from "./hub-actions"; +} from "../components/menu/hub-actions"; +import { Dataset, DocumentItem } from "../types/dataset-types"; const Main = styled(Box)` display: flex; justify-content: center; height: 100%; - padding-top: 5rem; - padding-bottom: 5rem; - padding-left: 10rem; - padding-right: 10rem; + padding: 5rem 10rem; `; const DatasetModal = styled(Modal)` @@ -38,7 +35,7 @@ export const DatasetScreen: React.FC = observer(() => { (async () => setDataset(await getDatasetFormDatabase()))(); }, []); - const setSelection = (id: string, selection: boolean) => { + const setSelection = useCallback((id: string, selection: boolean) => { setDataset((prevDataset: Dataset) => prevDataset.map((document: DocumentItem) => { if (document.id !== id) return document; @@ -54,35 +51,22 @@ export const DatasetScreen: React.FC = observer(() => { }; }), ); - }; - - const setSelectAll = (selection: boolean) => { - setSelectCount(selection ? dataset.length : 0); - setDataset((prevDataset: Dataset) => - prevDataset.map((document: DocumentItem) => ({ - ...document, - props: { ...document.props, isSelected: selection }, - })), - ); - }; + }, []); - const toggleShowAnnotations = (id: string) => { - setDataset((prevDataset: Dataset) => - prevDataset.map((document) => - document.id === id - ? { - ...document, - props: { - ...document.props, - showAnnotations: !document.props.showAnnotations, - }, - } - : document, - ), - ); - }; + const setSelectAll = useCallback( + (selection: boolean) => { + setSelectCount(selection ? dataset.length : 0); + setDataset((prevDataset: Dataset) => + prevDataset.map((document: DocumentItem) => ({ + ...document, + props: { ...document.props, isSelected: selection }, + })), + ); + }, + [dataset], + ); - const deleteSelectedDocuments = () => { + const deleteSelectedDocuments = useCallback(() => { setDataset((prevDataset) => prevDataset.filter((document) => document.props.isSelected @@ -90,17 +74,17 @@ export const DatasetScreen: React.FC = observer(() => { : true, ), ); - }; + }, []); return (
{ @@ -122,7 +106,6 @@ export const DatasetScreen: React.FC = observer(() => { isInSelectMode={isInSelectMode} dataset={dataset} setSelection={setSelection} - toggleShowAnnotations={toggleShowAnnotations} />
diff --git a/apps/editor/src/components/menu/data-types.tsx b/apps/editor/src/types/dataset-types.tsx similarity index 90% rename from apps/editor/src/components/menu/data-types.tsx rename to apps/editor/src/types/dataset-types.tsx index 47449f957..7f7c2414c 100644 --- a/apps/editor/src/components/menu/data-types.tsx +++ b/apps/editor/src/types/dataset-types.tsx @@ -1,6 +1,5 @@ export type DocumentProp = { isSelected: boolean; - showAnnotations: boolean; }; export type DocumentItem = { diff --git a/libs/ui-shared/src/lib/components/icon/icons/chevron.svg b/libs/ui-shared/src/lib/components/icon/icons/chevron.svg deleted file mode 100644 index 7abcd5a82..000000000 --- a/libs/ui-shared/src/lib/components/icon/icons/chevron.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index c726906c8..e74ce4f79 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -5,10 +5,9 @@ export { ReactComponent as arrowUp } from "./arrow-up.svg"; export { ReactComponent as backspace } from "./backspace.svg"; export { ReactComponent as boundedSmartBrush } from "./bounded-smart-brush.svg"; export { ReactComponent as boundedSmartEraser } from "./bounded-smart-eraser.svg"; -export { ReactComponent as exit } from "./exit.svg"; export { ReactComponent as check } from "./check.svg"; +export { ReactComponent as checked } from "./checkbox-checked.svg"; export { ReactComponent as circle } from "./circle.svg"; -export { ReactComponent as select } from "./select.svg"; export { ReactComponent as clearScan } from "./clear-scan.svg"; export { ReactComponent as clearSlice } from "./clear-slice.svg"; export { ReactComponent as columnView } from "./column-view.svg"; @@ -18,6 +17,7 @@ export { ReactComponent as crosshair } from "./crosshair.svg"; export { ReactComponent as document } from "./document.svg"; export { ReactComponent as downArrow } from "./down-arrow.svg"; export { ReactComponent as eraser } from "./eraser.svg"; +export { ReactComponent as exit } from "./exit.svg"; export { ReactComponent as export } from "./export.svg"; export { ReactComponent as eyeCrossed } from "./eye-crossed.svg"; export { ReactComponent as eye } from "./eye.svg"; @@ -48,16 +48,15 @@ export { ReactComponent as rightMouse } from "./right-mouse.svg"; export { ReactComponent as ruler } from "./ruler.svg"; export { ReactComponent as scrollDown } from "./scroll-down-mouse.svg"; export { ReactComponent as scrollUp } from "./scroll-up-mouse.svg"; +export { ReactComponent as select } from "./select.svg"; export { ReactComponent as settings } from "./settings.svg"; export { ReactComponent as smartBrush3D } from "./smart-brush-3d.svg"; export { ReactComponent as smartEraser } from "./smart-eraser.svg"; export { ReactComponent as terminateConnection } from "./terminate-connection.svg"; export { ReactComponent as threshold } from "./threshold.svg"; export { ReactComponent as trash } from "./trash.svg"; +export { ReactComponent as unchecked } from "./checkbox-unchecked.svg"; export { ReactComponent as undo } from "./undo.svg"; export { ReactComponent as upArrow } from "./up-arrow.svg"; export { ReactComponent as whoAI } from "./who-ai.svg"; export { ReactComponent as xSmall } from "./x-small.svg"; -export { ReactComponent as checked } from "./checkbox-checked.svg"; -export { ReactComponent as unchecked } from "./checkbox-unchecked.svg"; -export { ReactComponent as chevron } from "./chevron.svg"; From 581e52f998c964de7f68b22bbafc4623128c1632 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Wed, 4 Jan 2023 12:37:32 +0100 Subject: [PATCH 012/500] #4 refactor dataset componente auslagern --- .../dataset-document-list.tsx | 17 ++- .../document-list-item.tsx | 12 +- .../menu/dataset-modal/dataset-modal.tsx | 120 ++++++++++++++++++ .../components/menu/dataset-modal/index.ts | 1 + apps/editor/src/screens/dataset-screen.tsx | 96 +++----------- apps/editor/src/types/dataset-types.tsx | 6 +- apps/editor/src/types/index.ts | 1 + 7 files changed, 159 insertions(+), 94 deletions(-) create mode 100644 apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx create mode 100644 apps/editor/src/components/menu/dataset-modal/index.ts create mode 100644 apps/editor/src/types/index.ts diff --git a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx index e5dd0edca..e1a68d055 100644 --- a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx @@ -1,7 +1,7 @@ import { List, stopPropagation } from "@visian/ui-shared"; import styled from "styled-components"; -import { Dataset, DocumentItem } from "../../../types/dataset-types"; +import { DocumentWithProps } from "../../../types"; import { DocumentListItem } from "./document-list-item"; const DocumentList = styled(List)` @@ -12,22 +12,25 @@ const DocumentList = styled(List)` export const DatasetDocumentList = ({ isInSelectMode, - dataset, + datasetWithProps, setSelection, }: { isInSelectMode: boolean; - dataset: Dataset; + datasetWithProps: DocumentWithProps[]; setSelection: (id: string, selction: boolean) => void; }) => ( - {dataset.map((documentItem: DocumentItem) => ( + {datasetWithProps.map((documentWithProps: DocumentWithProps) => ( - setSelection(documentItem.id, !documentItem.props.isSelected) + setSelection( + documentWithProps.documentItem.id, + !documentWithProps.props.isSelected, + ) } - key={documentItem.id} + key={documentWithProps.documentItem.id} /> ))} diff --git a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx index 83cb0ca38..f767e75a9 100644 --- a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx @@ -2,7 +2,7 @@ import { InvisibleButton, List, ListItem, Text } from "@visian/ui-shared"; import { useCallback, useState } from "react"; import styled from "styled-components"; -import { DocumentItem } from "../../../types/dataset-types"; +import { DocumentWithProps } from "../../../types"; const Spacer = styled.div` width: 10px; @@ -23,11 +23,11 @@ const AnnotationsList = styled(List)` export const DocumentListItem = ({ isInSelectMode, - documentItem, + documentWithProps, toggleSelection, }: { isInSelectMode: boolean; - documentItem: DocumentItem; + documentWithProps: DocumentWithProps; toggleSelection: () => void; }) => { const [showAnnotations, setShowAnnotations] = useState(false); @@ -44,7 +44,7 @@ export const DocumentListItem = ({ <> )} - {documentItem.name} + {documentWithProps.documentItem.name} {showAnnotations && ( - {documentItem.annotations.map((annotation) => ( + {documentWithProps.documentItem.annotations.map((annotation) => ( {annotation.name} ))} diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx new file mode 100644 index 000000000..50175090a --- /dev/null +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -0,0 +1,120 @@ +import { Modal } from "@visian/ui-shared"; +import { useCallback, useEffect, useState } from "react"; +import styled from "styled-components"; + +import { Dataset, DocumentWithProps } from "../../../types"; +import { DatasetDocumentList } from "../dataset-document-list"; +import { DatasetNavigationbar } from "../dataset-navigationbar"; + +const StyledModal = styled(Modal)` + vertical-align: middle; + width: 100%; +`; + +export const DatasetModal = ({ + dataset, + deleteDocuments, +}: { + dataset: Dataset; + deleteDocuments: (ids: string[]) => void; +}) => { + const [isInSelectMode, setIsInSelectMode] = useState(false); + const [datasetWithProps, setDatasetWithProps] = useState( + dataset.map((document) => ({ + documentItem: document, + props: { isSelected: false }, + })), + ); + const [selectCount, setSelectCount] = useState(0); + + // sync dataset with datasetProps + useEffect(() => { + setDatasetWithProps( + dataset.map((document) => ({ + documentItem: document, + props: { isSelected: false }, + })), + ); + }, [dataset]); + + const setSelection = useCallback((id: string, selection: boolean) => { + setDatasetWithProps((prevDatasetWithProps: DocumentWithProps[]) => + prevDatasetWithProps.map((documentWithProps: DocumentWithProps) => { + if (documentWithProps.documentItem.id !== id) return documentWithProps; + if (documentWithProps.props.isSelected !== selection) { + setSelectCount( + (prevCount) => + prevCount + + (documentWithProps.props.isSelected && !selection ? -1 : 1), + ); + } + return { + ...documentWithProps, + props: { ...documentWithProps.props, isSelected: selection }, + }; + }), + ); + }, []); + + const setSelectAll = useCallback( + (selection: boolean) => { + setSelectCount(selection ? datasetWithProps.length : 0); + setDatasetWithProps((prevDatasetWithProps: DocumentWithProps[]) => + prevDatasetWithProps.map((documentWithProps: DocumentWithProps) => ({ + ...documentWithProps, + props: { ...documentWithProps.props, isSelected: selection }, + })), + ); + }, + [datasetWithProps], + ); + + const deleteSelectedDocuments = useCallback(() => { + const selectedIds = datasetWithProps + .filter( + (documentWithProps: DocumentWithProps) => + documentWithProps.props.isSelected, + ) + .map( + (documentWithProps: DocumentWithProps) => + documentWithProps.documentItem.id, + ); + deleteDocuments(selectedIds); + }, [deleteDocuments, datasetWithProps]); + + useEffect(() => { + console.log(selectCount); + }); + + return ( + { + if (isInSelectMode) { + setSelectAll(false); + } + setIsInSelectMode((prev: boolean) => !prev); + }} + toggleSelectAll={() => + selectCount === datasetWithProps.length + ? setSelectAll(false) + : setSelectAll(true) + } + deleteSelectedDocuments={deleteSelectedDocuments} + /> + } + > + + + ); +}; diff --git a/apps/editor/src/components/menu/dataset-modal/index.ts b/apps/editor/src/components/menu/dataset-modal/index.ts new file mode 100644 index 000000000..c187da890 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-modal/index.ts @@ -0,0 +1 @@ +export * from "./dataset-modal"; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index abf15600c..09762fac2 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,15 +1,11 @@ -import { Box, Modal, Screen, useIsDraggedOver } from "@visian/ui-shared"; +import { Box, Screen, useIsDraggedOver } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React, { useCallback, useEffect, useState } from "react"; import styled from "styled-components"; -import { DatasetDocumentList } from "../components/menu/dataset-document-list"; -import { DatasetNavigationbar } from "../components/menu/dataset-navigationbar"; -import { - deleteDocumentFromDatabase, - getDatasetFormDatabase, -} from "../components/menu/hub-actions"; -import { Dataset, DocumentItem } from "../types/dataset-types"; +import { DatasetModal } from "../components/menu/dataset-modal"; +import { getDatasetFormDatabase } from "../components/menu/hub-actions"; +import { Dataset } from "../types"; const Main = styled(Box)` display: flex; @@ -18,96 +14,36 @@ const Main = styled(Box)` padding: 5rem 10rem; `; -const DatasetModal = styled(Modal)` - vertical-align: middle; - width: 100%; -`; - export const DatasetScreen: React.FC = observer(() => { const [, { onDrop, ...dragListeners }] = useIsDraggedOver(); - const [isInSelectMode, setIsInSelectMode] = useState(false); const [dataset, setDataset] = useState([] as Dataset); - const [selectCount, setSelectCount] = useState(0); // fetch dataset useEffect(() => { (async () => setDataset(await getDatasetFormDatabase()))(); }, []); - const setSelection = useCallback((id: string, selection: boolean) => { - setDataset((prevDataset: Dataset) => - prevDataset.map((document: DocumentItem) => { - if (document.id !== id) return document; - if (document.props.isSelected !== selection) { - setSelectCount( - (prevCount) => - prevCount + (document.props.isSelected && !selection ? -1 : 1), - ); - } - return { - ...document, - props: { ...document.props, isSelected: selection }, - }; - }), + const deleteDocuments = (ids: string[]) => { + setDataset((prevDataset) => + prevDataset.filter((document) => !ids.includes(document.id)), ); - }, []); - - const setSelectAll = useCallback( - (selection: boolean) => { - setSelectCount(selection ? dataset.length : 0); - setDataset((prevDataset: Dataset) => - prevDataset.map((document: DocumentItem) => ({ - ...document, - props: { ...document.props, isSelected: selection }, - })), - ); - }, - [dataset], - ); + }; const deleteSelectedDocuments = useCallback(() => { - setDataset((prevDataset) => - prevDataset.filter((document) => - document.props.isSelected - ? !deleteDocumentFromDatabase(document.id) - : true, - ), - ); + // setDataset((prevDataset) => + // prevDataset.filter((document) => + // document.props.isSelected + // ? !deleteDocumentFromDatabase(document.id) + // : true, + // ), + // ); }, []); return (
- { - if (isInSelectMode) { - setSelectAll(false); - } - setIsInSelectMode((prev: boolean) => !prev); - }} - toggleSelectAll={() => - selectCount === dataset.length - ? setSelectAll(false) - : setSelectAll(true) - } - deleteSelectedDocuments={deleteSelectedDocuments} - /> - } - > - - +
); diff --git a/apps/editor/src/types/dataset-types.tsx b/apps/editor/src/types/dataset-types.tsx index 7f7c2414c..62ccc3f65 100644 --- a/apps/editor/src/types/dataset-types.tsx +++ b/apps/editor/src/types/dataset-types.tsx @@ -6,7 +6,6 @@ export type DocumentItem = { id: string; name: string; annotations: Annotation[]; - props: DocumentProp; }; export type Dataset = DocumentItem[]; @@ -15,3 +14,8 @@ export type Annotation = { id: string; name: string; }; + +export type DocumentWithProps = { + documentItem: DocumentItem; + props: DocumentProp; +}; diff --git a/apps/editor/src/types/index.ts b/apps/editor/src/types/index.ts new file mode 100644 index 000000000..770f7f420 --- /dev/null +++ b/apps/editor/src/types/index.ts @@ -0,0 +1 @@ +export * from "./dataset-types"; From 6852bedc71604afb32bd2f56fc75ab0335d6cd5d Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Wed, 4 Jan 2023 14:32:30 +0100 Subject: [PATCH 013/500] #4 fix syncronisation of selectCount --- .../document-list-item.tsx | 15 +++++--- .../menu/dataset-modal/dataset-modal.tsx | 38 +++++++++++-------- .../src/components/menu/hub-actions.tsx | 2 +- apps/editor/src/screens/dataset-screen.tsx | 26 ++++++------- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx index f767e75a9..05007f779 100644 --- a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx @@ -2,7 +2,7 @@ import { InvisibleButton, List, ListItem, Text } from "@visian/ui-shared"; import { useCallback, useState } from "react"; import styled from "styled-components"; -import { DocumentWithProps } from "../../../types"; +import { Annotation, DocumentWithProps } from "../../../types"; const Spacer = styled.div` width: 10px; @@ -33,7 +33,10 @@ export const DocumentListItem = ({ const [showAnnotations, setShowAnnotations] = useState(false); const toggleShowAnnotations = useCallback( - () => setShowAnnotations((prevShowAnnotations) => !prevShowAnnotations), + () => + setShowAnnotations( + (prevShowAnnotations: boolean) => !prevShowAnnotations, + ), [], ); @@ -62,9 +65,11 @@ export const DocumentListItem = ({
{showAnnotations && ( - {documentWithProps.documentItem.annotations.map((annotation) => ( - {annotation.name} - ))} + {documentWithProps.documentItem.annotations.map( + (annotation: Annotation) => ( + {annotation.name} + ), + )} )} diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 50175090a..a15a0b6b6 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -2,7 +2,7 @@ import { Modal } from "@visian/ui-shared"; import { useCallback, useEffect, useState } from "react"; import styled from "styled-components"; -import { Dataset, DocumentWithProps } from "../../../types"; +import { Dataset, DocumentItem, DocumentWithProps } from "../../../types"; import { DatasetDocumentList } from "../dataset-document-list"; import { DatasetNavigationbar } from "../dataset-navigationbar"; @@ -20,33 +20,42 @@ export const DatasetModal = ({ }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); const [datasetWithProps, setDatasetWithProps] = useState( - dataset.map((document) => ({ + dataset.map((document: DocumentItem) => ({ documentItem: document, props: { isSelected: false }, })), ); const [selectCount, setSelectCount] = useState(0); - // sync dataset with datasetProps + // sync dataset with datasetProps and update selectCount useEffect(() => { - setDatasetWithProps( - dataset.map((document) => ({ + let newDatasetWithProps: DocumentWithProps[] = []; + let prevDatasetWithProps: DocumentWithProps[] = []; + setDatasetWithProps((prev: DocumentWithProps[]) => { + prevDatasetWithProps = prev; + newDatasetWithProps = dataset.map((document: DocumentItem) => ({ documentItem: document, - props: { isSelected: false }, - })), + props: prevDatasetWithProps.find( + (prevDocument: DocumentWithProps) => + prevDocument.documentItem.id === document.id, + )?.props ?? { isSelected: false }, + })); + return newDatasetWithProps; + }); + setSelectCount( + newDatasetWithProps.filter( + (document: DocumentWithProps) => document.props.isSelected, + ).length, ); }, [dataset]); const setSelection = useCallback((id: string, selection: boolean) => { + let countDiff = 0; setDatasetWithProps((prevDatasetWithProps: DocumentWithProps[]) => prevDatasetWithProps.map((documentWithProps: DocumentWithProps) => { if (documentWithProps.documentItem.id !== id) return documentWithProps; if (documentWithProps.props.isSelected !== selection) { - setSelectCount( - (prevCount) => - prevCount + - (documentWithProps.props.isSelected && !selection ? -1 : 1), - ); + countDiff = documentWithProps.props.isSelected && !selection ? -1 : 1; } return { ...documentWithProps, @@ -54,6 +63,7 @@ export const DatasetModal = ({ }; }), ); + setSelectCount((prevCount: number) => prevCount + countDiff); }, []); const setSelectAll = useCallback( @@ -82,10 +92,6 @@ export const DatasetModal = ({ deleteDocuments(selectedIds); }, [deleteDocuments, datasetWithProps]); - useEffect(() => { - console.log(selectCount); - }); - return ( { }; // returns true if Document is succsessfully deleted from Database -export const deleteDocumentFromDatabase = async (id: string) => true; +export const deleteDocumentFromDatabase = (id: string) => true; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 09762fac2..2f8f9f088 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -4,8 +4,11 @@ import React, { useCallback, useEffect, useState } from "react"; import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; -import { getDatasetFormDatabase } from "../components/menu/hub-actions"; -import { Dataset } from "../types"; +import { + deleteDocumentFromDatabase, + getDatasetFormDatabase, +} from "../components/menu/hub-actions"; +import { Dataset, DocumentItem } from "../types"; const Main = styled(Box)` display: flex; @@ -24,20 +27,13 @@ export const DatasetScreen: React.FC = observer(() => { (async () => setDataset(await getDatasetFormDatabase()))(); }, []); - const deleteDocuments = (ids: string[]) => { - setDataset((prevDataset) => - prevDataset.filter((document) => !ids.includes(document.id)), + const deleteDocuments = useCallback((ids: string[]) => { + const deletedIds = ids.filter((id) => deleteDocumentFromDatabase(id)); + setDataset((prevDataset: Dataset) => + prevDataset.filter( + (document: DocumentItem) => !deletedIds.includes(document.id), + ), ); - }; - - const deleteSelectedDocuments = useCallback(() => { - // setDataset((prevDataset) => - // prevDataset.filter((document) => - // document.props.isSelected - // ? !deleteDocumentFromDatabase(document.id) - // : true, - // ), - // ); }, []); return ( From eff5476a6c41ec99dfc66467a722abde94aa102f Mon Sep 17 00:00:00 2001 From: Jonas Kordt Date: Thu, 5 Jan 2023 12:42:19 +0100 Subject: [PATCH 014/500] Use useMemo --- .../menu/dataset-modal/dataset-modal.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index a15a0b6b6..47f28f579 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -1,5 +1,5 @@ import { Modal } from "@visian/ui-shared"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; import { Dataset, DocumentItem, DocumentWithProps } from "../../../types"; @@ -92,6 +92,14 @@ export const DatasetModal = ({ deleteDocuments(selectedIds); }, [deleteDocuments, datasetWithProps]); + const areAllSelected = useMemo( + () => + datasetWithProps.filter( + (document: DocumentWithProps) => document.props.isSelected, + ).length === datasetWithProps.length, + [datasetWithProps], + ); + return ( { if (isInSelectMode) { setSelectAll(false); } setIsInSelectMode((prev: boolean) => !prev); }} + // TODO: Use a callback toggleSelectAll={() => - selectCount === datasetWithProps.length - ? setSelectAll(false) - : setSelectAll(true) + areAllSelected ? setSelectAll(false) : setSelectAll(true) } deleteSelectedDocuments={deleteSelectedDocuments} /> From 9bf1e5b15928c55cc089a3f9457208a715b5a1b5 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Thu, 5 Jan 2023 14:43:34 +0100 Subject: [PATCH 015/500] remove selectCount --- .../menu/dataset-modal/dataset-modal.tsx | 40 ++++++------------- apps/editor/src/screens/dataset-screen.tsx | 4 +- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 47f28f579..8611b80b7 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -19,33 +19,24 @@ export const DatasetModal = ({ deleteDocuments: (ids: string[]) => void; }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); + // TODO: use a map for props const [datasetWithProps, setDatasetWithProps] = useState( dataset.map((document: DocumentItem) => ({ documentItem: document, props: { isSelected: false }, })), ); - const [selectCount, setSelectCount] = useState(0); // sync dataset with datasetProps and update selectCount useEffect(() => { - let newDatasetWithProps: DocumentWithProps[] = []; - let prevDatasetWithProps: DocumentWithProps[] = []; - setDatasetWithProps((prev: DocumentWithProps[]) => { - prevDatasetWithProps = prev; - newDatasetWithProps = dataset.map((document: DocumentItem) => ({ + setDatasetWithProps((prev: DocumentWithProps[]) => + dataset.map((document: DocumentItem) => ({ documentItem: document, - props: prevDatasetWithProps.find( + props: prev.find( (prevDocument: DocumentWithProps) => prevDocument.documentItem.id === document.id, )?.props ?? { isSelected: false }, - })); - return newDatasetWithProps; - }); - setSelectCount( - newDatasetWithProps.filter( - (document: DocumentWithProps) => document.props.isSelected, - ).length, + })), ); }, [dataset]); @@ -63,21 +54,16 @@ export const DatasetModal = ({ }; }), ); - setSelectCount((prevCount: number) => prevCount + countDiff); }, []); - const setSelectAll = useCallback( - (selection: boolean) => { - setSelectCount(selection ? datasetWithProps.length : 0); - setDatasetWithProps((prevDatasetWithProps: DocumentWithProps[]) => - prevDatasetWithProps.map((documentWithProps: DocumentWithProps) => ({ - ...documentWithProps, - props: { ...documentWithProps.props, isSelected: selection }, - })), - ); - }, - [datasetWithProps], - ); + const setSelectAll = useCallback((selection: boolean) => { + setDatasetWithProps((prevDatasetWithProps: DocumentWithProps[]) => + prevDatasetWithProps.map((documentWithProps: DocumentWithProps) => ({ + ...documentWithProps, + props: { ...documentWithProps.props, isSelected: selection }, + })), + ); + }, []); const deleteSelectedDocuments = useCallback(() => { const selectedIds = datasetWithProps diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 2f8f9f088..70bb45a54 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -18,8 +18,6 @@ const Main = styled(Box)` `; export const DatasetScreen: React.FC = observer(() => { - const [, { onDrop, ...dragListeners }] = useIsDraggedOver(); - const [dataset, setDataset] = useState([] as Dataset); // fetch dataset @@ -37,7 +35,7 @@ export const DatasetScreen: React.FC = observer(() => { }, []); return ( - +
From 347c9a0819731cc6ee11d9f21aa0c5922f6f7630 Mon Sep 17 00:00:00 2001 From: Richard Keil <8680858+richartkeil@users.noreply.github.com> Date: Thu, 5 Jan 2023 16:44:28 +0100 Subject: [PATCH 016/500] feat: use hub backend as data source --- .../dataset-document-list.tsx | 37 ------ .../menu/dataset-document-list/index.ts | 1 - .../dataset-image-list-item.tsx} | 31 ++--- .../dataset-image-list/dataset-image-list.tsx | 37 ++++++ .../menu/dataset-image-list/index.ts | 1 + .../src/components/menu/dataset-moc.json | 49 -------- .../menu/dataset-modal/dataset-modal.tsx | 117 ++++++++---------- .../src/components/menu/hub-actions.tsx | 38 +++--- apps/editor/src/screens/dataset-screen.tsx | 33 ++--- apps/editor/src/types/dataset-types.tsx | 19 ++- 10 files changed, 145 insertions(+), 218 deletions(-) delete mode 100644 apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx delete mode 100644 apps/editor/src/components/menu/dataset-document-list/index.ts rename apps/editor/src/components/menu/{dataset-document-list/document-list-item.tsx => dataset-image-list/dataset-image-list-item.tsx} (62%) create mode 100644 apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx create mode 100644 apps/editor/src/components/menu/dataset-image-list/index.ts delete mode 100644 apps/editor/src/components/menu/dataset-moc.json diff --git a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx b/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx deleted file mode 100644 index e1a68d055..000000000 --- a/apps/editor/src/components/menu/dataset-document-list/dataset-document-list.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { List, stopPropagation } from "@visian/ui-shared"; -import styled from "styled-components"; - -import { DocumentWithProps } from "../../../types"; -import { DocumentListItem } from "./document-list-item"; - -const DocumentList = styled(List)` - width: 100%; - height: 400px; - overflow-y: auto; -`; - -export const DatasetDocumentList = ({ - isInSelectMode, - datasetWithProps, - setSelection, -}: { - isInSelectMode: boolean; - datasetWithProps: DocumentWithProps[]; - setSelection: (id: string, selction: boolean) => void; -}) => ( - - {datasetWithProps.map((documentWithProps: DocumentWithProps) => ( - - setSelection( - documentWithProps.documentItem.id, - !documentWithProps.props.isSelected, - ) - } - key={documentWithProps.documentItem.id} - /> - ))} - -); diff --git a/apps/editor/src/components/menu/dataset-document-list/index.ts b/apps/editor/src/components/menu/dataset-document-list/index.ts deleted file mode 100644 index 29a49f5e7..000000000 --- a/apps/editor/src/components/menu/dataset-document-list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./dataset-document-list"; diff --git a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx similarity index 62% rename from apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx rename to apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index 05007f779..f35e3c6d1 100644 --- a/apps/editor/src/components/menu/dataset-document-list/document-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -2,7 +2,7 @@ import { InvisibleButton, List, ListItem, Text } from "@visian/ui-shared"; import { useCallback, useState } from "react"; import styled from "styled-components"; -import { Annotation, DocumentWithProps } from "../../../types"; +import { Annotation, Image } from "../../../types"; const Spacer = styled.div` width: 10px; @@ -21,22 +21,21 @@ const AnnotationsList = styled(List)` width: calc(100% - 30px); `; -export const DocumentListItem = ({ +export const DatasetImageListItem = ({ isInSelectMode, - documentWithProps, + image, + isSelected, toggleSelection, }: { isInSelectMode: boolean; - documentWithProps: DocumentWithProps; + image: Image; + isSelected: boolean; toggleSelection: () => void; }) => { const [showAnnotations, setShowAnnotations] = useState(false); const toggleShowAnnotations = useCallback( - () => - setShowAnnotations( - (prevShowAnnotations: boolean) => !prevShowAnnotations, - ), + () => setShowAnnotations((prev: boolean) => !prev), [], ); @@ -46,17 +45,13 @@ export const DocumentListItem = ({ {isInSelectMode && ( <> )} - {documentWithProps.documentItem.name} + {image.dataUri} {showAnnotations && ( - {documentWithProps.documentItem.annotations.map( - (annotation: Annotation) => ( - {annotation.name} - ), - )} + {image.annotations.map((annotation: Annotation) => ( + {annotation.dataUri} + ))} )} diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx new file mode 100644 index 000000000..d4cced917 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx @@ -0,0 +1,37 @@ +import { List, stopPropagation } from "@visian/ui-shared"; +import styled from "styled-components"; + +import { Dataset, Image } from "../../../types"; +import { DatasetImageListItem } from "./dataset-image-list-item"; + +const DocumentList = styled(List)` + width: 100%; + height: 400px; + overflow-y: auto; +`; + +export const DatasetImageList = ({ + isInSelectMode, + dataset, + selectedImages, + setSelection, +}: { + isInSelectMode: boolean; + dataset: Dataset; + selectedImages: Map; + setSelection: (id: string, selection: boolean) => void; +}) => ( + + {dataset.images.map((image: Image) => ( + + setSelection(image.id, !selectedImages.get(image.id)) + } + key={image.id} + /> + ))} + +); diff --git a/apps/editor/src/components/menu/dataset-image-list/index.ts b/apps/editor/src/components/menu/dataset-image-list/index.ts new file mode 100644 index 000000000..d21f7178e --- /dev/null +++ b/apps/editor/src/components/menu/dataset-image-list/index.ts @@ -0,0 +1 @@ +export * from "./dataset-image-list"; diff --git a/apps/editor/src/components/menu/dataset-moc.json b/apps/editor/src/components/menu/dataset-moc.json deleted file mode 100644 index e7bf68f87..000000000 --- a/apps/editor/src/components/menu/dataset-moc.json +++ /dev/null @@ -1,49 +0,0 @@ -[ - { - "id": "0", - "name": "File 1", - "annotations": [ - { - "id": "0", - "name": "Annotaion 1" - }, - { - "id": "1", - "name": "Annotaion 2" - } - ] - }, - { - "id": "1", - "name": "File 2", - "annotations": [ - { - "id": "0", - "name": "Annotaion 1" - }, - { - "id": "1", - "name": "Annotaion 2" - }, - { - "id": "2", - "name": "Annotaion 123123" - } - ] - }, - { - "id": "2", - "name": "File 3", - "annotations": [] - }, - { - "id": "3", - "name": "File 4", - "annotations": [ - { - "id": "0", - "name": "Annotaion 1" - } - ] - } -] diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 8611b80b7..ea6931772 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -2,8 +2,8 @@ import { Modal } from "@visian/ui-shared"; import { useCallback, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; -import { Dataset, DocumentItem, DocumentWithProps } from "../../../types"; -import { DatasetDocumentList } from "../dataset-document-list"; +import { Dataset } from "../../../types"; +import { DatasetImageList } from "../dataset-image-list"; import { DatasetNavigationbar } from "../dataset-navigationbar"; const StyledModal = styled(Modal)` @@ -19,71 +19,66 @@ export const DatasetModal = ({ deleteDocuments: (ids: string[]) => void; }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); - // TODO: use a map for props - const [datasetWithProps, setDatasetWithProps] = useState( - dataset.map((document: DocumentItem) => ({ - documentItem: document, - props: { isSelected: false }, - })), + + const [selectedImages, setSelectedImages] = useState>( + new Map(dataset.images.map((image) => [image.id, false])), ); // sync dataset with datasetProps and update selectCount useEffect(() => { - setDatasetWithProps((prev: DocumentWithProps[]) => - dataset.map((document: DocumentItem) => ({ - documentItem: document, - props: prev.find( - (prevDocument: DocumentWithProps) => - prevDocument.documentItem.id === document.id, - )?.props ?? { isSelected: false }, - })), - ); + setSelectedImages((previousSelectedImages) => { + const newSelectedImages = new Map( + dataset.images.map((image) => [image.id, false]), + ); + previousSelectedImages.forEach((value, key) => { + if (newSelectedImages.has(key)) newSelectedImages.set(key, value); + }); + return newSelectedImages; + }); }, [dataset]); const setSelection = useCallback((id: string, selection: boolean) => { - let countDiff = 0; - setDatasetWithProps((prevDatasetWithProps: DocumentWithProps[]) => - prevDatasetWithProps.map((documentWithProps: DocumentWithProps) => { - if (documentWithProps.documentItem.id !== id) return documentWithProps; - if (documentWithProps.props.isSelected !== selection) { - countDiff = documentWithProps.props.isSelected && !selection ? -1 : 1; - } - return { - ...documentWithProps, - props: { ...documentWithProps.props, isSelected: selection }, - }; - }), - ); + setSelectedImages((prevSelectedImages) => { + prevSelectedImages.set(id, selection); + return new Map(prevSelectedImages); + }); }, []); const setSelectAll = useCallback((selection: boolean) => { - setDatasetWithProps((prevDatasetWithProps: DocumentWithProps[]) => - prevDatasetWithProps.map((documentWithProps: DocumentWithProps) => ({ - ...documentWithProps, - props: { ...documentWithProps.props, isSelected: selection }, - })), - ); + setSelectedImages((prevSelectedImages) => { + prevSelectedImages.forEach((value, key) => + prevSelectedImages.set(key, selection), + ); + return new Map(prevSelectedImages); + }); + }, []); + + const toggleSelectMode = useCallback(() => { + setIsInSelectMode((prevIsInSelectMode) => !prevIsInSelectMode); }, []); + const toggleSelectAll = useCallback(() => setSelectAll(!areAllSelected), []); + const deleteSelectedDocuments = useCallback(() => { - const selectedIds = datasetWithProps - .filter( - (documentWithProps: DocumentWithProps) => - documentWithProps.props.isSelected, - ) - .map( - (documentWithProps: DocumentWithProps) => - documentWithProps.documentItem.id, - ); - deleteDocuments(selectedIds); - }, [deleteDocuments, datasetWithProps]); + console.log("pass"); + }, []); + + // const deleteSelectedDocuments = useCallback(() => { + // const selectedIds = datasetWithProps + // .filter( + // (documentWithProps: DocumentWithProps) => + // documentWithProps.props.isSelected, + // ) + // .map( + // (documentWithProps: DocumentWithProps) => + // documentWithProps.documentItem.id, + // ); + // deleteDocuments(selectedIds); + // }, [deleteDocuments, datasetWithProps]); const areAllSelected = useMemo( - () => - datasetWithProps.filter( - (document: DocumentWithProps) => document.props.isSelected, - ).length === datasetWithProps.length, - [datasetWithProps], + () => [...selectedImages.values()].every((value) => value), + [selectedImages], ); return ( @@ -95,24 +90,16 @@ export const DatasetModal = ({ { - if (isInSelectMode) { - setSelectAll(false); - } - setIsInSelectMode((prev: boolean) => !prev); - }} - // TODO: Use a callback - toggleSelectAll={() => - areAllSelected ? setSelectAll(false) : setSelectAll(true) - } + toggleSelectMode={toggleSelectMode} + toggleSelectAll={toggleSelectAll} deleteSelectedDocuments={deleteSelectedDocuments} /> } > -
diff --git a/apps/editor/src/components/menu/hub-actions.tsx b/apps/editor/src/components/menu/hub-actions.tsx index 57d954051..0fb1bcc06 100755 --- a/apps/editor/src/components/menu/hub-actions.tsx +++ b/apps/editor/src/components/menu/hub-actions.tsx @@ -1,26 +1,26 @@ -import { Dataset, DocumentItem } from "../../types/dataset-types"; +import { Dataset } from "../../types/dataset-types"; -const path = "https://webhook.site/b9c9c6a8-7804-4477-869e-dc6cec7ba8ba"; +const baseUrl = "http://localhost:3000/"; +const datasetId = "f21452ab-a82a-4f88-9786-0c5cbf1086fa"; // fetches Dataset from Database export const getDatasetFormDatabase = async () => { - let dataset: Dataset = []; - await fetch(path) - .then((response) => { - if (!response.ok) { - throw response; - } - return response.json(); - }) - .then((data) => - data.map((document: DocumentItem) => ({ - ...document, - props: { isSelected: false }, - })), - ) - .then((data) => { - dataset = data; - }); + const datasetResponse = await fetch(`${baseUrl}datasets/${datasetId}`); + const dataset = (await datasetResponse.json()) as Dataset; + + const imagesResponse = await fetch(`${baseUrl}images?dataset=${datasetId}`); + dataset.images = await imagesResponse.json(); + + await Promise.all( + dataset.images.map(async (image: any) => { + const annotationsResponse = await fetch( + `${baseUrl}annotations?image=${image.id}`, + ); + const annotations = await annotationsResponse.json(); + image.annotations = annotations; + }), + ); + return dataset; }; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 70bb45a54..14adf4921 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,14 +1,11 @@ -import { Box, Screen, useIsDraggedOver } from "@visian/ui-shared"; +import { Box, Screen } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; -import React, { useCallback, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; -import { - deleteDocumentFromDatabase, - getDatasetFormDatabase, -} from "../components/menu/hub-actions"; -import { Dataset, DocumentItem } from "../types"; +import { getDatasetFormDatabase } from "../components/menu/hub-actions"; +import { Dataset } from "../types"; const Main = styled(Box)` display: flex; @@ -18,26 +15,30 @@ const Main = styled(Box)` `; export const DatasetScreen: React.FC = observer(() => { - const [dataset, setDataset] = useState([] as Dataset); + const [dataset, setDataset] = useState(); // fetch dataset useEffect(() => { (async () => setDataset(await getDatasetFormDatabase()))(); }, []); - const deleteDocuments = useCallback((ids: string[]) => { - const deletedIds = ids.filter((id) => deleteDocumentFromDatabase(id)); - setDataset((prevDataset: Dataset) => - prevDataset.filter( - (document: DocumentItem) => !deletedIds.includes(document.id), - ), + if (!dataset?.images) { + return ( + +
Loading...
+
); - }, []); + } return (
- + { + console.log("foo"); + }} + />
); diff --git a/apps/editor/src/types/dataset-types.tsx b/apps/editor/src/types/dataset-types.tsx index 62ccc3f65..1752d9599 100644 --- a/apps/editor/src/types/dataset-types.tsx +++ b/apps/editor/src/types/dataset-types.tsx @@ -1,21 +1,16 @@ -export type DocumentProp = { - isSelected: boolean; +export type Dataset = { + id: string; + name: string; + images: Image[]; }; -export type DocumentItem = { +export type Image = { id: string; - name: string; + dataUri: string; annotations: Annotation[]; }; -export type Dataset = DocumentItem[]; - export type Annotation = { id: string; - name: string; -}; - -export type DocumentWithProps = { - documentItem: DocumentItem; - props: DocumentProp; + dataUri: string; }; From f8050d9b91cbca75663d482197906b71ae0ed408 Mon Sep 17 00:00:00 2001 From: Richard Keil <8680858+richartkeil@users.noreply.github.com> Date: Thu, 5 Jan 2023 16:51:38 +0100 Subject: [PATCH 017/500] refactor: remove not yet implemented delete feature --- .../menu/dataset-modal/dataset-modal.tsx | 26 +------------------ .../dataset-navigationbar.tsx | 10 ++----- .../src/components/menu/hub-actions.tsx | 6 +---- apps/editor/src/screens/dataset-screen.tsx | 11 +++----- 4 files changed, 7 insertions(+), 46 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index ea6931772..53b644004 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -11,13 +11,7 @@ const StyledModal = styled(Modal)` width: 100%; `; -export const DatasetModal = ({ - dataset, - deleteDocuments, -}: { - dataset: Dataset; - deleteDocuments: (ids: string[]) => void; -}) => { +export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); const [selectedImages, setSelectedImages] = useState>( @@ -59,23 +53,6 @@ export const DatasetModal = ({ const toggleSelectAll = useCallback(() => setSelectAll(!areAllSelected), []); - const deleteSelectedDocuments = useCallback(() => { - console.log("pass"); - }, []); - - // const deleteSelectedDocuments = useCallback(() => { - // const selectedIds = datasetWithProps - // .filter( - // (documentWithProps: DocumentWithProps) => - // documentWithProps.props.isSelected, - // ) - // .map( - // (documentWithProps: DocumentWithProps) => - // documentWithProps.documentItem.id, - // ); - // deleteDocuments(selectedIds); - // }, [deleteDocuments, datasetWithProps]); - const areAllSelected = useMemo( () => [...selectedImages.values()].every((value) => value), [selectedImages], @@ -92,7 +69,6 @@ export const DatasetModal = ({ allSelected={areAllSelected} toggleSelectMode={toggleSelectMode} toggleSelectAll={toggleSelectAll} - deleteSelectedDocuments={deleteSelectedDocuments} /> } > diff --git a/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx index 4750593d5..0cd72df6a 100644 --- a/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx +++ b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx @@ -15,13 +15,11 @@ export const DatasetNavigationbar = ({ allSelected, toggleSelectMode, toggleSelectAll, - deleteSelectedDocuments, }: { isInSelectMode: boolean; allSelected: boolean; toggleSelectMode: () => void; toggleSelectAll: () => void; - deleteSelectedDocuments: () => void; }) => isInSelectMode ? ( <> @@ -29,12 +27,8 @@ export const DatasetNavigationbar = ({ labelTx={allSelected ? "deselect-all" : "select-all"} handlePress={toggleSelectAll} /> - - + + { +export const fetchDataset = async () => { const datasetResponse = await fetch(`${baseUrl}datasets/${datasetId}`); const dataset = (await datasetResponse.json()) as Dataset; @@ -23,6 +22,3 @@ export const getDatasetFormDatabase = async () => { return dataset; }; - -// returns true if Document is succsessfully deleted from Database -export const deleteDocumentFromDatabase = (id: string) => true; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 14adf4921..0a8a5350c 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from "react"; import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; -import { getDatasetFormDatabase } from "../components/menu/hub-actions"; +import { fetchDataset } from "../components/menu/hub-actions"; import { Dataset } from "../types"; const Main = styled(Box)` @@ -19,7 +19,7 @@ export const DatasetScreen: React.FC = observer(() => { // fetch dataset useEffect(() => { - (async () => setDataset(await getDatasetFormDatabase()))(); + (async () => setDataset(await fetchDataset()))(); }, []); if (!dataset?.images) { @@ -33,12 +33,7 @@ export const DatasetScreen: React.FC = observer(() => { return (
- { - console.log("foo"); - }} - /> +
); From 86831180bda1958617d8757689f895b70250f415 Mon Sep 17 00:00:00 2001 From: Richard Keil <8680858+richartkeil@users.noreply.github.com> Date: Thu, 5 Jan 2023 17:12:17 +0100 Subject: [PATCH 018/500] fix: cannot deselect all images in dataset --- .../src/components/menu/dataset-modal/dataset-modal.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 53b644004..952a6c97a 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -51,13 +51,16 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { setIsInSelectMode((prevIsInSelectMode) => !prevIsInSelectMode); }, []); - const toggleSelectAll = useCallback(() => setSelectAll(!areAllSelected), []); - const areAllSelected = useMemo( () => [...selectedImages.values()].every((value) => value), [selectedImages], ); + const toggleSelectAll = useCallback( + () => setSelectAll(!areAllSelected), + [areAllSelected, setSelectAll], + ); + return ( Date: Thu, 5 Jan 2023 19:28:26 +0100 Subject: [PATCH 019/500] fix: ci --- .../src/components/icons/{delete.svg => delete-ar.svg} | 0 apps/ar-demo/src/components/icons/index.ts | 4 ++-- .../src/components/icons/{select.svg => select-ar.svg} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename apps/ar-demo/src/components/icons/{delete.svg => delete-ar.svg} (100%) rename apps/ar-demo/src/components/icons/{select.svg => select-ar.svg} (100%) diff --git a/apps/ar-demo/src/components/icons/delete.svg b/apps/ar-demo/src/components/icons/delete-ar.svg similarity index 100% rename from apps/ar-demo/src/components/icons/delete.svg rename to apps/ar-demo/src/components/icons/delete-ar.svg diff --git a/apps/ar-demo/src/components/icons/index.ts b/apps/ar-demo/src/components/icons/index.ts index 4f479ec2c..19c501117 100644 --- a/apps/ar-demo/src/components/icons/index.ts +++ b/apps/ar-demo/src/components/icons/index.ts @@ -1,6 +1,6 @@ -export { ReactComponent as SelectIcon } from "./select.svg"; +export { ReactComponent as SelectIcon } from "./select-ar.svg"; export { ReactComponent as EraserIcon } from "./eraser-ar.svg"; -export { ReactComponent as DeleteIcon } from "./delete.svg"; +export { ReactComponent as DeleteIcon } from "./delete-ar.svg"; export { ReactComponent as ClearIcon } from "./clear.svg"; export { ReactComponent as InvertSelectionIcon } from "./invertSelection.svg"; export { ReactComponent as UndoIcon } from "./undo-ar.svg"; diff --git a/apps/ar-demo/src/components/icons/select.svg b/apps/ar-demo/src/components/icons/select-ar.svg similarity index 100% rename from apps/ar-demo/src/components/icons/select.svg rename to apps/ar-demo/src/components/icons/select-ar.svg From 11d09a5aeb4e474f7fdaa814a2f4d9faf139d212 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Fri, 6 Jan 2023 00:01:57 +0100 Subject: [PATCH 020/500] fix: DatasetModal is not showing the correct name --- .../src/components/menu/dataset-modal/dataset-modal.tsx | 2 +- .../menu/dataset-navigationbar/dataset-navigationbar.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 952a6c97a..8c23b5aba 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -64,7 +64,7 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { return ( - + Date: Fri, 13 Jan 2023 14:59:32 +0100 Subject: [PATCH 021/500] refactor: use react-query for data fetching --- .../dataset-image-list-item.tsx | 50 ++++++++++++++----- .../dataset-image-list/dataset-image-list.tsx | 11 ++-- .../menu/dataset-modal/dataset-modal.tsx | 33 ++++++++---- .../src/components/menu/hub-actions.tsx | 24 --------- apps/editor/src/querys/base-url.tsx | 3 ++ apps/editor/src/querys/index.ts | 3 ++ apps/editor/src/querys/use-annotations-by.tsx | 37 ++++++++++++++ apps/editor/src/querys/use-dataset.tsx | 35 +++++++++++++ apps/editor/src/querys/use-images-by.tsx | 33 ++++++++++++ apps/editor/src/screens/dataset-screen.tsx | 50 ++++++++++++------- apps/editor/src/types/dataset-types.tsx | 11 +++- 11 files changed, 218 insertions(+), 72 deletions(-) delete mode 100755 apps/editor/src/components/menu/hub-actions.tsx create mode 100644 apps/editor/src/querys/base-url.tsx create mode 100644 apps/editor/src/querys/index.ts create mode 100644 apps/editor/src/querys/use-annotations-by.tsx create mode 100644 apps/editor/src/querys/use-dataset.tsx create mode 100644 apps/editor/src/querys/use-images-by.tsx diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index f35e3c6d1..90402c75f 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -1,7 +1,8 @@ import { InvisibleButton, List, ListItem, Text } from "@visian/ui-shared"; -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import styled from "styled-components"; +import { useAnnotationsBy } from "../../../querys"; import { Annotation, Image } from "../../../types"; const Spacer = styled.div` @@ -24,20 +25,38 @@ const AnnotationsList = styled(List)` export const DatasetImageListItem = ({ isInSelectMode, image, + refetchImages, isSelected, toggleSelection, }: { isInSelectMode: boolean; image: Image; + refetchImages: () => void; isSelected: boolean; toggleSelection: () => void; }) => { + const { + annotations, + annotationsError, + isErrorAnnotations, + isLoadingAnnotations, + refetchAnnotations, + } = useAnnotationsBy(image.id); + const [showAnnotations, setShowAnnotations] = useState(false); - const toggleShowAnnotations = useCallback( - () => setShowAnnotations((prev: boolean) => !prev), - [], - ); + // refetch images if annotations can't be loaded + useEffect(() => { + if (isErrorAnnotations) refetchImages(); + }, [isErrorAnnotations, refetchImages]); + + const toggleShowAnnotations = useCallback(() => { + setShowAnnotations((prev: boolean) => { + // refetch annotations if the annotations list is being opened + if (!prev) refetchAnnotations(); + return !prev; + }); + }, [refetchAnnotations]); return ( <> @@ -58,13 +77,20 @@ export const DatasetImageListItem = ({ onPointerDown={toggleShowAnnotations} /> - {showAnnotations && ( - - {image.annotations.map((annotation: Annotation) => ( - {annotation.dataUri} - ))} - - )} + {showAnnotations && + (isLoadingAnnotations ? ( + Loading Annotations... + ) : isErrorAnnotations ? ( + {`Error on loading Annotations: ${annotationsError?.message}`} + ) : ( + annotations && ( + + {annotations.map((annotation: Annotation) => ( + {annotation.dataUri} + ))} + + ) + ))} ); }; diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx index d4cced917..7b9fec27e 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx @@ -1,7 +1,7 @@ import { List, stopPropagation } from "@visian/ui-shared"; import styled from "styled-components"; -import { Dataset, Image } from "../../../types"; +import { Image } from "../../../types"; import { DatasetImageListItem } from "./dataset-image-list-item"; const DocumentList = styled(List)` @@ -12,20 +12,23 @@ const DocumentList = styled(List)` export const DatasetImageList = ({ isInSelectMode, - dataset, + images, + refetchImages, selectedImages, setSelection, }: { isInSelectMode: boolean; - dataset: Dataset; + images: Image[]; + refetchImages: () => void; selectedImages: Map; setSelection: (id: string, selection: boolean) => void; }) => ( - {dataset.images.map((image: Image) => ( + {images.map((image: Image) => ( setSelection(image.id, !selectedImages.get(image.id)) diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 8c23b5aba..05a46f9ef 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -1,7 +1,8 @@ -import { Modal } from "@visian/ui-shared"; +import { Modal, Text } from "@visian/ui-shared"; import { useCallback, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; +import { useImagesBy } from "../../../querys"; import { Dataset } from "../../../types"; import { DatasetImageList } from "../dataset-image-list"; import { DatasetNavigationbar } from "../dataset-navigationbar"; @@ -14,22 +15,25 @@ const StyledModal = styled(Modal)` export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); + const { images, imagesError, isErrorImages, isLoadingImages, refetchImages } = + useImagesBy(dataset.id); + const [selectedImages, setSelectedImages] = useState>( - new Map(dataset.images.map((image) => [image.id, false])), + new Map((images ?? []).map((image) => [image.id, false])), ); - // sync dataset with datasetProps and update selectCount + // sync selectedImages and images array useEffect(() => { setSelectedImages((previousSelectedImages) => { const newSelectedImages = new Map( - dataset.images.map((image) => [image.id, false]), + (images ?? []).map((image) => [image.id, false]), ); previousSelectedImages.forEach((value, key) => { if (newSelectedImages.has(key)) newSelectedImages.set(key, value); }); return newSelectedImages; }); - }, [dataset]); + }, [images]); const setSelection = useCallback((id: string, selection: boolean) => { setSelectedImages((prevSelectedImages) => { @@ -75,12 +79,19 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { /> } > - + {isLoadingImages && Loading Images...} + {isErrorImages && ( + {`Error on loading Images: ${imagesError?.message}`} + )} + {images && ( + + )} ); }; diff --git a/apps/editor/src/components/menu/hub-actions.tsx b/apps/editor/src/components/menu/hub-actions.tsx deleted file mode 100755 index 03e0114ce..000000000 --- a/apps/editor/src/components/menu/hub-actions.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Dataset } from "../../types/dataset-types"; - -const baseUrl = "http://localhost:3000/"; -const datasetId = "f21452ab-a82a-4f88-9786-0c5cbf1086fa"; - -export const fetchDataset = async () => { - const datasetResponse = await fetch(`${baseUrl}datasets/${datasetId}`); - const dataset = (await datasetResponse.json()) as Dataset; - - const imagesResponse = await fetch(`${baseUrl}images?dataset=${datasetId}`); - dataset.images = await imagesResponse.json(); - - await Promise.all( - dataset.images.map(async (image: any) => { - const annotationsResponse = await fetch( - `${baseUrl}annotations?image=${image.id}`, - ); - const annotations = await annotationsResponse.json(); - image.annotations = annotations; - }), - ); - - return dataset; -}; diff --git a/apps/editor/src/querys/base-url.tsx b/apps/editor/src/querys/base-url.tsx new file mode 100644 index 000000000..95b138d97 --- /dev/null +++ b/apps/editor/src/querys/base-url.tsx @@ -0,0 +1,3 @@ +export const baseUrl = "http://localhost:3000/"; + +export default baseUrl; diff --git a/apps/editor/src/querys/index.ts b/apps/editor/src/querys/index.ts new file mode 100644 index 000000000..2ced3fc5a --- /dev/null +++ b/apps/editor/src/querys/index.ts @@ -0,0 +1,3 @@ +export * from "./use-dataset"; +export * from "./use-images-by"; +export * from "./use-annotations-by"; diff --git a/apps/editor/src/querys/use-annotations-by.tsx b/apps/editor/src/querys/use-annotations-by.tsx new file mode 100644 index 000000000..393a8b718 --- /dev/null +++ b/apps/editor/src/querys/use-annotations-by.tsx @@ -0,0 +1,37 @@ +import { useQuery } from "react-query"; + +import { Annotation } from "../types"; +import { baseUrl } from "./base-url"; + +const fetchAnnotationsBy = async (imageId: string) => { + const annotationsResponse = await fetch( + `${baseUrl}annotations?image=${imageId}`, + ); + if (!annotationsResponse.ok) + throw new Error( + `${annotationsResponse.statusText} (${annotationsResponse.status})`, + ); + const annotations = (await annotationsResponse.json()) as Annotation[]; + return annotations; +}; + +export const useAnnotationsBy = (imageId: string) => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Annotation[], + Error + >(["annotationsBy", imageId], () => fetchAnnotationsBy(imageId), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 20, // refetch every 20 seconds + }); + + return { + annotations: data, + annotationsError: error, + isErrorAnnotations: isError, + isLoadingAnnotations: isLoading, + refetchAnnotations: refetch, + removeAnnotations: remove, + }; +}; + +export default useAnnotationsBy; diff --git a/apps/editor/src/querys/use-dataset.tsx b/apps/editor/src/querys/use-dataset.tsx new file mode 100644 index 000000000..1d975d070 --- /dev/null +++ b/apps/editor/src/querys/use-dataset.tsx @@ -0,0 +1,35 @@ +import { useQuery } from "react-query"; + +import { Dataset } from "../types"; +import { baseUrl } from "./base-url"; + +const fetchDataset = async (datasetId: string) => { + const datasetResponse = await fetch(`${baseUrl}datasets/${datasetId}`); + if (!datasetResponse.ok) + throw new Error( + `${datasetResponse.statusText} (${datasetResponse.status})`, + ); + const dataset = (await datasetResponse.json()) as Dataset; + return dataset; +}; + +export const useDataset = (datasetId: string) => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Dataset, + Error + >(["dataset", datasetId], () => fetchDataset(datasetId), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 60, // refetch every minute + }); + + return { + dataset: data, + datasetError: error, + isErrorDataset: isError, + isLoadingDataset: isLoading, + refetchDataset: refetch, + removeDataset: remove, + }; +}; + +export default useDataset; diff --git a/apps/editor/src/querys/use-images-by.tsx b/apps/editor/src/querys/use-images-by.tsx new file mode 100644 index 000000000..0abcfe532 --- /dev/null +++ b/apps/editor/src/querys/use-images-by.tsx @@ -0,0 +1,33 @@ +import { useQuery } from "react-query"; + +import { Image } from "../types"; +import { baseUrl } from "./base-url"; + +const fetchImagesBy = async (datasetId: string) => { + const imagesResponse = await fetch(`${baseUrl}images?dataset=${datasetId}`); + if (!imagesResponse.ok) + throw new Error(`${imagesResponse.statusText} (${imagesResponse.status})`); + const images = (await imagesResponse.json()) as Image[]; + return images; +}; + +export const useImagesBy = (datasetId: string) => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Image[], + Error + >(["imagesBy", datasetId], () => fetchImagesBy(datasetId), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 10, // refetch every 10 seconds + }); + + return { + images: data, + imagesError: error, + isErrorImages: isError, + isLoadingImages: isLoading, + refetchImages: refetch, + removeImages: remove, + }; +}; + +export default useImagesBy; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 0a8a5350c..0e61164f8 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,11 +1,11 @@ -import { Box, Screen } from "@visian/ui-shared"; +import { Box, Modal, Screen, Text } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; -import React, { useEffect, useState } from "react"; +import React from "react"; +import { ReactQueryDevtools } from "react-query/devtools"; import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; -import { fetchDataset } from "../components/menu/hub-actions"; -import { Dataset } from "../types"; +import { useDataset } from "../querys"; const Main = styled(Box)` display: flex; @@ -14,27 +14,39 @@ const Main = styled(Box)` padding: 5rem 10rem; `; -export const DatasetScreen: React.FC = observer(() => { - const [dataset, setDataset] = useState(); +const StyledModal = styled(Modal)` + vertical-align: middle; + width: 100%; +`; - // fetch dataset - useEffect(() => { - (async () => setDataset(await fetchDataset()))(); - }, []); +const datasetId = "2f7c3aaf-6008-4537-9ab2-3893b16a67f6"; - if (!dataset?.images) { - return ( - -
Loading...
-
- ); - } +export const DatasetScreen: React.FC = observer(() => { + const { dataset, datasetError, isErrorDataset, isLoadingDataset } = + useDataset(datasetId); return ( - +
- + {isLoadingDataset && } + {isErrorDataset && ( + + {`Error on loading Dataset: ${datasetError?.message}`} + + )} + {dataset && }
+ {false && }
); }); diff --git a/apps/editor/src/types/dataset-types.tsx b/apps/editor/src/types/dataset-types.tsx index 1752d9599..3a9b06424 100644 --- a/apps/editor/src/types/dataset-types.tsx +++ b/apps/editor/src/types/dataset-types.tsx @@ -1,16 +1,23 @@ export type Dataset = { id: string; name: string; - images: Image[]; + project: string; + createdAt: string; + updatedAt: string; }; export type Image = { id: string; dataUri: string; - annotations: Annotation[]; + dataset: string; + createdAt: string; + updatedAt: string; }; export type Annotation = { id: string; dataUri: string; + image: string; + createdAt: string; + updatedAt: string; }; From 9651d01fe76a942154c274ec0a74b3a41f5ed094 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Mon, 16 Jan 2023 12:07:12 +0100 Subject: [PATCH 022/500] feat: user translations for dataset-screen --- apps/editor/src/assets/de.json | 14 +++++++++- apps/editor/src/assets/en.json | 14 +++++++++- .../dataset-image-list-item.tsx | 16 +++++++++--- .../dataset-image-list/dataset-image-list.tsx | 6 ++--- .../menu/dataset-modal/dataset-modal.tsx | 10 ++++--- apps/editor/src/screens/dataset-screen.tsx | 26 +++++++++++-------- 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index 2aa0334ec..e5982811a 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -1,5 +1,6 @@ { "loading": "Lädt...", + "error": "Fehler!", "reset": "Zurücksetzen", "drop-file": "Legen Sie Scan oder Annotation hier ab", @@ -266,5 +267,16 @@ "delete-documents": "Dokumente löschen", "auto-annotate-documents": "Dokumente automatisch annotieren", "exit-select-mode": "Auswahlmodus verlassen", - "select-mode": "Auswahlmodus" + "select-mode": "Auswahlmodus", + + "dataset-base-title": "VISIAN Dataset", + + "dataset-loading": "Dataset wird geladen...", + "dataset-loading-error": "Fehler beim Laden des Datensatzes:", + + "images-loading": "Images werden geladen...", + "images-loading-error": "Fehler beim Laden der Images:", + + "annotations-loading": "Annotationen werden geladen...", + "annotations-loading-error": "Fehler beim Laden der Annotationen:" } diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 4c81f16f0..91c6d43a4 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -1,5 +1,6 @@ { "loading": "Loading...", + "error": "Error!", "reset": "Reset", "drop-file": "Drop your scan or annotation here", @@ -266,5 +267,16 @@ "delete-documents": "Delete documents", "auto-annotate-documents": "Auto-annotate documents", "exit-select-mode": "Exit select mode", - "select-mode": "Select mode" + "select-mode": "Select mode", + + "dataset-base-title": "VISIAN Dataset", + + "dataset-loading": "Loading Dataset...", + "dataset-loading-error": "Error on loading Dataset:", + + "images-loading": "Loading Images...", + "images-loading-error": "Error on loading Images:", + + "annotations-loading": "Loading Annotations...", + "annotations-loading-error": "Error on loading Annotations:" } diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index 90402c75f..f82adcea9 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -1,4 +1,10 @@ -import { InvisibleButton, List, ListItem, Text } from "@visian/ui-shared"; +import { + InvisibleButton, + List, + ListItem, + Text, + useTranslation, +} from "@visian/ui-shared"; import { useCallback, useEffect, useState } from "react"; import styled from "styled-components"; @@ -58,6 +64,8 @@ export const DatasetImageListItem = ({ }); }, [refetchAnnotations]); + const { t: translate } = useTranslation(); + return ( <> @@ -79,9 +87,11 @@ export const DatasetImageListItem = ({ {showAnnotations && (isLoadingAnnotations ? ( - Loading Annotations... + ) : isErrorAnnotations ? ( - {`Error on loading Annotations: ${annotationsError?.message}`} + {`${translate("annotations-loading-error")} ${ + annotationsError?.message + }`} ) : ( annotations && ( diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx index 7b9fec27e..4af7158bc 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { Image } from "../../../types"; import { DatasetImageListItem } from "./dataset-image-list-item"; -const DocumentList = styled(List)` +const ImageList = styled(List)` width: 100%; height: 400px; overflow-y: auto; @@ -23,7 +23,7 @@ export const DatasetImageList = ({ selectedImages: Map; setSelection: (id: string, selection: boolean) => void; }) => ( - + {images.map((image: Image) => ( ))} - + ); diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 05a46f9ef..9cbb6d125 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -1,4 +1,4 @@ -import { Modal, Text } from "@visian/ui-shared"; +import { Modal, Text, useTranslation } from "@visian/ui-shared"; import { useCallback, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; @@ -65,6 +65,8 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { [areAllSelected, setSelectAll], ); + const { t: translate } = useTranslation(); + return ( { /> } > - {isLoadingImages && Loading Images...} + {isLoadingImages && } {isErrorImages && ( - {`Error on loading Images: ${imagesError?.message}`} + {`${translate("images-loading-error")} ${ + imagesError?.message + }`} )} {images && ( { const { dataset, datasetError, isErrorDataset, isLoadingDataset } = useDataset(datasetId); + const { t: translate } = useTranslation(); + return (
- {isLoadingDataset && } + {isLoadingDataset && } {isErrorDataset && ( - - {`Error on loading Dataset: ${datasetError?.message}`} + + {`${translate("dataset-loading-error")} ${ + datasetError?.message + }`} )} {dataset && } From f63b29e1f822d74059dcd91045851611002e0545 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Mon, 16 Jan 2023 12:09:39 +0100 Subject: [PATCH 023/500] refactor: use interface instead of types --- apps/editor/src/types/dataset-types.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/editor/src/types/dataset-types.tsx b/apps/editor/src/types/dataset-types.tsx index 3a9b06424..bd1c39c9f 100644 --- a/apps/editor/src/types/dataset-types.tsx +++ b/apps/editor/src/types/dataset-types.tsx @@ -1,23 +1,23 @@ -export type Dataset = { +export interface Dataset { id: string; name: string; project: string; createdAt: string; updatedAt: string; -}; +} -export type Image = { +export interface Image { id: string; dataUri: string; dataset: string; createdAt: string; updatedAt: string; -}; +} -export type Annotation = { +export interface Annotation { id: string; dataUri: string; image: string; createdAt: string; updatedAt: string; -}; +} From 0649350f825a33cfd16aae7ed417eadba724427c Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Mon, 16 Jan 2023 13:12:41 +0100 Subject: [PATCH 024/500] build: add axios as dependencie --- package.json | 1 + yarn.lock | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/package.json b/package.json index f2917dbae..e39da2bd6 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "dependencies": { "@aws-amplify/ui-react": "^1.2.15", "aws-amplify": "^4.2.9", + "axios": "^1.2.2", "core-js": "^3.6.5", "css-element-queries": "^1.2.3", "file-saver": "^2.0.5", diff --git a/yarn.lock b/yarn.lock index 656e5a5c5..dbc23dbd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9166,6 +9166,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.2.2": + version: 1.2.2 + resolution: "axios@npm:1.2.2" + dependencies: + follow-redirects: ^1.15.0 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: 6e357491b38426c5720f7328ecbafca3c643b03952c052d787570672ce7a9365717c2d64db4ce97cfbee3f830fa405101e360e14d0857ef7f96a9f4d814c4e03 + languageName: node + linkType: hard + "axobject-query@npm:^2.2.0": version: 2.2.0 resolution: "axobject-query@npm:2.2.0" @@ -24152,6 +24163,7 @@ __metadata: "@typescript-eslint/eslint-plugin": ^5.41.0 "@typescript-eslint/parser": ^5.41.0 aws-amplify: ^4.2.9 + axios: ^1.2.2 babel-jest: 28.1.1 babel-loader: 8.1.0 babel-plugin-styled-components: 1.10.7 From 4be03a4add7d3366f92faeb608a9cb6b497fcc7f Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Mon, 16 Jan 2023 13:15:13 +0100 Subject: [PATCH 025/500] refactor: use axios instead of fetch for querys --- .../dataset-image-list-item.tsx | 4 ++-- .../menu/dataset-modal/dataset-modal.tsx | 4 ++-- apps/editor/src/querys/use-annotations-by.tsx | 23 ++++++++++--------- apps/editor/src/querys/use-dataset.tsx | 18 +++++++-------- apps/editor/src/querys/use-images-by.tsx | 18 ++++++++------- apps/editor/src/screens/dataset-screen.tsx | 6 ++--- 6 files changed, 37 insertions(+), 36 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index f82adcea9..c0d4df44d 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -90,8 +90,8 @@ export const DatasetImageListItem = ({ ) : isErrorAnnotations ? ( {`${translate("annotations-loading-error")} ${ - annotationsError?.message - }`} + annotationsError?.response?.statusText + } (${annotationsError?.response?.status})`} ) : ( annotations && ( diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 9cbb6d125..dd920d443 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -84,8 +84,8 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { {isLoadingImages && } {isErrorImages && ( {`${translate("images-loading-error")} ${ - imagesError?.message - }`} + imagesError?.response?.statusText + } (${imagesError?.response?.status})`} )} {images && ( { - const annotationsResponse = await fetch( - `${baseUrl}annotations?image=${imageId}`, +const getAnnotationsBy = async (imageId: string) => { + const annotationsResponse = await axios.get( + `${baseUrl}annotations`, + { + params: { + image: imageId, + }, + }, ); - if (!annotationsResponse.ok) - throw new Error( - `${annotationsResponse.statusText} (${annotationsResponse.status})`, - ); - const annotations = (await annotationsResponse.json()) as Annotation[]; - return annotations; + return annotationsResponse.data; }; export const useAnnotationsBy = (imageId: string) => { const { data, error, isError, isLoading, refetch, remove } = useQuery< Annotation[], - Error - >(["annotationsBy", imageId], () => fetchAnnotationsBy(imageId), { + AxiosError + >(["annotationsBy", imageId], () => getAnnotationsBy(imageId), { retry: 2, // retry twice if fetch fails refetchInterval: 1000 * 20, // refetch every 20 seconds }); diff --git a/apps/editor/src/querys/use-dataset.tsx b/apps/editor/src/querys/use-dataset.tsx index 1d975d070..00a78e6cf 100644 --- a/apps/editor/src/querys/use-dataset.tsx +++ b/apps/editor/src/querys/use-dataset.tsx @@ -1,23 +1,21 @@ +import axios, { AxiosError } from "axios"; import { useQuery } from "react-query"; import { Dataset } from "../types"; import { baseUrl } from "./base-url"; -const fetchDataset = async (datasetId: string) => { - const datasetResponse = await fetch(`${baseUrl}datasets/${datasetId}`); - if (!datasetResponse.ok) - throw new Error( - `${datasetResponse.statusText} (${datasetResponse.status})`, - ); - const dataset = (await datasetResponse.json()) as Dataset; - return dataset; +const getDataset = async (datasetId: string) => { + const datasetResponse = await axios.get( + `${baseUrl}datasets/${datasetId}`, + ); + return datasetResponse.data; }; export const useDataset = (datasetId: string) => { const { data, error, isError, isLoading, refetch, remove } = useQuery< Dataset, - Error - >(["dataset", datasetId], () => fetchDataset(datasetId), { + AxiosError + >(["dataset", datasetId], () => getDataset(datasetId), { retry: 2, // retry twice if fetch fails refetchInterval: 1000 * 60, // refetch every minute }); diff --git a/apps/editor/src/querys/use-images-by.tsx b/apps/editor/src/querys/use-images-by.tsx index 0abcfe532..386f12d74 100644 --- a/apps/editor/src/querys/use-images-by.tsx +++ b/apps/editor/src/querys/use-images-by.tsx @@ -1,21 +1,23 @@ +import axios, { AxiosError } from "axios"; import { useQuery } from "react-query"; import { Image } from "../types"; import { baseUrl } from "./base-url"; -const fetchImagesBy = async (datasetId: string) => { - const imagesResponse = await fetch(`${baseUrl}images?dataset=${datasetId}`); - if (!imagesResponse.ok) - throw new Error(`${imagesResponse.statusText} (${imagesResponse.status})`); - const images = (await imagesResponse.json()) as Image[]; - return images; +const getImagesBy = async (datasetId: string) => { + const imagesResponse = await axios.get(`${baseUrl}images`, { + params: { + dataset: datasetId, + }, + }); + return imagesResponse.data; }; export const useImagesBy = (datasetId: string) => { const { data, error, isError, isLoading, refetch, remove } = useQuery< Image[], - Error - >(["imagesBy", datasetId], () => fetchImagesBy(datasetId), { + AxiosError + >(["imagesBy", datasetId], () => getImagesBy(datasetId), { retry: 2, // retry twice if fetch fails refetchInterval: 1000 * 10, // refetch every 10 seconds }); diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index db5cf1e32..290a01df1 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -20,7 +20,7 @@ const StyledModal = styled(Modal)` `; const datasetId = "2f7c3aaf-6008-4537-9ab2-3893b16a67f6"; -// `Error on loading Dataset: ${datasetError?.message}` + export const DatasetScreen: React.FC = observer(() => { const { dataset, datasetError, isErrorDataset, isLoadingDataset } = useDataset(datasetId); @@ -44,8 +44,8 @@ export const DatasetScreen: React.FC = observer(() => { {isErrorDataset && ( {`${translate("dataset-loading-error")} ${ - datasetError?.message - }`} + datasetError?.response?.statusText + } (${datasetError?.response?.status})`} )} {dataset && } From af0d93894449dd545d209f2d631d659cec4029ee Mon Sep 17 00:00:00 2001 From: Richard Keil <8680858+richartkeil@users.noreply.github.com> Date: Thu, 19 Jan 2023 17:43:48 +0100 Subject: [PATCH 026/500] feat: add Dockerfile --- Dockerfile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..1d2d5b02f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM node:18 as build + +ENV NODE_ENV=production + +WORKDIR /app + +COPY .yarn .yarn +COPY package.json yarn.lock .yarnrc.yml ./ +RUN yarn install + +COPY . . +RUN yarn build editor --prod + +FROM nginx:alpine +COPY --from=build /app/dist/apps/editor /usr/share/nginx/html +EXPOSE 80 From a91f0242fa832c3fda17a4f60e4b9facf2da9aaa Mon Sep 17 00:00:00 2001 From: Richard Keil <8680858+richartkeil@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:04:54 +0100 Subject: [PATCH 027/500] feat: allow passing hub URL when building container --- Dockerfile | 1 + apps/editor/src/querys/base-url.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1d2d5b02f..5bd1ca4c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ FROM node:18 as build ENV NODE_ENV=production +ARG NX_ANNOTATION_SERVICE_HUB_URL WORKDIR /app diff --git a/apps/editor/src/querys/base-url.tsx b/apps/editor/src/querys/base-url.tsx index 95b138d97..9d4a9c68f 100644 --- a/apps/editor/src/querys/base-url.tsx +++ b/apps/editor/src/querys/base-url.tsx @@ -1,3 +1,3 @@ -export const baseUrl = "http://localhost:3000/"; +export const baseUrl = process.env.NX_ANNOTATION_SERVICE_HUB_URL; export default baseUrl; From 5eb6de159435f5963883b9662bd7d972da43eed9 Mon Sep 17 00:00:00 2001 From: Richard Keil <8680858+richartkeil@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:36:45 +0100 Subject: [PATCH 028/500] feat: adjust routes based on presence of hub url --- apps/editor/src/app/app.tsx | 15 +++++++++++---- apps/editor/src/querys/base-url.tsx | 3 --- apps/editor/src/querys/hub-base-url.tsx | 3 +++ apps/editor/src/querys/use-annotations-by.tsx | 4 ++-- apps/editor/src/querys/use-dataset.tsx | 4 ++-- apps/editor/src/querys/use-images-by.tsx | 4 ++-- 6 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 apps/editor/src/querys/base-url.tsx create mode 100644 apps/editor/src/querys/hub-base-url.tsx diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index 28bd962dc..a9b17e7c7 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -20,6 +20,7 @@ import { } from "../constants"; import { setUpEventHandling } from "../event-handling"; import type { RootStore } from "../models"; +import hubBaseUrl from "../querys/hub-base-url"; import { DatasetScreen, EditorScreen } from "../screens"; import { setupRootStore, StoreProvider } from "./root-store"; @@ -75,10 +76,16 @@ function App(): JSX.Element { {isReady && ( - - } /> - } /> - + {hubBaseUrl ? ( + + } /> + } /> + + ) : ( + + } /> + + )} )} diff --git a/apps/editor/src/querys/base-url.tsx b/apps/editor/src/querys/base-url.tsx deleted file mode 100644 index 9d4a9c68f..000000000 --- a/apps/editor/src/querys/base-url.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const baseUrl = process.env.NX_ANNOTATION_SERVICE_HUB_URL; - -export default baseUrl; diff --git a/apps/editor/src/querys/hub-base-url.tsx b/apps/editor/src/querys/hub-base-url.tsx new file mode 100644 index 000000000..3f5c3aa55 --- /dev/null +++ b/apps/editor/src/querys/hub-base-url.tsx @@ -0,0 +1,3 @@ +export const hubBaseUrl = process.env.NX_ANNOTATION_SERVICE_HUB_URL; + +export default hubBaseUrl; diff --git a/apps/editor/src/querys/use-annotations-by.tsx b/apps/editor/src/querys/use-annotations-by.tsx index cbe50ffaa..491a84a1c 100644 --- a/apps/editor/src/querys/use-annotations-by.tsx +++ b/apps/editor/src/querys/use-annotations-by.tsx @@ -2,11 +2,11 @@ import axios, { AxiosError } from "axios"; import { useQuery } from "react-query"; import { Annotation } from "../types"; -import { baseUrl } from "./base-url"; +import { hubBaseUrl } from "./hub-base-url"; const getAnnotationsBy = async (imageId: string) => { const annotationsResponse = await axios.get( - `${baseUrl}annotations`, + `${hubBaseUrl}annotations`, { params: { image: imageId, diff --git a/apps/editor/src/querys/use-dataset.tsx b/apps/editor/src/querys/use-dataset.tsx index 00a78e6cf..f979a11e9 100644 --- a/apps/editor/src/querys/use-dataset.tsx +++ b/apps/editor/src/querys/use-dataset.tsx @@ -2,11 +2,11 @@ import axios, { AxiosError } from "axios"; import { useQuery } from "react-query"; import { Dataset } from "../types"; -import { baseUrl } from "./base-url"; +import { hubBaseUrl } from "./hub-base-url"; const getDataset = async (datasetId: string) => { const datasetResponse = await axios.get( - `${baseUrl}datasets/${datasetId}`, + `${hubBaseUrl}datasets/${datasetId}`, ); return datasetResponse.data; }; diff --git a/apps/editor/src/querys/use-images-by.tsx b/apps/editor/src/querys/use-images-by.tsx index 386f12d74..38c4f93ab 100644 --- a/apps/editor/src/querys/use-images-by.tsx +++ b/apps/editor/src/querys/use-images-by.tsx @@ -2,10 +2,10 @@ import axios, { AxiosError } from "axios"; import { useQuery } from "react-query"; import { Image } from "../types"; -import { baseUrl } from "./base-url"; +import { hubBaseUrl } from "./hub-base-url"; const getImagesBy = async (datasetId: string) => { - const imagesResponse = await axios.get(`${baseUrl}images`, { + const imagesResponse = await axios.get(`${hubBaseUrl}images`, { params: { dataset: datasetId, }, From fbc15678c46eb34fa416135da91943edad69dd67 Mon Sep 17 00:00:00 2001 From: Richard Keil <8680858+richartkeil@users.noreply.github.com> Date: Thu, 19 Jan 2023 21:15:07 +0100 Subject: [PATCH 029/500] fix: routes not handled correctly by nginx --- Dockerfile | 1 + nginx.conf | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 nginx.conf diff --git a/Dockerfile b/Dockerfile index 5bd1ca4c8..e74b2db67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,5 +13,6 @@ COPY . . RUN yarn build editor --prod FROM nginx:alpine +COPY ./nginx.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/dist/apps/editor /usr/share/nginx/html EXPOSE 80 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 000000000..260aa4265 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,8 @@ +server { + listen 80; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } +} From 12a5735041b30834b407500049e426e328f46527 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 24 Jan 2023 17:58:58 +0100 Subject: [PATCH 030/500] feat: model selection popup to create annotation job --- .../menu/dataset-modal/dataset-modal.tsx | 26 ++++++- .../dataset-navigationbar.tsx | 4 +- .../components/menu/ml-model-list/index.ts | 1 + .../menu/ml-model-list/ml-model-list-item.tsx | 53 ++++++++++++++ .../menu/ml-model-list/ml-model-list.tsx | 28 ++++++++ .../components/menu/ml-model-popup/index.ts | 1 + .../ml-model-selection-popup.props.ts | 6 ++ .../ml-model-selection-popup.tsx | 72 +++++++++++++++++++ apps/editor/src/querys/use-ml-models.tsx | 32 +++++++++ apps/editor/src/types/index.ts | 1 + apps/editor/src/types/ml-model-types.tsx | 7 ++ 11 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 apps/editor/src/components/menu/ml-model-list/index.ts create mode 100644 apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx create mode 100644 apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx create mode 100644 apps/editor/src/components/menu/ml-model-popup/index.ts create mode 100644 apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts create mode 100644 apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx create mode 100644 apps/editor/src/querys/use-ml-models.tsx create mode 100644 apps/editor/src/types/ml-model-types.tsx diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index dd920d443..874ad5f94 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -6,11 +6,14 @@ import { useImagesBy } from "../../../querys"; import { Dataset } from "../../../types"; import { DatasetImageList } from "../dataset-image-list"; import { DatasetNavigationbar } from "../dataset-navigationbar"; +import {ModelSelectionPopup} from "../ml-model-popup"; const StyledModal = styled(Modal)` vertical-align: middle; width: 100%; + z-index: 49; `; +//TODO: z-index logic export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); @@ -22,6 +25,15 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { new Map((images ?? []).map((image) => [image.id, false])), ); + // model selection popup + const [isModelSelectionPopUpOpen, setIsModelSelectionPopUpOpen] = useState(false); + const openModelSelectionPopUp = useCallback(() => { + setIsModelSelectionPopUpOpen(true); + }, []); + const closeModelSelectionPopUp = useCallback(() => { + setIsModelSelectionPopUpOpen(false); + }, []); + // sync selectedImages and images array useEffect(() => { setSelectedImages((previousSelectedImages) => { @@ -65,8 +77,13 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { [areAllSelected, setSelectAll], ); - const { t: translate } = useTranslation(); + const getSelectedImageList = () => { + const imageSelection = [...selectedImages.keys()].filter((image) => selectedImages.get(image)); + return imageSelection; + }; + const { t: translate } = useTranslation(); + return ( { allSelected={areAllSelected} toggleSelectMode={toggleSelectMode} toggleSelectAll={toggleSelectAll} + openModelSelectionPopUp={openModelSelectionPopUp} /> } > @@ -96,6 +114,12 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { setSelection={setSelection} /> )} + + ); }; diff --git a/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx index 3929b3535..5b67485de 100644 --- a/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx +++ b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx @@ -15,11 +15,13 @@ export const DatasetNavigationbar = ({ allSelected, toggleSelectMode, toggleSelectAll, + openModelSelectionPopUp, }: { isInSelectMode: boolean; allSelected: boolean; toggleSelectMode: () => void; toggleSelectAll: () => void; + openModelSelectionPopUp: () => void; }) => isInSelectMode ? ( <> @@ -30,9 +32,9 @@ export const DatasetNavigationbar = ({ void; +}) => { + + // const { t: translate } = useTranslation(); + + return ( + + + {model.name} + + {`v${model.version}`} + + + {console.log("info")}} + /> + + ); +}; diff --git a/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx b/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx new file mode 100644 index 000000000..86a43a6d0 --- /dev/null +++ b/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx @@ -0,0 +1,28 @@ +import { List, stopPropagation } from "@visian/ui-shared"; +import { MlModel } from "apps/editor/src/types"; +import styled from "styled-components"; +import { ModelListItem } from "./ml-model-list-item"; + +const ModelsList = styled(List)` + width: 100%; + height: 400px; + overflow-y: auto; +`; + +export const MlModelList = ({ + models, + createAutoAnnotationJob +} : { + models: MlModel[]; + createAutoAnnotationJob: (model: MlModel) => void +}) => ( + + {models.map((model: MlModel) => ( + createAutoAnnotationJob(model)} + key={`${model.name}${model.version}`} + /> + ))} + +); diff --git a/apps/editor/src/components/menu/ml-model-popup/index.ts b/apps/editor/src/components/menu/ml-model-popup/index.ts new file mode 100644 index 000000000..7daf52566 --- /dev/null +++ b/apps/editor/src/components/menu/ml-model-popup/index.ts @@ -0,0 +1 @@ +export * from "./ml-model-selection-popup"; diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts new file mode 100644 index 000000000..85b10c34e --- /dev/null +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts @@ -0,0 +1,6 @@ +import type { StatefulPopUpProps } from "@visian/ui-shared"; + + +export interface ModelPopUpProps extends StatefulPopUpProps{ + getSelectedImageList: () => void; +} diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx new file mode 100644 index 000000000..f10a0ae8d --- /dev/null +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx @@ -0,0 +1,72 @@ +/* eslint-disable max-len */ +import { + PopUp, + Text, +} from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import styled from "styled-components"; +import { ModelPopUpProps } from "./ml-model-selection-popup.props"; +import useMlModels from "apps/editor/src/querys/use-ml-models"; +import axios from "axios"; +import { baseUrl } from "../../../querys/base-url"; +import { MlModel } from "apps/editor/src/types"; +import { MlModelList } from "../ml-model-list"; + + +const SectionLabel = styled(Text)` + font-size: 14px; + margin-bottom: 8px; +`; + + +const ModelSelectionPopupContainer = styled(PopUp)` + align-items: left; + width: 400px; +`; + +export const ModelSelectionPopup = observer(({ isOpen, onClose, getSelectedImageList}) => { + + const {mlModels, mlModelsError, isErrorMlModels, isLoadingMlModels, refetchMlModels, removeMlModels } = + useMlModels(); + + + const createAutoAnnotationJob = (model: MlModel) => { + const imageSelection = getSelectedImageList(); + + console.log({ + "images": imageSelection, + "modelName": model.name, + "modelVersion": model.version + + }); + + axios.post(`${baseUrl}jobs`, { + "images": imageSelection, + "modelName": model.name, + "modelVersion": model.version + + }).then(function (response) { + console.log(response); + onClose && onClose(); + }) + .catch(function (error) { + console.log(error); + onClose && onClose(); + }); + }; + + + return ( + + + {mlModels && ( + + )} + + ); +}); diff --git a/apps/editor/src/querys/use-ml-models.tsx b/apps/editor/src/querys/use-ml-models.tsx new file mode 100644 index 000000000..172e7cb62 --- /dev/null +++ b/apps/editor/src/querys/use-ml-models.tsx @@ -0,0 +1,32 @@ +import axios, { AxiosError } from "axios"; +import { useQuery } from "react-query"; + +import { Image, MlModel } from "../types"; +import { baseUrl } from "./base-url"; + +const getModelVersions = async () => { + const modelsResponse = await axios.get(`${baseUrl}model-versions`, { + }); + return modelsResponse.data; +}; + +export const useMlModels = () => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + MlModel[], + AxiosError + >(["mlModels"], () => getModelVersions(), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 60, // refetch every 10 seconds + }); + + return { + mlModels: data, + mlModelsError: error, + isErrorMlModels: isError, + isLoadingMlModels: isLoading, + refetchMlModels: refetch, + removeMlModels: remove, + }; +}; + +export default useMlModels; diff --git a/apps/editor/src/types/index.ts b/apps/editor/src/types/index.ts index 770f7f420..5b699617c 100644 --- a/apps/editor/src/types/index.ts +++ b/apps/editor/src/types/index.ts @@ -1 +1,2 @@ export * from "./dataset-types"; +export * from "./ml-model-types"; diff --git a/apps/editor/src/types/ml-model-types.tsx b/apps/editor/src/types/ml-model-types.tsx new file mode 100644 index 000000000..5df83f8e3 --- /dev/null +++ b/apps/editor/src/types/ml-model-types.tsx @@ -0,0 +1,7 @@ +export interface MlModel { + name: string; + version: string; + description: string; + createdAt: string; + updatedAt: string; + } \ No newline at end of file From 0590850a19701042f4e5bbcf4c8f376944c1d899 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 24 Jan 2023 18:45:58 +0100 Subject: [PATCH 031/500] feat: disable auto annotate button when no data selected --- .../src/components/menu/dataset-modal/dataset-modal.tsx | 6 ++++++ .../menu/dataset-navigationbar/dataset-navigationbar.tsx | 3 +++ 2 files changed, 9 insertions(+) diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 874ad5f94..25758bc8d 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -72,6 +72,11 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { [selectedImages], ); + const isAnySelected = useMemo( + () => [...selectedImages.values()].some((value) => value), + [selectedImages], + ); + const toggleSelectAll = useCallback( () => setSelectAll(!areAllSelected), [areAllSelected, setSelectAll], @@ -93,6 +98,7 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { void; toggleSelectAll: () => void; openModelSelectionPopUp: () => void; @@ -32,6 +34,7 @@ export const DatasetNavigationbar = ({ Date: Wed, 25 Jan 2023 00:19:36 +0100 Subject: [PATCH 032/500] feat: add NX_ANNOTATION_SERVICE_HUB_URL env variable to nx cloud cache key --- Dockerfile | 2 +- apps/editor/project.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e74b2db67..ad91532c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ FROM node:18 as build ENV NODE_ENV=production -ARG NX_ANNOTATION_SERVICE_HUB_URL WORKDIR /app @@ -10,6 +9,7 @@ COPY package.json yarn.lock .yarnrc.yml ./ RUN yarn install COPY . . +ARG NX_ANNOTATION_SERVICE_HUB_URL RUN yarn build editor --prod FROM nginx:alpine diff --git a/apps/editor/project.json b/apps/editor/project.json index b428a7b3f..26bc12135 100644 --- a/apps/editor/project.json +++ b/apps/editor/project.json @@ -6,6 +6,7 @@ "targets": { "build": { "executor": "@nrwl/webpack:webpack", + "inputs": [{ "env": "NX_ANNOTATION_SERVICE_HUB_URL" }], "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { From 5383579d1ca2eab7acb1cd5e7efa90f22175fdcd Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Wed, 25 Jan 2023 01:23:37 +0100 Subject: [PATCH 033/500] feat: ensure hubBaseUrl has correct format --- apps/editor/src/querys/hub-base-url.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/editor/src/querys/hub-base-url.tsx b/apps/editor/src/querys/hub-base-url.tsx index 3f5c3aa55..a11ea4703 100644 --- a/apps/editor/src/querys/hub-base-url.tsx +++ b/apps/editor/src/querys/hub-base-url.tsx @@ -1,3 +1,16 @@ -export const hubBaseUrl = process.env.NX_ANNOTATION_SERVICE_HUB_URL; +const formatUrl = (url: string | null | undefined) => { + if (!url || url == '') { + return url; + } + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "http://" + url; + } + if (!url.endsWith("/")) { + url = url + "/"; + } + return url; +} + +export const hubBaseUrl = formatUrl(process.env.NX_ANNOTATION_SERVICE_HUB_URL); export default hubBaseUrl; From ae9adc438daca1241271af5aedac9e2a5172a6fc Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Wed, 25 Jan 2023 01:56:34 +0100 Subject: [PATCH 034/500] fix: linter --- apps/editor/src/querys/hub-base-url.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/apps/editor/src/querys/hub-base-url.tsx b/apps/editor/src/querys/hub-base-url.tsx index a11ea4703..5fd89b700 100644 --- a/apps/editor/src/querys/hub-base-url.tsx +++ b/apps/editor/src/querys/hub-base-url.tsx @@ -1,15 +1,19 @@ const formatUrl = (url: string | null | undefined) => { - if (!url || url == '') { - return url; - } - if (!url.startsWith("http://") && !url.startsWith("https://")) { - url = "http://" + url; - } - if (!url.endsWith("/")) { - url = url + "/"; - } + if (!url || url === "") { return url; -} + } + let formattedUrl = url; + if ( + !formattedUrl.startsWith("http://") && + !formattedUrl.startsWith("https://") + ) { + formattedUrl = `http://${formattedUrl}`; + } + if (!formattedUrl.endsWith("/")) { + formattedUrl = `${formattedUrl}/`; + } + return formattedUrl; +}; export const hubBaseUrl = formatUrl(process.env.NX_ANNOTATION_SERVICE_HUB_URL); From 184f9fc3e2b5bc11b8955a7c5694b4935967b8c2 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Wed, 25 Jan 2023 01:57:53 +0100 Subject: [PATCH 035/500] feat: .dockerignore node_modules for smaller images and faster build time --- .dockerignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file From 7c102e4d300183f5c83149a7c3196d672b1214f0 Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Thu, 26 Jan 2023 18:17:39 +0100 Subject: [PATCH 036/500] refactor: language support and remove logs --- apps/editor/src/assets/de.json | 7 ++- apps/editor/src/assets/en.json | 7 ++- .../menu/dataset-modal/dataset-modal.tsx | 2 +- .../dataset-navigationbar.tsx | 2 +- .../menu/ml-model-list/ml-model-list-item.tsx | 13 ++--- .../menu/ml-model-list/ml-model-list.tsx | 3 +- .../ml-model-selection-popup.tsx | 47 +++++++++---------- apps/editor/src/screens/dataset-screen.tsx | 2 +- .../src/lib/components/icon/icons/index.ts | 1 + .../icon/icons/ml-auto-annotation.svg | 1 + 10 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 libs/ui-shared/src/lib/components/icon/icons/ml-auto-annotation.svg diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index e5982811a..5d36e5f04 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -278,5 +278,10 @@ "images-loading-error": "Fehler beim Laden der Images:", "annotations-loading": "Annotationen werden geladen...", - "annotations-loading-error": "Fehler beim Laden der Annotationen:" + "annotations-loading-error": "Fehler beim Laden der Annotationen:", + + "ml-model-selection-description": "Wähle das Modell mit dem die Bilder annotiert werden sollen", + "ml-model-selection-title": "Modell Auswahl", + "ml-models-loading": "Modelle werden geladen", + "ml-models-loading-error": "Fehler beim Laden der Modelle" } diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 91c6d43a4..f4240af0c 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -278,5 +278,10 @@ "images-loading-error": "Error on loading Images:", "annotations-loading": "Loading Annotations...", - "annotations-loading-error": "Error on loading Annotations:" + "annotations-loading-error": "Error on loading Annotations:", + + "ml-model-selection-description": "Select the model you want to use to annotate the images", + "ml-model-selection-title": "Model Selection", + "ml-models-loading": "models loading", + "ml-models-loading-error": "Error on loading ml models" } diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 25758bc8d..871daec3e 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -13,7 +13,7 @@ const StyledModal = styled(Modal)` width: 100%; z-index: 49; `; -//TODO: z-index logic +// TODO: z-index logic export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); diff --git a/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx index 2c3b99508..74b775b29 100644 --- a/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx +++ b/apps/editor/src/components/menu/dataset-navigationbar/dataset-navigationbar.tsx @@ -35,7 +35,7 @@ export const DatasetNavigationbar = ({ diff --git a/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx b/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx index 542228b3c..e5aaf4730 100644 --- a/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx +++ b/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx @@ -4,9 +4,10 @@ import { ListItem, Text, } from "@visian/ui-shared"; -import { MlModel } from "apps/editor/src/types"; import styled from "styled-components"; +import { MlModel } from "../../../types"; + const Spacer = styled.div` width: 10px; `; @@ -32,11 +33,7 @@ export const ModelListItem = ({ }: { model: MlModel; createAutoAnnotationJob: () => void; -}) => { - - // const { t: translate } = useTranslation(); - - return ( +}) => ( {model.name} @@ -45,9 +42,7 @@ export const ModelListItem = ({ {console.log("info")}} + icon= "info" /> ); -}; diff --git a/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx b/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx index 86a43a6d0..a9eaaadac 100644 --- a/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx +++ b/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx @@ -1,6 +1,7 @@ import { List, stopPropagation } from "@visian/ui-shared"; -import { MlModel } from "apps/editor/src/types"; import styled from "styled-components"; + +import { MlModel } from "../../../types"; import { ModelListItem } from "./ml-model-list-item"; const ModelsList = styled(List)` diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx index f10a0ae8d..2913b111b 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx @@ -2,15 +2,18 @@ import { PopUp, Text, + useTranslation, } from "@visian/ui-shared"; +import axios from "axios"; import { observer } from "mobx-react-lite"; import styled from "styled-components"; -import { ModelPopUpProps } from "./ml-model-selection-popup.props"; -import useMlModels from "apps/editor/src/querys/use-ml-models"; -import axios from "axios"; + + import { baseUrl } from "../../../querys/base-url"; -import { MlModel } from "apps/editor/src/types"; +import useMlModels from "../../../querys/use-ml-models"; +import { MlModel } from "../../../types"; import { MlModelList } from "../ml-model-list"; +import { ModelPopUpProps } from "./ml-model-selection-popup.props"; const SectionLabel = styled(Text)` @@ -26,46 +29,42 @@ const ModelSelectionPopupContainer = styled(PopUp)` export const ModelSelectionPopup = observer(({ isOpen, onClose, getSelectedImageList}) => { - const {mlModels, mlModelsError, isErrorMlModels, isLoadingMlModels, refetchMlModels, removeMlModels } = + const {mlModels, mlModelsError, isErrorMlModels, isLoadingMlModels} = useMlModels(); const createAutoAnnotationJob = (model: MlModel) => { const imageSelection = getSelectedImageList(); - console.log({ - "images": imageSelection, - "modelName": model.name, - "modelVersion": model.version - - }); - axios.post(`${baseUrl}jobs`, { "images": imageSelection, "modelName": model.name, "modelVersion": model.version - }).then(function (response) { - console.log(response); - onClose && onClose(); - }) - .catch(function (error) { - console.log(error); - onClose && onClose(); - }); + }).then((_response) => onClose && onClose()) + .catch((_error) => onClose && onClose()); }; - + const { t } = useTranslation(); + return ( - + + {isLoadingMlModels && } + {isErrorMlModels && ( + {`${t("ml-models-loading-error")} ${ + mlModelsError?.response?.statusText + } (${mlModelsError?.response?.status})`} + )} {mlModels && ( - + )} ); diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 290a01df1..2ce956fdb 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -19,7 +19,7 @@ const StyledModal = styled(Modal)` width: 100%; `; -const datasetId = "2f7c3aaf-6008-4537-9ab2-3893b16a67f6"; +const datasetId = "3c0e0243-cabe-4bb4-aa50-369f1c4a8fc3"; export const DatasetScreen: React.FC = observer(() => { const { dataset, datasetError, isErrorDataset, isLoadingDataset } = diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index e74ce4f79..6561d40b5 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -60,3 +60,4 @@ export { ReactComponent as undo } from "./undo.svg"; export { ReactComponent as upArrow } from "./up-arrow.svg"; export { ReactComponent as whoAI } from "./who-ai.svg"; export { ReactComponent as xSmall } from "./x-small.svg"; +export { ReactComponent as mlAutoAnnotation } from "./ml-auto-annotation.svg"; \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/ml-auto-annotation.svg b/libs/ui-shared/src/lib/components/icon/icons/ml-auto-annotation.svg new file mode 100644 index 000000000..143a377c2 --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/ml-auto-annotation.svg @@ -0,0 +1 @@ + \ No newline at end of file From 266b7dfd25165ffc9bab8f110f621251fab9db99 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Mon, 30 Jan 2023 12:58:46 +0100 Subject: [PATCH 037/500] refactor: use react-query dev tools in development-mode --- apps/editor/src/app/app.tsx | 4 ++++ apps/editor/src/screens/dataset-screen.tsx | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index a9b17e7c7..14688fe3e 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -11,6 +11,7 @@ import Amplify from "aws-amplify"; import { observer } from "mobx-react-lite"; import React, { useEffect, useMemo, useRef, useState } from "react"; import { QueryClient, QueryClientProvider } from "react-query"; +import { ReactQueryDevtools } from "react-query/devtools"; import { Route, Routes } from "react-router-dom"; import { @@ -89,6 +90,9 @@ function App(): JSX.Element { )} + {process.env.NODE_ENV === "development" && ( + + )} ); diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 290a01df1..17a71c035 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,7 +1,6 @@ import { Box, Modal, Screen, Text, useTranslation } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; -import { ReactQueryDevtools } from "react-query/devtools"; import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; @@ -50,7 +49,6 @@ export const DatasetScreen: React.FC = observer(() => { )} {dataset && }
- {false && }
); }); From 86e7d8f1b4c7ef8609db07e3685f26c954858833 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 31 Jan 2023 14:28:10 +0100 Subject: [PATCH 038/500] refactor: implement PR review feedback --- apps/editor/src/assets/de.json | 4 +- apps/editor/src/assets/en.json | 4 +- .../menu/dataset-modal/dataset-modal.tsx | 43 +++++---- .../menu/ml-model-list/ml-model-list-item.tsx | 36 +++---- .../menu/ml-model-list/ml-model-list.tsx | 8 +- .../ml-model-selection-popup.props.ts | 5 +- .../ml-model-selection-popup.tsx | 94 +++++++++---------- apps/editor/src/querys/index.ts | 3 +- apps/editor/src/querys/use-ml-models.tsx | 11 +-- .../src/lib/components/icon/icons/index.ts | 2 +- 10 files changed, 100 insertions(+), 110 deletions(-) diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index 5d36e5f04..a455f0935 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -282,6 +282,6 @@ "ml-model-selection-description": "Wähle das Modell mit dem die Bilder annotiert werden sollen", "ml-model-selection-title": "Modell Auswahl", - "ml-models-loading": "Modelle werden geladen", - "ml-models-loading-error": "Fehler beim Laden der Modelle" + "ml-models-loading": "Modelle werden geladen...", + "ml-models-loading-error": "Fehler beim Laden der Modelle:" } diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index f4240af0c..3f2073e15 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -282,6 +282,6 @@ "ml-model-selection-description": "Select the model you want to use to annotate the images", "ml-model-selection-title": "Model Selection", - "ml-models-loading": "models loading", - "ml-models-loading-error": "Error on loading ml models" + "ml-models-loading": "models loading...", + "ml-models-loading-error": "Error on loading ml models:" } diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index 871daec3e..bf7ad19b1 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -6,7 +6,7 @@ import { useImagesBy } from "../../../querys"; import { Dataset } from "../../../types"; import { DatasetImageList } from "../dataset-image-list"; import { DatasetNavigationbar } from "../dataset-navigationbar"; -import {ModelSelectionPopup} from "../ml-model-popup"; +import { ModelSelectionPopup } from "../ml-model-popup"; const StyledModal = styled(Modal)` vertical-align: middle; @@ -26,13 +26,14 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { ); // model selection popup - const [isModelSelectionPopUpOpen, setIsModelSelectionPopUpOpen] = useState(false); - const openModelSelectionPopUp = useCallback(() => { - setIsModelSelectionPopUpOpen(true); - }, []); - const closeModelSelectionPopUp = useCallback(() => { - setIsModelSelectionPopUpOpen(false); - }, []); + const [isModelSelectionPopUpOpen, setIsModelSelectionPopUpOpen] = + useState(false); + const openModelSelectionPopUp = useCallback(() => { + setIsModelSelectionPopUpOpen(true); + }, []); + const closeModelSelectionPopUp = useCallback(() => { + setIsModelSelectionPopUpOpen(false); + }, []); // sync selectedImages and images array useEffect(() => { @@ -77,18 +78,21 @@ export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { [selectedImages], ); + const activeImageSelection = useMemo( + () => + [...selectedImages.keys()].filter((imageId) => + selectedImages.get(imageId), + ), + [selectedImages], + ); + const toggleSelectAll = useCallback( () => setSelectAll(!areAllSelected), [areAllSelected, setSelectAll], ); - const getSelectedImageList = () => { - const imageSelection = [...selectedImages.keys()].filter((image) => selectedImages.get(image)); - return imageSelection; - }; - const { t: translate } = useTranslation(); - + return ( { setSelection={setSelection} /> )} - + - ); }; diff --git a/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx b/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx index e5aaf4730..01ec88ff9 100644 --- a/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx +++ b/apps/editor/src/components/menu/ml-model-list/ml-model-list-item.tsx @@ -1,9 +1,4 @@ -import { - FlexRow, - InvisibleButton, - ListItem, - Text, -} from "@visian/ui-shared"; +import { FlexRow, InvisibleButton, ListItem, Text } from "@visian/ui-shared"; import styled from "styled-components"; import { MlModel } from "../../../types"; @@ -12,10 +7,6 @@ const Spacer = styled.div` width: 10px; `; -const ExpandedSpacer = styled.div` - margin-right: auto; -`; - const InfoButton = styled(InvisibleButton)` width: 18px; `; @@ -26,6 +17,10 @@ const VersionText = styled(Text)` margin: auto; `; +const ModelFlexRow = styled(FlexRow)` + margin-right: auto; + cursor: pointer; +`; export const ModelListItem = ({ model, @@ -34,15 +29,12 @@ export const ModelListItem = ({ model: MlModel; createAutoAnnotationJob: () => void; }) => ( - - - {model.name} - - {`v${model.version}`} - - - - - ); + + + {model.name} + + {`v${model.version}`} + + + +); diff --git a/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx b/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx index a9eaaadac..bc58da863 100644 --- a/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx +++ b/apps/editor/src/components/menu/ml-model-list/ml-model-list.tsx @@ -12,16 +12,16 @@ const ModelsList = styled(List)` export const MlModelList = ({ models, - createAutoAnnotationJob -} : { + createAutoAnnotationJob, +}: { models: MlModel[]; - createAutoAnnotationJob: (model: MlModel) => void + createAutoAnnotationJob: (model: MlModel) => void; }) => ( {models.map((model: MlModel) => ( createAutoAnnotationJob(model)} + createAutoAnnotationJob={() => createAutoAnnotationJob(model)} key={`${model.name}${model.version}`} /> ))} diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts index 85b10c34e..9a2d932c8 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts @@ -1,6 +1,5 @@ import type { StatefulPopUpProps } from "@visian/ui-shared"; - -export interface ModelPopUpProps extends StatefulPopUpProps{ - getSelectedImageList: () => void; +export interface ModelPopUpProps extends StatefulPopUpProps { + activeImageSelection: string[]; } diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx index 2913b111b..77f772d43 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx @@ -1,71 +1,67 @@ -/* eslint-disable max-len */ -import { - PopUp, - Text, - useTranslation, -} from "@visian/ui-shared"; +import { PopUp, Text, useTranslation } from "@visian/ui-shared"; import axios from "axios"; import { observer } from "mobx-react-lite"; import styled from "styled-components"; - +import { useMlModels } from "../../../querys"; import { baseUrl } from "../../../querys/base-url"; -import useMlModels from "../../../querys/use-ml-models"; import { MlModel } from "../../../types"; import { MlModelList } from "../ml-model-list"; import { ModelPopUpProps } from "./ml-model-selection-popup.props"; - const SectionLabel = styled(Text)` font-size: 14px; margin-bottom: 8px; `; - const ModelSelectionPopupContainer = styled(PopUp)` align-items: left; width: 400px; `; -export const ModelSelectionPopup = observer(({ isOpen, onClose, getSelectedImageList}) => { - - const {mlModels, mlModelsError, isErrorMlModels, isLoadingMlModels} = - useMlModels(); - - - const createAutoAnnotationJob = (model: MlModel) => { - const imageSelection = getSelectedImageList(); +export const ModelSelectionPopup = observer( + ({ isOpen, onClose, activeImageSelection }) => { + const { mlModels, mlModelsError, isErrorMlModels, isLoadingMlModels } = + useMlModels(); - axios.post(`${baseUrl}jobs`, { - "images": imageSelection, - "modelName": model.name, - "modelVersion": model.version + const createAutoAnnotationJob = async (model: MlModel) => { + try { + await axios.post(`${baseUrl}jobs`, { + images: activeImageSelection, + modelName: model.name, + modelVersion: model.version, + }); + // eslint-disable-next-line no-unused-expressions + onClose && onClose(); + } catch (error: any) { + // eslint-disable-next-line no-unused-expressions + onClose && onClose(); + } + }; - }).then((_response) => onClose && onClose()) - .catch((_error) => onClose && onClose()); - }; + const { t } = useTranslation(); - const { t } = useTranslation(); - - return ( - - - {isLoadingMlModels && } - {isErrorMlModels && ( - {`${t("ml-models-loading-error")} ${ - mlModelsError?.response?.statusText - } (${mlModelsError?.response?.status})`} - )} - {mlModels && ( - - )} - - ); -}); + return ( + + + {isLoadingMlModels && } + {isErrorMlModels && ( + {`${t("ml-models-loading-error")} ${ + mlModelsError?.response?.statusText + } (${mlModelsError?.response?.status})`} + )} + {mlModels && ( + + )} + + ); + }, +); diff --git a/apps/editor/src/querys/index.ts b/apps/editor/src/querys/index.ts index 2ced3fc5a..94667f858 100644 --- a/apps/editor/src/querys/index.ts +++ b/apps/editor/src/querys/index.ts @@ -1,3 +1,4 @@ +export * from "./use-annotations-by"; export * from "./use-dataset"; export * from "./use-images-by"; -export * from "./use-annotations-by"; +export * from "./use-ml-models"; diff --git a/apps/editor/src/querys/use-ml-models.tsx b/apps/editor/src/querys/use-ml-models.tsx index 172e7cb62..d757e660b 100644 --- a/apps/editor/src/querys/use-ml-models.tsx +++ b/apps/editor/src/querys/use-ml-models.tsx @@ -1,22 +1,21 @@ import axios, { AxiosError } from "axios"; import { useQuery } from "react-query"; -import { Image, MlModel } from "../types"; +import { MlModel } from "../types"; import { baseUrl } from "./base-url"; const getModelVersions = async () => { - const modelsResponse = await axios.get(`${baseUrl}model-versions`, { - }); + const modelsResponse = await axios.get(`${baseUrl}model-versions`); return modelsResponse.data; }; export const useMlModels = () => { const { data, error, isError, isLoading, refetch, remove } = useQuery< MlModel[], - AxiosError - >(["mlModels"], () => getModelVersions(), { + AxiosError + >(["mlModels"], getModelVersions, { retry: 2, // retry twice if fetch fails - refetchInterval: 1000 * 60, // refetch every 10 seconds + refetchInterval: 1000 * 60, // refetch every 60 seconds }); return { diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index 6561d40b5..74fa3f846 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -33,6 +33,7 @@ export { ReactComponent as listView } from "./list-view.svg"; export { ReactComponent as magicBrush } from "./magic-brush.svg"; export { ReactComponent as menu } from "./menu.svg"; export { ReactComponent as middleMouse } from "./middle-mouse.svg"; +export { ReactComponent as mlAutoAnnotation } from "./ml-auto-annotation.svg"; export { ReactComponent as navigationTool } from "./navigation-tool.svg"; export { ReactComponent as outline } from "./outline.svg"; export { ReactComponent as outlineEraser } from "./outline-eraser.svg"; @@ -60,4 +61,3 @@ export { ReactComponent as undo } from "./undo.svg"; export { ReactComponent as upArrow } from "./up-arrow.svg"; export { ReactComponent as whoAI } from "./who-ai.svg"; export { ReactComponent as xSmall } from "./x-small.svg"; -export { ReactComponent as mlAutoAnnotation } from "./ml-auto-annotation.svg"; \ No newline at end of file From 6e3af96cd08adcd359ee686ed4c2fbbd8f13be41 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 31 Jan 2023 14:42:15 +0100 Subject: [PATCH 039/500] fix: use hub base url specified in env --- .../menu/ml-model-popup/ml-model-selection-popup.tsx | 4 ++-- apps/editor/src/querys/use-ml-models.tsx | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx index 77f772d43..8f15dc9a3 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite"; import styled from "styled-components"; import { useMlModels } from "../../../querys"; -import { baseUrl } from "../../../querys/base-url"; +import { hubBaseUrl } from "../../../querys/hub-base-url"; import { MlModel } from "../../../types"; import { MlModelList } from "../ml-model-list"; import { ModelPopUpProps } from "./ml-model-selection-popup.props"; @@ -26,7 +26,7 @@ export const ModelSelectionPopup = observer( const createAutoAnnotationJob = async (model: MlModel) => { try { - await axios.post(`${baseUrl}jobs`, { + await axios.post(`${hubBaseUrl}jobs`, { images: activeImageSelection, modelName: model.name, modelVersion: model.version, diff --git a/apps/editor/src/querys/use-ml-models.tsx b/apps/editor/src/querys/use-ml-models.tsx index d757e660b..3423916c0 100644 --- a/apps/editor/src/querys/use-ml-models.tsx +++ b/apps/editor/src/querys/use-ml-models.tsx @@ -2,10 +2,12 @@ import axios, { AxiosError } from "axios"; import { useQuery } from "react-query"; import { MlModel } from "../types"; -import { baseUrl } from "./base-url"; +import { hubBaseUrl } from "./hub-base-url"; const getModelVersions = async () => { - const modelsResponse = await axios.get(`${baseUrl}model-versions`); + const modelsResponse = await axios.get( + `${hubBaseUrl}model-versions`, + ); return modelsResponse.data; }; From 3efcf04874c68dff91746016e1bcb02798fcd495 Mon Sep 17 00:00:00 2001 From: konrad-gerlach Date: Thu, 2 Feb 2023 16:06:11 +0100 Subject: [PATCH 040/500] Feat: Add opening images and annotations from backend in editor Co-authored-by: Daniel dJ --- .../editor/ui-overlay/ui-overlay.tsx | 60 +++++++++++++++++++ .../dataset-image-list-item.tsx | 10 +++- .../src/components/menu/util/openInEditor.tsx | 19 ++++++ apps/editor/src/querys/use-files.tsx | 32 ++++++++++ package.json | 3 +- yarn.lock | 11 ++++ 6 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 apps/editor/src/components/menu/util/openInEditor.tsx create mode 100644 apps/editor/src/querys/use-files.tsx diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index 6450a6e8d..8863179cc 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -7,12 +7,15 @@ import { Text, } from "@visian/ui-shared"; import { isFromWHO } from "@visian/utils"; +import { fetchAnnotation, fetchImage } from "apps/editor/src/querys/use-files"; import { observer } from "mobx-react-lite"; import React, { useCallback, useEffect, useRef, useState } from "react"; +import { useSearchParams } from "react-router-dom"; import styled from "styled-components"; import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; +import { Annotation, Image } from "../../../types"; import { DilateErodeModal, MeasurementModal, @@ -193,6 +196,63 @@ export const UIOverlay = observer( }); }, [store]); + // Displaying images and annotations from Backend + const loadSessionStorage = (key: string) => { + let object = null; + const objectJSON = sessionStorage.getItem(key); + if (objectJSON) { + object = JSON.parse(objectJSON); + } + return object; + }; + + const openImageInEditor = async (image: Image) => { + const imageFile = await fetchImage(image); + store?.editor.activeDocument?.importFiles( + imageFile, + imageFile.name, + false, + ); + }; + const openAnnotationInEditor = async (annotation: Annotation) => { + const annotationFile = await fetchAnnotation(annotation); + store?.editor.activeDocument?.importFiles( + annotationFile, + annotationFile.name, + true, + ); + }; + const [openedImage, setOpenedImage] = useState(null); + const [openedAnnotation, setOpenedAnnotation] = useState( + null, + ); + + useEffect(() => { + if (openedImage) { + openImageInEditor(openedImage); + } + }, [openedImage]); + + useEffect(() => { + if (openedAnnotation) { + openAnnotationInEditor(openedAnnotation); + } + }, [openedAnnotation]); + const [searchParams] = useSearchParams(); + const loadImagesAndAnnotations = () => { + const openImage = searchParams.get("openImage"); + if (openImage) { + setOpenedImage(loadSessionStorage("ImageToOpen")); + } + const openAnnotation = searchParams.get("openAnnotation"); + + if (openAnnotation) { + setOpenedAnnotation(loadSessionStorage("AnnotationToOpen")); + } + }; + + useEffect(loadImagesAndAnnotations, [searchParams]); + return ( {annotations.map((annotation: Annotation) => ( - {annotation.dataUri} + { + openInEditor(annotation); + }} + key={annotation.id} + > + {annotation.dataUri} + ))}
) diff --git a/apps/editor/src/components/menu/util/openInEditor.tsx b/apps/editor/src/components/menu/util/openInEditor.tsx new file mode 100644 index 000000000..7464899ef --- /dev/null +++ b/apps/editor/src/components/menu/util/openInEditor.tsx @@ -0,0 +1,19 @@ +import { Annotation, Image } from "../../../types"; + +export function openInEditor( + image: Image | null, + annotation: Annotation | null, +) { + // save image id in local storage + const query: string[] = []; + if (image) { + sessionStorage.setItem("ImageToOpen", JSON.stringify(image)); + query.push("openImage=true"); + } + if (annotation) { + sessionStorage.setItem("AnnotationToOpen", JSON.stringify(annotation)); + query.push("openAnnotation=true"); + } + // redirect to editor page + window.location.assign(`/editor?${query.join("&")}`); +} diff --git a/apps/editor/src/querys/use-files.tsx b/apps/editor/src/querys/use-files.tsx new file mode 100644 index 000000000..77b8ad7c8 --- /dev/null +++ b/apps/editor/src/querys/use-files.tsx @@ -0,0 +1,32 @@ +import path from "path"; +import hubBaseUrl from "./hub-base-url"; +import { Annotation, Image } from "../types"; + +const fetchFile = async ( + id: string, + endpoint: string, + fileName: string, +): Promise => + fetch(`${hubBaseUrl}${endpoint}/${id}/file`, { + method: "GET", + }) + .then((response) => response.blob()) + .then( + (blob) => + new File([blob], fileName, { + type: blob.type, + lastModified: Date.now(), + }), + ); + +export const fetchImage = async (image: Image): Promise => { + const fileName: string = path.basename(image.dataUri); + return fetchFile(image.id, "images", fileName); +}; + +export const fetchAnnotation = async ( + annotation: Annotation, +): Promise => { + const fileName: string = path.basename(annotation.dataUri); + return fetchFile(annotation.id, "annotations", fileName); +}; diff --git a/package.json b/package.json index e39da2bd6..90bb22589 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,8 @@ "tslib": "^2.3.0", "uuid": "^9.0.0", "webxr-polyfill": "^2.0.3", - "worker-rpc": "^0.2.0" + "worker-rpc": "^0.2.0", + "yarn": "^1.22.19" }, "resolutions": { "@types/hoist-non-react-statics/@types/react": "17.0.30", diff --git a/yarn.lock b/yarn.lock index dbc23dbd2..e7e53e151 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24226,6 +24226,7 @@ __metadata: webxr-polyfill: ^2.0.3 worker-loader: ^3.0.8 worker-rpc: ^0.2.0 + yarn: ^1.22.19 languageName: unknown linkType: soft @@ -25104,6 +25105,16 @@ __metadata: languageName: node linkType: hard +"yarn@npm:^1.22.19": + version: 1.22.19 + resolution: "yarn@npm:1.22.19" + bin: + yarn: bin/yarn.js + yarnpkg: bin/yarn.js + checksum: b43d2cc5fee7e933beb12a8aee7dfceca9e9ef2dd17c5d04d15a12ab7bec5f5744ea34a07b86e013da7f291a18c4e1ad8f70e150f5ed2f4666e6723c7f0a8452 + languageName: node + linkType: hard + "yauzl@npm:^2.10.0": version: 2.10.0 resolution: "yauzl@npm:2.10.0" From 1771e187dea4374be21959f79653181497be72c1 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Thu, 2 Feb 2023 17:11:43 +0100 Subject: [PATCH 041/500] feat: clicking files or annotations opens them in editor --- .../dataset-image-list-item.tsx | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index 478db3d30..f1feefd31 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -29,6 +29,10 @@ const AnnotationsList = styled(List)` width: calc(100% - 30px); `; +const ClickableText = styled(Text)` + cursor: pointer; +`; + export const DatasetImageListItem = ({ isInSelectMode, image, @@ -79,7 +83,13 @@ export const DatasetImageListItem = ({ )} - {image.dataUri} + { + openInEditor(image, null); + }} + > + {image.dataUri} + {annotations.map((annotation: Annotation) => ( - { - openInEditor(annotation); - }} - key={annotation.id} - > - {annotation.dataUri} + + { + openInEditor(image, annotation); + }} + > + {annotation.dataUri} + ))} From d5d5cdaa5f31497c05bfbb2088bddd7fc5a6f311 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Thu, 2 Feb 2023 17:45:14 +0100 Subject: [PATCH 042/500] build: yarn dev starts visian with default hub url --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index e39da2bd6..8e94536fc 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "prepare": "husky install", "start": "nx serve", + "dev": "NX_ANNOTATION_SERVICE_HUB_URL=\"localhost:3000\" nx serve", "format": "nx format:write", "lint": "nx workspace-lint && nx lint", "lint:all": "yarn nx run-many --target=lint --all", From 83bd0d0d8548087ed9932c8f1df4c41a2f1a08e7 Mon Sep 17 00:00:00 2001 From: konrad-gerlach Date: Thu, 2 Feb 2023 20:01:20 +0100 Subject: [PATCH 043/500] feat: Improve importing and clear editor before importing Co-authored-by: Daniel dJ --- .vscode/settings.json | 3 +- .../editor/ui-overlay/ui-overlay.tsx | 66 +++++++++---------- apps/editor/src/models/root.ts | 14 ++-- apps/editor/src/screens/dataset-screen.tsx | 2 +- 4 files changed, 41 insertions(+), 44 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 03d614193..41946d757 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,5 +21,6 @@ }, "[dockerfile]": { "editor.defaultFormatter": "ms-azuretools.vscode-docker" - } + }, + "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }] } diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index 8863179cc..4cfcd45ef 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -15,6 +15,7 @@ import styled from "styled-components"; import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; +import { importFilesToDocument } from "../../../import-handling"; import { Annotation, Image } from "../../../types"; import { DilateErodeModal, @@ -205,50 +206,47 @@ export const UIOverlay = observer( } return object; }; - const openImageInEditor = async (image: Image) => { const imageFile = await fetchImage(image); - store?.editor.activeDocument?.importFiles( - imageFile, - imageFile.name, - false, - ); }; const openAnnotationInEditor = async (annotation: Annotation) => { const annotationFile = await fetchAnnotation(annotation); - store?.editor.activeDocument?.importFiles( - annotationFile, - annotationFile.name, - true, - ); - }; - const [openedImage, setOpenedImage] = useState(null); - const [openedAnnotation, setOpenedAnnotation] = useState( - null, - ); - - useEffect(() => { - if (openedImage) { - openImageInEditor(openedImage); + const dT = new DataTransfer(); + dT.items.add(annotationFile); + if (store) { + importFilesToDocument(dT.files, store); } - }, [openedImage]); + }; - useEffect(() => { - if (openedAnnotation) { - openAnnotationInEditor(openedAnnotation); - } - }, [openedAnnotation]); const [searchParams] = useSearchParams(); const loadImagesAndAnnotations = () => { - const openImage = searchParams.get("openImage"); - if (openImage) { - setOpenedImage(loadSessionStorage("ImageToOpen")); - } - const openAnnotation = searchParams.get("openAnnotation"); - - if (openAnnotation) { - setOpenedAnnotation(loadSessionStorage("AnnotationToOpen")); + async function asyncfunc() { + if (store?.editor.activeDocument?.layers.length != 0) { + return await store?.destroy(); + } + const openImage = searchParams.get("openImage"); + const dT = new DataTransfer(); + let shouldImport = false; + if (openImage && sessionStorage.getItem("ImageToOpen")) { + const image = loadSessionStorage("ImageToOpen"); + sessionStorage.removeItem("ImageToOpen"); + const imageFile = await fetchImage(image); + dT.items.add(imageFile); + shouldImport = true; + } + const openAnnotation = searchParams.get("openAnnotation"); + if (openAnnotation && sessionStorage.getItem("AnnotationToOpen")) { + const annotation = loadSessionStorage("AnnotationToOpen"); + sessionStorage.removeItem("AnnotationToOpen"); + const annotationFile = await fetchAnnotation(annotation); + dT.items.add(annotationFile); + shouldImport = true; + } + if (store && shouldImport) { + importFilesToDocument(dT.files, store); + } } + asyncfunc(); }; useEffect(loadImagesAndAnnotations, [searchParams]); diff --git a/apps/editor/src/models/root.ts b/apps/editor/src/models/root.ts index 1e200095e..1af85feee 100644 --- a/apps/editor/src/models/root.ts +++ b/apps/editor/src/models/root.ts @@ -313,24 +313,22 @@ export class RootStore implements ISerializable, IDisposable { this.shouldPersist = true; } - public destroy = async (forceDestroy?: boolean) => { - if (!this.shouldPersist && !forceDestroy) return; + public destroy = async (forceDestroy?: boolean): Promise => { + if (!this.shouldPersist && !forceDestroy) return false; if ( !forceDestroy && // eslint-disable-next-line no-alert !window.confirm(i18n.t("erase-application-data-confirmation")) ) - return; + return false; this.shouldPersist = false; localStorage.clear(); await this.config.storageBackend?.clear(); this.setIsDirty(false, true); - window.location.href = new URL(window.location.href).searchParams.has( - "tracking", - ) - ? `${window.location.pathname}?tracking` - : window.location.pathname; + let redirectURl = new URL(window.location.href); + window.location.href = redirectURl.href; + return true; }; } diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 95360ad37..cc06850e3 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -18,7 +18,7 @@ const StyledModal = styled(Modal)` width: 100%; `; -const datasetId = "3c0e0243-cabe-4bb4-aa50-369f1c4a8fc3"; +const datasetId = "ed74a5b0-bb97-4f92-9bc9-50d47fafc17f"; export const DatasetScreen: React.FC = observer(() => { const { dataset, datasetError, isErrorDataset, isLoadingDataset } = From c51bbe94f9b66f8c753ce6c32826893c277105fd Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Thu, 2 Feb 2023 21:23:32 +0100 Subject: [PATCH 044/500] build: use yarn start:hub instead of yarn dev --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e94536fc..59caa611a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "prepare": "husky install", "start": "nx serve", - "dev": "NX_ANNOTATION_SERVICE_HUB_URL=\"localhost:3000\" nx serve", + "start:hub": "NX_ANNOTATION_SERVICE_HUB_URL=\"localhost:3000\" nx serve", "format": "nx format:write", "lint": "nx workspace-lint && nx lint", "lint:all": "yarn nx run-many --target=lint --all", From 15b2d990da4f2482655f5cfe1d38405b6a9d70f8 Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Thu, 2 Feb 2023 21:24:18 +0100 Subject: [PATCH 045/500] refactor: document yarn start:hub --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b7d0e1932..318d76356 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ Omitting an app name starts the default app. After running this command, the app will be available at the URL printed in the console.
The app will automatically reload if you change any of the source files. +It is possible to use Visian with the annotation-service backend. The location of the backend is set via the enviorment variable `NX_ANNOTATION_SERVICE_HUB_URL`. Assuming the backend is located at `localhost:3000` you can use the shortcut `yarn start:hub`. + ### `yarn format []` Runs automated code formatting on all applicable file types. From ac83f82f1d1421d6fcccecf481e7ed983f055757 Mon Sep 17 00:00:00 2001 From: konrad-gerlach Date: Fri, 3 Feb 2023 12:21:43 +0100 Subject: [PATCH 046/500] refactor: please the linter --- apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx | 6 +++--- apps/editor/src/models/root.ts | 2 +- apps/editor/src/querys/use-files.tsx | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index 4cfcd45ef..ca5806449 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -7,7 +7,6 @@ import { Text, } from "@visian/ui-shared"; import { isFromWHO } from "@visian/utils"; -import { fetchAnnotation, fetchImage } from "apps/editor/src/querys/use-files"; import { observer } from "mobx-react-lite"; import React, { useCallback, useEffect, useRef, useState } from "react"; import { useSearchParams } from "react-router-dom"; @@ -16,6 +15,7 @@ import styled from "styled-components"; import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; import { importFilesToDocument } from "../../../import-handling"; +import { fetchAnnotation, fetchImage } from "../../../querys/use-files"; import { Annotation, Image } from "../../../types"; import { DilateErodeModal, @@ -221,8 +221,8 @@ export const UIOverlay = observer( const [searchParams] = useSearchParams(); const loadImagesAndAnnotations = () => { async function asyncfunc() { - if (store?.editor.activeDocument?.layers.length != 0) { - return await store?.destroy(); + if (store?.editor.activeDocument?.layers.length !== 0) { + return store?.destroy(); } const openImage = searchParams.get("openImage"); const dT = new DataTransfer(); diff --git a/apps/editor/src/models/root.ts b/apps/editor/src/models/root.ts index 1af85feee..75edea583 100644 --- a/apps/editor/src/models/root.ts +++ b/apps/editor/src/models/root.ts @@ -327,7 +327,7 @@ export class RootStore implements ISerializable, IDisposable { await this.config.storageBackend?.clear(); this.setIsDirty(false, true); - let redirectURl = new URL(window.location.href); + const redirectURl = new URL(window.location.href); window.location.href = redirectURl.href; return true; }; diff --git a/apps/editor/src/querys/use-files.tsx b/apps/editor/src/querys/use-files.tsx index 77b8ad7c8..559cd4e20 100644 --- a/apps/editor/src/querys/use-files.tsx +++ b/apps/editor/src/querys/use-files.tsx @@ -1,6 +1,7 @@ import path from "path"; -import hubBaseUrl from "./hub-base-url"; + import { Annotation, Image } from "../types"; +import hubBaseUrl from "./hub-base-url"; const fetchFile = async ( id: string, From f1ad4bf1825eab955925febf1bc0a85396c3fb5a Mon Sep 17 00:00:00 2001 From: konrad-gerlach Date: Fri, 3 Feb 2023 13:57:30 +0100 Subject: [PATCH 047/500] fix: readd old destroy method with new destroyReload feature --- .../editor/ui-overlay/ui-overlay.tsx | 2 +- apps/editor/src/models/root.ts | 23 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index ca5806449..06ff56087 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -222,7 +222,7 @@ export const UIOverlay = observer( const loadImagesAndAnnotations = () => { async function asyncfunc() { if (store?.editor.activeDocument?.layers.length !== 0) { - return store?.destroy(); + return store?.destroyReload(); } const openImage = searchParams.get("openImage"); const dT = new DataTransfer(); diff --git a/apps/editor/src/models/root.ts b/apps/editor/src/models/root.ts index 75edea583..b9cdd06ca 100644 --- a/apps/editor/src/models/root.ts +++ b/apps/editor/src/models/root.ts @@ -313,7 +313,7 @@ export class RootStore implements ISerializable, IDisposable { this.shouldPersist = true; } - public destroy = async (forceDestroy?: boolean): Promise => { + private destroyLayers = async (forceDestroy?: boolean): Promise => { if (!this.shouldPersist && !forceDestroy) return false; if ( !forceDestroy && @@ -327,8 +327,25 @@ export class RootStore implements ISerializable, IDisposable { await this.config.storageBackend?.clear(); this.setIsDirty(false, true); - const redirectURl = new URL(window.location.href); - window.location.href = redirectURl.href; return true; }; + public destroy = async (forceDestroy?: boolean): Promise => { + if (await this.destroyLayers(forceDestroy)) { + window.location.href = new URL(window.location.href).searchParams.has( + "tracking", + ) + ? `${window.location.pathname}?tracking` + : window.location.pathname; + return true; + } + return false; + }; + public destroyReload = async (forceDestroy?: boolean): Promise => { + if (await this.destroyLayers(forceDestroy)) { + const redirectURl = new URL(window.location.href); + window.location.href = redirectURl.href; + return true; + } + return false; + }; } From c1003336f6334496b7b15292ea3cf5d5993e8feb Mon Sep 17 00:00:00 2001 From: konrad-gerlach Date: Fri, 3 Feb 2023 14:00:37 +0100 Subject: [PATCH 048/500] refactor: remove dead code --- .../src/components/editor/ui-overlay/ui-overlay.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index 06ff56087..df7df7a72 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -16,7 +16,6 @@ import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; import { importFilesToDocument } from "../../../import-handling"; import { fetchAnnotation, fetchImage } from "../../../querys/use-files"; -import { Annotation, Image } from "../../../types"; import { DilateErodeModal, MeasurementModal, @@ -206,17 +205,6 @@ export const UIOverlay = observer( } return object; }; - const openImageInEditor = async (image: Image) => { - const imageFile = await fetchImage(image); - }; - const openAnnotationInEditor = async (annotation: Annotation) => { - const annotationFile = await fetchAnnotation(annotation); - const dT = new DataTransfer(); - dT.items.add(annotationFile); - if (store) { - importFilesToDocument(dT.files, store); - } - }; const [searchParams] = useSearchParams(); const loadImagesAndAnnotations = () => { From ed5df8018368a8de6587c177e4bf0eded4d3c1f5 Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Fri, 3 Feb 2023 14:09:33 +0100 Subject: [PATCH 049/500] feat: add job history skeleton --- apps/editor/src/app/app.tsx | 3 +- .../src/components/menu/job-history/index.ts | 1 + .../menu/job-history/job-history.tsx | 35 +++++++++++++++++++ .../menu/job-history/job-list-item.tsx | 20 +++++++++++ .../components/menu/job-history/job-list.tsx | 19 ++++++++++ apps/editor/src/querys/index.ts | 1 + apps/editor/src/querys/use-jobs.tsx | 31 ++++++++++++++++ apps/editor/src/screens/index.ts | 1 + apps/editor/src/screens/project-screen.tsx | 34 ++++++++++++++++++ apps/editor/src/types/index.ts | 1 + apps/editor/src/types/jobs-history-types.tsx | 8 +++++ 11 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 apps/editor/src/components/menu/job-history/index.ts create mode 100644 apps/editor/src/components/menu/job-history/job-history.tsx create mode 100644 apps/editor/src/components/menu/job-history/job-list-item.tsx create mode 100644 apps/editor/src/components/menu/job-history/job-list.tsx create mode 100644 apps/editor/src/querys/use-jobs.tsx create mode 100644 apps/editor/src/screens/project-screen.tsx create mode 100644 apps/editor/src/types/jobs-history-types.tsx diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index 14688fe3e..21595d7d8 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -22,7 +22,7 @@ import { import { setUpEventHandling } from "../event-handling"; import type { RootStore } from "../models"; import hubBaseUrl from "../querys/hub-base-url"; -import { DatasetScreen, EditorScreen } from "../screens"; +import { DatasetScreen, EditorScreen, ProjectScreen } from "../screens"; import { setupRootStore, StoreProvider } from "./root-store"; if (isFromWHO()) { @@ -81,6 +81,7 @@ function App(): JSX.Element { } /> } /> + } /> ) : ( diff --git a/apps/editor/src/components/menu/job-history/index.ts b/apps/editor/src/components/menu/job-history/index.ts new file mode 100644 index 000000000..f67b61802 --- /dev/null +++ b/apps/editor/src/components/menu/job-history/index.ts @@ -0,0 +1 @@ +export * from "./job-history"; diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx new file mode 100644 index 000000000..de572a1e4 --- /dev/null +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -0,0 +1,35 @@ +import { Modal, Text, useTranslation } from "@visian/ui-shared"; +import styled from "styled-components"; + +import { useJobs } from "../../../querys"; +import { JobList } from "./job-list"; + +const StyledModal = styled(Modal)` + vertical-align: middle; + width: 100%; + z-index: 49; +`; + +export const JobHistory = () => { + const { jobs, jobsError, isErrorJobs, isLoadingJobs } = useJobs(); + + const { t: translate } = useTranslation(); + + return ( + + // } + > + {isLoadingJobs && } + {isErrorJobs && ( + {`${translate("images-loading-error")} ${ + jobsError?.response?.statusText + } (${jobsError?.response?.status})`} + )} + {jobs && } + + ); +}; diff --git a/apps/editor/src/components/menu/job-history/job-list-item.tsx b/apps/editor/src/components/menu/job-history/job-list-item.tsx new file mode 100644 index 000000000..aa4792455 --- /dev/null +++ b/apps/editor/src/components/menu/job-history/job-list-item.tsx @@ -0,0 +1,20 @@ +import { ListItem, Text } from "@visian/ui-shared"; +import styled from "styled-components"; + +import { Job } from "../../../types"; + +// const Spacer = styled.div` +// width: 10px; +// `; + +const ExpandedSpacer = styled.div` + margin-right: auto; +`; + +export const JobListItem = ({ job }: { job: Job }) => ( + + {job.modelName} + + {job.status} + +); diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-list.tsx new file mode 100644 index 000000000..15679f7a2 --- /dev/null +++ b/apps/editor/src/components/menu/job-history/job-list.tsx @@ -0,0 +1,19 @@ +import { List, stopPropagation } from "@visian/ui-shared"; +import styled from "styled-components"; + +import { Job } from "../../../types"; +import { JobListItem } from "./job-list-item"; + +const StyledList = styled(List)` + width: 100%; + height: 400px; + overflow-y: auto; +`; + +export const JobList = ({ jobs }: { jobs: Job[] }) => ( + + {jobs.map((job: Job) => ( + + ))} + +); diff --git a/apps/editor/src/querys/index.ts b/apps/editor/src/querys/index.ts index 94667f858..b431ca63e 100644 --- a/apps/editor/src/querys/index.ts +++ b/apps/editor/src/querys/index.ts @@ -2,3 +2,4 @@ export * from "./use-annotations-by"; export * from "./use-dataset"; export * from "./use-images-by"; export * from "./use-ml-models"; +export * from "./use-jobs"; diff --git a/apps/editor/src/querys/use-jobs.tsx b/apps/editor/src/querys/use-jobs.tsx new file mode 100644 index 000000000..cdd9b43b8 --- /dev/null +++ b/apps/editor/src/querys/use-jobs.tsx @@ -0,0 +1,31 @@ +import axios, { AxiosError } from "axios"; +import { useQuery } from "react-query"; + +import { Job } from "../types"; +import { hubBaseUrl } from "./hub-base-url"; + +const getJobs = async () => { + const jobsResponse = await axios.get(`${hubBaseUrl}jobs`); + return jobsResponse.data; +}; + +export const useJobs = () => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Job[], + AxiosError + >(["jobs"], getJobs, { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 20, // refetch every 20 seconds + }); + + return { + jobs: data, + jobsError: error, + isErrorJobs: isError, + isLoadingJobs: isLoading, + refetchJobs: refetch, + removeJobs: remove, + }; +}; + +export default useJobs; diff --git a/apps/editor/src/screens/index.ts b/apps/editor/src/screens/index.ts index 350d74fa8..8975e0d3c 100644 --- a/apps/editor/src/screens/index.ts +++ b/apps/editor/src/screens/index.ts @@ -1,2 +1,3 @@ export * from "./editor-screen"; export * from "./dataset-screen"; +export * from "./project-screen"; diff --git a/apps/editor/src/screens/project-screen.tsx b/apps/editor/src/screens/project-screen.tsx new file mode 100644 index 000000000..157b16614 --- /dev/null +++ b/apps/editor/src/screens/project-screen.tsx @@ -0,0 +1,34 @@ +import { Box, Modal, Screen, Text, useTranslation } from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import styled from "styled-components"; + +import { JobHistory } from "../components/menu/job-history"; + +const Main = styled(Box)` + display: flex; + justify-content: center; + height: 100%; + padding: 5rem 10rem; +`; + +const StyledModal = styled(Modal)` + vertical-align: middle; + width: 100%; +`; + +// const projectId = "3c0e0243-cabe-4bb4-aa50-369f1c4a8fc3"; + +export const ProjectScreen: React.FC = observer(() => { + const { t: translate } = useTranslation(); + + return ( + +
+ +
+
+ ); +}); + +export default ProjectScreen; diff --git a/apps/editor/src/types/index.ts b/apps/editor/src/types/index.ts index 5b699617c..3589b68de 100644 --- a/apps/editor/src/types/index.ts +++ b/apps/editor/src/types/index.ts @@ -1,2 +1,3 @@ export * from "./dataset-types"; export * from "./ml-model-types"; +export * from "./jobs-history-types"; diff --git a/apps/editor/src/types/jobs-history-types.tsx b/apps/editor/src/types/jobs-history-types.tsx new file mode 100644 index 000000000..b9026b8cf --- /dev/null +++ b/apps/editor/src/types/jobs-history-types.tsx @@ -0,0 +1,8 @@ +export interface Job { + id: string; + modelName: string; + modelVersion: string; + startedAt: string; + finishedAt: string; + status: string; +} From f1063d8b99f6426d0a956fd3de49e3a94b90bb89 Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Tue, 7 Feb 2023 10:16:19 +0100 Subject: [PATCH 050/500] feat: Add model version --- .../src/components/menu/job-history/job-history.tsx | 1 + .../src/components/menu/job-history/job-list-item.tsx | 8 +++++--- apps/editor/src/components/menu/job-history/job-list.tsx | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index de572a1e4..5d4ef1439 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -7,6 +7,7 @@ import { JobList } from "./job-list"; const StyledModal = styled(Modal)` vertical-align: middle; width: 100%; + height: 50%; z-index: 49; `; diff --git a/apps/editor/src/components/menu/job-history/job-list-item.tsx b/apps/editor/src/components/menu/job-history/job-list-item.tsx index aa4792455..7e3baab71 100644 --- a/apps/editor/src/components/menu/job-history/job-list-item.tsx +++ b/apps/editor/src/components/menu/job-history/job-list-item.tsx @@ -3,9 +3,9 @@ import styled from "styled-components"; import { Job } from "../../../types"; -// const Spacer = styled.div` -// width: 10px; -// `; +const Spacer = styled.div` + width: 10px; +`; const ExpandedSpacer = styled.div` margin-right: auto; @@ -14,6 +14,8 @@ const ExpandedSpacer = styled.div` export const JobListItem = ({ job }: { job: Job }) => ( {job.modelName} + + {job.modelVersion} {job.status} diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-list.tsx index 15679f7a2..1d11b12f7 100644 --- a/apps/editor/src/components/menu/job-history/job-list.tsx +++ b/apps/editor/src/components/menu/job-history/job-list.tsx @@ -6,7 +6,6 @@ import { JobListItem } from "./job-list-item"; const StyledList = styled(List)` width: 100%; - height: 400px; overflow-y: auto; `; From 400258390a600a4734914252ac7780096bc33eaf Mon Sep 17 00:00:00 2001 From: Janis Wehen Date: Tue, 7 Feb 2023 10:40:57 +0100 Subject: [PATCH 051/500] build: set host to 0.0.0.0 so node binds to all hosts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 59caa611a..5d8227a74 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "license": "MIT", "scripts": { "prepare": "husky install", - "start": "nx serve", - "start:hub": "NX_ANNOTATION_SERVICE_HUB_URL=\"localhost:3000\" nx serve", + "start": "nx serve --host 0.0.0.0", + "start:hub": "NX_ANNOTATION_SERVICE_HUB_URL=\"localhost:3000\" nx serve --host 0.0.0.0", "format": "nx format:write", "lint": "nx workspace-lint && nx lint", "lint:all": "yarn nx run-many --target=lint --all", From a9547891d4b3198ddac9f4b225bc0e0aa6b5c3e9 Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Tue, 7 Feb 2023 21:02:30 +0100 Subject: [PATCH 052/500] feat: Add projects and projects lists --- apps/editor/src/app/app.tsx | 25 +++++++-- apps/editor/src/assets/de.json | 8 +++ apps/editor/src/assets/en.json | 8 +++ .../menu/dataset-list/dataset-list-item.tsx | 32 ++++++++++++ .../menu/dataset-list/dataset-list.tsx | 19 +++++++ .../src/components/menu/dataset-list/index.ts | 2 + .../src/components/menu/project-tab/index.ts | 1 + .../menu/projects-list/project-list-item.tsx | 29 +++++++++++ .../menu/projects-list/project-list.tsx | 19 +++++++ apps/editor/src/queries/hub-base-url.tsx | 20 ++++++++ apps/editor/src/screens/dataset-screen.tsx | 7 +-- apps/editor/src/screens/index.ts | 2 + apps/editor/src/screens/project-screen.tsx | 51 +++++++++++++++++++ apps/editor/src/screens/projects-screen.tsx | 45 ++++++++++++++++ apps/editor/src/types/index.ts | 1 + apps/editor/src/types/project-types.tsx | 6 +++ 16 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx create mode 100644 apps/editor/src/components/menu/dataset-list/dataset-list.tsx create mode 100644 apps/editor/src/components/menu/dataset-list/index.ts create mode 100644 apps/editor/src/components/menu/project-tab/index.ts create mode 100644 apps/editor/src/components/menu/projects-list/project-list-item.tsx create mode 100644 apps/editor/src/components/menu/projects-list/project-list.tsx create mode 100644 apps/editor/src/queries/hub-base-url.tsx create mode 100644 apps/editor/src/screens/project-screen.tsx create mode 100644 apps/editor/src/screens/projects-screen.tsx create mode 100644 apps/editor/src/types/project-types.tsx diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index 14688fe3e..36f2aa59b 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -14,6 +14,7 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; import { Route, Routes } from "react-router-dom"; +import { ProjectTab } from "../components/menu/project-tab"; import { whoAwsConfigDeployment, whoAwsConfigDevelopment, @@ -21,8 +22,13 @@ import { } from "../constants"; import { setUpEventHandling } from "../event-handling"; import type { RootStore } from "../models"; -import hubBaseUrl from "../querys/hub-base-url"; -import { DatasetScreen, EditorScreen } from "../screens"; +import hubBaseUrl from "../queries/hub-base-url"; +import { + DatasetScreen, + EditorScreen, + ProjectScreen, + ProjectsScreen, +} from "../screens"; import { setupRootStore, StoreProvider } from "./root-store"; if (isFromWHO()) { @@ -79,8 +85,21 @@ function App(): JSX.Element { {hubBaseUrl ? ( - } /> + } /> + } + /> + } + /> + } + /> } /> + } /> ) : ( diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index a455f0935..6e1d06f1e 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -269,8 +269,16 @@ "exit-select-mode": "Auswahlmodus verlassen", "select-mode": "Auswahlmodus", + "projects-base-title": "VISIAN Projekte", + "project-base-title": "VISIAN Projekt", "dataset-base-title": "VISIAN Dataset", + "projects-loading": "Projekte werden geladen...", + "projects-loading-error": "Fehler beim Laden der Projekte:", + + "project-loading": "Projekt wird geladen...", + "project-loading-error": "Fehler beim Laden des Projekts:", + "dataset-loading": "Dataset wird geladen...", "dataset-loading-error": "Fehler beim Laden des Datensatzes:", diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 3f2073e15..56e7c7164 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -269,8 +269,16 @@ "exit-select-mode": "Exit select mode", "select-mode": "Select mode", + "projects-base-title": "VISIAN Projects", + "project-base-title": "VISIAN Project", "dataset-base-title": "VISIAN Dataset", + "projects-loading": "Loading projects...", + "projects-loading-error": "Error on loading Projects:", + + "project-loading": "Loading Project", + "project-loading-error": "Error on loading Datasets:", + "dataset-loading": "Loading Dataset...", "dataset-loading-error": "Error on loading Dataset:", diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx new file mode 100644 index 000000000..0ab057521 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx @@ -0,0 +1,32 @@ +import { FlexRow, ListItem, Text } from "@visian/ui-shared"; +import { useNavigate } from "react-router-dom"; +import styled from "styled-components"; + +import hubBaseUrl from "../../../queries/hub-base-url"; +import { Dataset } from "../../../types"; + +const Spacer = styled.div` + width: 10px; +`; + +const ModelFlexRow = styled(FlexRow)` + margin-right: auto; + cursor: pointer; +`; + +export const DatasetListItem = ({ dataset }: { dataset: Dataset }) => { + const navigate = useNavigate(); + + return ( + + navigate(`/project/${dataset.project}/${dataset.id}`)} + > + {dataset.name} + + + + ); +}; + +export default DatasetListItem; diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx new file mode 100644 index 000000000..f335c2427 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx @@ -0,0 +1,19 @@ +import { List, stopPropagation } from "@visian/ui-shared"; +import styled from "styled-components"; + +import { Dataset } from "../../../types"; +import { DatasetListItem } from "./dataset-list-item"; + +const StyledProjectList = styled(List)` + width: 100%; + height: 400px; + overflow-y: auto; +`; + +export const DatasetList = ({ datasets }: { datasets: Dataset[] }) => ( + + {datasets.map((dataset: Dataset) => ( + + ))} + +); diff --git a/apps/editor/src/components/menu/dataset-list/index.ts b/apps/editor/src/components/menu/dataset-list/index.ts new file mode 100644 index 000000000..74ad9725a --- /dev/null +++ b/apps/editor/src/components/menu/dataset-list/index.ts @@ -0,0 +1,2 @@ +export * from "./dataset-list-item"; +export * from "./dataset-list"; diff --git a/apps/editor/src/components/menu/project-tab/index.ts b/apps/editor/src/components/menu/project-tab/index.ts new file mode 100644 index 000000000..26e983acb --- /dev/null +++ b/apps/editor/src/components/menu/project-tab/index.ts @@ -0,0 +1 @@ +export * from "./project-tab"; diff --git a/apps/editor/src/components/menu/projects-list/project-list-item.tsx b/apps/editor/src/components/menu/projects-list/project-list-item.tsx new file mode 100644 index 000000000..1de60a0df --- /dev/null +++ b/apps/editor/src/components/menu/projects-list/project-list-item.tsx @@ -0,0 +1,29 @@ +import { FlexRow, ListItem, Text } from "@visian/ui-shared"; +import { useNavigate } from "react-router-dom"; +import styled from "styled-components"; + +import { Project } from "../../../types"; + +const Spacer = styled.div` + width: 10px; +`; + +const ModelFlexRow = styled(FlexRow)` + margin-right: auto; + cursor: pointer; +`; + +export const ProjectListItem = ({ project }: { project: Project }) => { + const navigate = useNavigate(); + + return ( + + navigate(`/project/${project.id}/datasets`)}> + {project.name} + + + + ); +}; + +export default ProjectListItem; diff --git a/apps/editor/src/components/menu/projects-list/project-list.tsx b/apps/editor/src/components/menu/projects-list/project-list.tsx new file mode 100644 index 000000000..56e83d31e --- /dev/null +++ b/apps/editor/src/components/menu/projects-list/project-list.tsx @@ -0,0 +1,19 @@ +import { List, stopPropagation } from "@visian/ui-shared"; +import styled from "styled-components"; + +import { Project } from "../../../types"; +import { ProjectListItem } from "./project-list-item"; + +const StyledProjectList = styled(List)` + width: 100%; + height: 400px; + overflow-y: auto; +`; + +export const ProjectList = ({ projects }: { projects: Project[] }) => ( + + {projects.map((project: Project) => ( + + ))} + +); diff --git a/apps/editor/src/queries/hub-base-url.tsx b/apps/editor/src/queries/hub-base-url.tsx new file mode 100644 index 000000000..5fd89b700 --- /dev/null +++ b/apps/editor/src/queries/hub-base-url.tsx @@ -0,0 +1,20 @@ +const formatUrl = (url: string | null | undefined) => { + if (!url || url === "") { + return url; + } + let formattedUrl = url; + if ( + !formattedUrl.startsWith("http://") && + !formattedUrl.startsWith("https://") + ) { + formattedUrl = `http://${formattedUrl}`; + } + if (!formattedUrl.endsWith("/")) { + formattedUrl = `${formattedUrl}/`; + } + return formattedUrl; +}; + +export const hubBaseUrl = formatUrl(process.env.NX_ANNOTATION_SERVICE_HUB_URL); + +export default hubBaseUrl; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index cc06850e3..ada02e508 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,10 +1,11 @@ import { Box, Modal, Screen, Text, useTranslation } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; +import { useParams } from "react-router-dom"; import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; -import { useDataset } from "../querys"; +import { useDataset } from "../queries"; const Main = styled(Box)` display: flex; @@ -18,9 +19,9 @@ const StyledModal = styled(Modal)` width: 100%; `; -const datasetId = "ed74a5b0-bb97-4f92-9bc9-50d47fafc17f"; - export const DatasetScreen: React.FC = observer(() => { + const datasetId = useParams().datasetId || ""; + const { dataset, datasetError, isErrorDataset, isLoadingDataset } = useDataset(datasetId); diff --git a/apps/editor/src/screens/index.ts b/apps/editor/src/screens/index.ts index 350d74fa8..f980d1bd6 100644 --- a/apps/editor/src/screens/index.ts +++ b/apps/editor/src/screens/index.ts @@ -1,2 +1,4 @@ export * from "./editor-screen"; export * from "./dataset-screen"; +export * from "./project-screen"; +export * from "./projects-screen"; diff --git a/apps/editor/src/screens/project-screen.tsx b/apps/editor/src/screens/project-screen.tsx new file mode 100644 index 000000000..e852ca610 --- /dev/null +++ b/apps/editor/src/screens/project-screen.tsx @@ -0,0 +1,51 @@ +import { Box, Modal, Screen, Text, useTranslation } from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import { useParams } from "react-router-dom"; +import styled from "styled-components"; + +import { DatasetList } from "../components/menu/dataset-list"; +import { ProjectTab } from "../components/menu/project-tab"; +import useDatasetsBy from "../queries/use-datasets-by"; + +const Main = styled(Box)` + display: flex; + justify-content: center; + height: 100%; + padding: 5rem 10rem; +`; + +const StyledModal = styled(Modal)` + vertical-align: middle; + width: 100%; +`; + +export const ProjectScreen: React.FC = observer(() => { + const projectId = useParams().projectId || ""; + + const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = + useDatasetsBy(projectId); + + const { t: translate } = useTranslation(); + + return ( + +
+ {isLoadingDatasets && } + {isErrorDatasets && ( + + {`${translate("project-loading-error")} ${ + datasetsError?.response?.statusText + } (${datasetsError?.response?.status})`} + + )} + + + {datasets && } + +
+
+ ); +}); + +export default ProjectScreen; diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/projects-screen.tsx new file mode 100644 index 000000000..859cde675 --- /dev/null +++ b/apps/editor/src/screens/projects-screen.tsx @@ -0,0 +1,45 @@ +import { Box, Modal, Screen, Text, useTranslation } from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import styled from "styled-components"; + +import { ProjectList } from "../components/menu/projects-list/project-list"; +import { useProjects } from "../queries"; + +const Main = styled(Box)` + display: flex; + justify-content: center; + height: 100%; + padding: 5rem 10rem; +`; + +const StyledModal = styled(Modal)` + vertical-align: middle; + width: 100%; +`; + +export const ProjectsScreen: React.FC = observer(() => { + const { projects, projectsError, isErrorProjects, isLoadingProjects } = + useProjects(); + const { t: translate } = useTranslation(); + + return ( + +
+ {isLoadingProjects && } + {isErrorProjects && ( + + {`${translate("projects-loading-error")} ${ + projectsError?.response?.statusText + } (${projectsError?.response?.status})`} + + )} + + {projects && } + +
+
+ ); +}); + +export default ProjectsScreen; diff --git a/apps/editor/src/types/index.ts b/apps/editor/src/types/index.ts index 5b699617c..2cb1e9ade 100644 --- a/apps/editor/src/types/index.ts +++ b/apps/editor/src/types/index.ts @@ -1,2 +1,3 @@ export * from "./dataset-types"; export * from "./ml-model-types"; +export * from "./project-types"; diff --git a/apps/editor/src/types/project-types.tsx b/apps/editor/src/types/project-types.tsx new file mode 100644 index 000000000..ca429a0e3 --- /dev/null +++ b/apps/editor/src/types/project-types.tsx @@ -0,0 +1,6 @@ +export interface Project { + id: string; + name: string; + createdAt: string; + updatedAt: string; +} From d33039632fc9b52741a97ce2ddb0625dadc4ae8b Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Tue, 7 Feb 2023 21:04:15 +0100 Subject: [PATCH 053/500] feat: Add switch for /datasets and /jobs --- .../menu/project-tab/project-tab.tsx | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 apps/editor/src/components/menu/project-tab/project-tab.tsx diff --git a/apps/editor/src/components/menu/project-tab/project-tab.tsx b/apps/editor/src/components/menu/project-tab/project-tab.tsx new file mode 100644 index 000000000..7ee1ee394 --- /dev/null +++ b/apps/editor/src/components/menu/project-tab/project-tab.tsx @@ -0,0 +1,40 @@ +import { Box, Switch, Theme } from "@visian/ui-shared"; +import React from "react"; +import { useNavigate } from "react-router-dom"; +import styled, { useTheme } from "styled-components"; + +import { useStore } from "../../../app/root-store"; + +const StyledBox = styled(Box)` + width: 50%; + height: 10%; +`; + +const projectTabSwitchOptions = [ + { labelTx: "Datasets", value: "datasets" }, + { labelTx: "Jobs", value: "jobs" }, +]; + +const tabSelection = "datasets"; + +function toggleTabSelection(newValue: string) { + const pathParts = window.location.pathname.split("/"); + pathParts[pathParts.length - 1] = newValue; + window.location.pathname = pathParts.join("/"); +} + +export const ProjectTab = () => { + const theme = useTheme() as Theme; + + return ( + + toggleTabSelection(newValue)} + /> + + ); +}; From 143bb3e4d0d1c195d9aeab751a30a6469c51dcf8 Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Tue, 7 Feb 2023 21:04:50 +0100 Subject: [PATCH 054/500] refactor: Change querys to queries --- .../editor/ui-overlay/ui-overlay.tsx | 2 +- .../dataset-image-list-item.tsx | 2 +- .../menu/dataset-modal/dataset-modal.tsx | 2 +- .../ml-model-selection-popup.tsx | 4 +-- apps/editor/src/{querys => queries}/index.ts | 2 ++ .../use-annotations-by.tsx | 0 .../src/{querys => queries}/use-dataset.tsx | 0 apps/editor/src/queries/use-datasets-by.tsx | 35 +++++++++++++++++++ .../src/{querys => queries}/use-files.tsx | 0 .../src/{querys => queries}/use-images-by.tsx | 0 .../src/{querys => queries}/use-ml-models.tsx | 0 apps/editor/src/queries/use-project.tsx | 33 +++++++++++++++++ apps/editor/src/queries/use-projects.tsx | 31 ++++++++++++++++ apps/editor/src/querys/hub-base-url.tsx | 20 ----------- 14 files changed, 106 insertions(+), 25 deletions(-) rename apps/editor/src/{querys => queries}/index.ts (68%) rename apps/editor/src/{querys => queries}/use-annotations-by.tsx (100%) rename apps/editor/src/{querys => queries}/use-dataset.tsx (100%) create mode 100644 apps/editor/src/queries/use-datasets-by.tsx rename apps/editor/src/{querys => queries}/use-files.tsx (100%) rename apps/editor/src/{querys => queries}/use-images-by.tsx (100%) rename apps/editor/src/{querys => queries}/use-ml-models.tsx (100%) create mode 100644 apps/editor/src/queries/use-project.tsx create mode 100644 apps/editor/src/queries/use-projects.tsx delete mode 100644 apps/editor/src/querys/hub-base-url.tsx diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index df7df7a72..d138adc03 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -15,7 +15,7 @@ import styled from "styled-components"; import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; import { importFilesToDocument } from "../../../import-handling"; -import { fetchAnnotation, fetchImage } from "../../../querys/use-files"; +import { fetchAnnotation, fetchImage } from "../../../queries/use-files"; import { DilateErodeModal, MeasurementModal, diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index f1feefd31..e654f0eca 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -8,7 +8,7 @@ import { import { useCallback, useEffect, useState } from "react"; import styled from "styled-components"; -import { useAnnotationsBy } from "../../../querys"; +import { useAnnotationsBy } from "../../../queries"; import { Annotation, Image } from "../../../types"; import { openInEditor } from "../util/openInEditor"; diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx index bf7ad19b1..2080d6594 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx @@ -2,7 +2,7 @@ import { Modal, Text, useTranslation } from "@visian/ui-shared"; import { useCallback, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; -import { useImagesBy } from "../../../querys"; +import { useImagesBy } from "../../../queries"; import { Dataset } from "../../../types"; import { DatasetImageList } from "../dataset-image-list"; import { DatasetNavigationbar } from "../dataset-navigationbar"; diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx index 8f15dc9a3..10d09440d 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx @@ -3,8 +3,8 @@ import axios from "axios"; import { observer } from "mobx-react-lite"; import styled from "styled-components"; -import { useMlModels } from "../../../querys"; -import { hubBaseUrl } from "../../../querys/hub-base-url"; +import { useMlModels } from "../../../queries"; +import { hubBaseUrl } from "../../../queries/hub-base-url"; import { MlModel } from "../../../types"; import { MlModelList } from "../ml-model-list"; import { ModelPopUpProps } from "./ml-model-selection-popup.props"; diff --git a/apps/editor/src/querys/index.ts b/apps/editor/src/queries/index.ts similarity index 68% rename from apps/editor/src/querys/index.ts rename to apps/editor/src/queries/index.ts index 94667f858..eaddbe4cd 100644 --- a/apps/editor/src/querys/index.ts +++ b/apps/editor/src/queries/index.ts @@ -2,3 +2,5 @@ export * from "./use-annotations-by"; export * from "./use-dataset"; export * from "./use-images-by"; export * from "./use-ml-models"; +export * from "./use-project"; +export * from "./use-projects"; diff --git a/apps/editor/src/querys/use-annotations-by.tsx b/apps/editor/src/queries/use-annotations-by.tsx similarity index 100% rename from apps/editor/src/querys/use-annotations-by.tsx rename to apps/editor/src/queries/use-annotations-by.tsx diff --git a/apps/editor/src/querys/use-dataset.tsx b/apps/editor/src/queries/use-dataset.tsx similarity index 100% rename from apps/editor/src/querys/use-dataset.tsx rename to apps/editor/src/queries/use-dataset.tsx diff --git a/apps/editor/src/queries/use-datasets-by.tsx b/apps/editor/src/queries/use-datasets-by.tsx new file mode 100644 index 000000000..247fa06e3 --- /dev/null +++ b/apps/editor/src/queries/use-datasets-by.tsx @@ -0,0 +1,35 @@ +import axios, { AxiosError } from "axios"; +import { useQuery } from "react-query"; + +import { Dataset } from "../types"; +import { hubBaseUrl } from "./hub-base-url"; + +const getDatasetsBy = async (projectId: string) => { + const datasetsResponse = await axios.get(`${hubBaseUrl}datasets`, { + params: { + project: projectId, + }, + }); + return datasetsResponse.data; +}; + +export const useDatasetsBy = (projectId: string) => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Dataset[], + AxiosError + >(["datasetsBy", projectId], () => getDatasetsBy(projectId), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 30, // refetch every 30 seconds + }); + + return { + datasets: data, + datasetsError: error, + isErrorDatasets: isError, + isLoadingDatasets: isLoading, + refetchDatasets: refetch, + removeDatasets: remove, + }; +}; + +export default useDatasetsBy; diff --git a/apps/editor/src/querys/use-files.tsx b/apps/editor/src/queries/use-files.tsx similarity index 100% rename from apps/editor/src/querys/use-files.tsx rename to apps/editor/src/queries/use-files.tsx diff --git a/apps/editor/src/querys/use-images-by.tsx b/apps/editor/src/queries/use-images-by.tsx similarity index 100% rename from apps/editor/src/querys/use-images-by.tsx rename to apps/editor/src/queries/use-images-by.tsx diff --git a/apps/editor/src/querys/use-ml-models.tsx b/apps/editor/src/queries/use-ml-models.tsx similarity index 100% rename from apps/editor/src/querys/use-ml-models.tsx rename to apps/editor/src/queries/use-ml-models.tsx diff --git a/apps/editor/src/queries/use-project.tsx b/apps/editor/src/queries/use-project.tsx new file mode 100644 index 000000000..a11145247 --- /dev/null +++ b/apps/editor/src/queries/use-project.tsx @@ -0,0 +1,33 @@ +import axios, { AxiosError } from "axios"; +import { useQuery } from "react-query"; + +import { Project } from "../types"; +import { hubBaseUrl } from "./hub-base-url"; + +const getProject = async (projectId: string) => { + const projectResponse = await axios.get( + `${hubBaseUrl}projects/${projectId}`, + ); + return projectResponse.data; +}; + +export const useProject = (projectId: string) => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Project, + AxiosError + >(["project", projectId], () => getProject(projectId), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 60, // refetch every minute + }); + + return { + project: data, + projectError: error, + isErrorProject: isError, + isLoadingProject: isLoading, + refetchProject: refetch, + removeProject: remove, + }; +}; + +export default useProject; diff --git a/apps/editor/src/queries/use-projects.tsx b/apps/editor/src/queries/use-projects.tsx new file mode 100644 index 000000000..58381508c --- /dev/null +++ b/apps/editor/src/queries/use-projects.tsx @@ -0,0 +1,31 @@ +import axios, { AxiosError } from "axios"; +import { useQuery } from "react-query"; + +import { Project } from "../types"; +import { hubBaseUrl } from "./hub-base-url"; + +const getProjects = async () => { + const projectsResponse = await axios.get(`${hubBaseUrl}projects`); + return projectsResponse.data; +}; + +export const useProjects = () => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Project[], + AxiosError + >(["project"], () => getProjects(), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 60, // refetch every minute + }); + + return { + projects: data, + projectsError: error, + isErrorProjects: isError, + isLoadingProjects: isLoading, + refetchProjects: refetch, + removeProjects: remove, + }; +}; + +export default useProjects; diff --git a/apps/editor/src/querys/hub-base-url.tsx b/apps/editor/src/querys/hub-base-url.tsx deleted file mode 100644 index 5fd89b700..000000000 --- a/apps/editor/src/querys/hub-base-url.tsx +++ /dev/null @@ -1,20 +0,0 @@ -const formatUrl = (url: string | null | undefined) => { - if (!url || url === "") { - return url; - } - let formattedUrl = url; - if ( - !formattedUrl.startsWith("http://") && - !formattedUrl.startsWith("https://") - ) { - formattedUrl = `http://${formattedUrl}`; - } - if (!formattedUrl.endsWith("/")) { - formattedUrl = `${formattedUrl}/`; - } - return formattedUrl; -}; - -export const hubBaseUrl = formatUrl(process.env.NX_ANNOTATION_SERVICE_HUB_URL); - -export default hubBaseUrl; From 381025e23a8461c83f699a881e40a4b0420b1cec Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Wed, 8 Feb 2023 14:23:58 +0100 Subject: [PATCH 055/500] refactor: Use useNavigate for tab switch --- .../menu/project-tab/project-tab.tsx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/editor/src/components/menu/project-tab/project-tab.tsx b/apps/editor/src/components/menu/project-tab/project-tab.tsx index 7ee1ee394..0353db6e2 100644 --- a/apps/editor/src/components/menu/project-tab/project-tab.tsx +++ b/apps/editor/src/components/menu/project-tab/project-tab.tsx @@ -3,8 +3,6 @@ import React from "react"; import { useNavigate } from "react-router-dom"; import styled, { useTheme } from "styled-components"; -import { useStore } from "../../../app/root-store"; - const StyledBox = styled(Box)` width: 50%; height: 10%; @@ -15,16 +13,17 @@ const projectTabSwitchOptions = [ { labelTx: "Jobs", value: "jobs" }, ]; -const tabSelection = "datasets"; - -function toggleTabSelection(newValue: string) { - const pathParts = window.location.pathname.split("/"); - pathParts[pathParts.length - 1] = newValue; - window.location.pathname = pathParts.join("/"); -} - +const defaultTabSelection = "datasets"; export const ProjectTab = () => { const theme = useTheme() as Theme; + const navigate = useNavigate(); + + // expect path like /project/projectId/datasets + const handleChange = (newValue: string) => { + const pathParts = window.location.pathname.split("/"); + pathParts[pathParts.length - 1] = newValue; + navigate(pathParts.join("/")); + }; return ( @@ -32,8 +31,8 @@ export const ProjectTab = () => { infoBaseZIndex={theme.zIndices.overlay} options={projectTabSwitchOptions} // with store? - value={tabSelection} - onChange={(newValue) => toggleTabSelection(newValue)} + value={defaultTabSelection} + onChange={(newValue) => handleChange(newValue)} /> ); From 3eeef5210566d01079827e5784225416e540929d Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Thu, 9 Feb 2023 11:34:07 +0100 Subject: [PATCH 056/500] refactor: navigation and naming --- .../editor/ui-overlay/ui-overlay.tsx | 10 ++++---- .../dataset-image-list-item.tsx | 23 ++++++++++++++++--- .../src/components/menu/util/openInEditor.tsx | 19 --------------- 3 files changed, 25 insertions(+), 27 deletions(-) delete mode 100644 apps/editor/src/components/menu/util/openInEditor.tsx diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index df7df7a72..a66210b9a 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -198,12 +198,12 @@ export const UIOverlay = observer( // Displaying images and annotations from Backend const loadSessionStorage = (key: string) => { - let object = null; - const objectJSON = sessionStorage.getItem(key); - if (objectJSON) { - object = JSON.parse(objectJSON); + let storedObject = null; + const storedObjectJSON = sessionStorage.getItem(key); + if (storedObjectJSON) { + storedObject = JSON.parse(storedObjectJSON); } - return object; + return storedObject; }; const [searchParams] = useSearchParams(); diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index f1feefd31..1482db4de 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -6,11 +6,11 @@ import { useTranslation, } from "@visian/ui-shared"; import { useCallback, useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; import styled from "styled-components"; import { useAnnotationsBy } from "../../../querys"; import { Annotation, Image } from "../../../types"; -import { openInEditor } from "../util/openInEditor"; const Spacer = styled.div` width: 10px; @@ -69,7 +69,24 @@ export const DatasetImageListItem = ({ }); }, [refetchAnnotations]); + const configureEditorUrl = ( + img: Image | null, + annotation: Annotation | null, + ): string => { + const query: string[] = []; + if (img) { + sessionStorage.setItem("ImageToOpen", JSON.stringify(img)); + query.push("openImage=true"); + } + if (annotation) { + sessionStorage.setItem("AnnotationToOpen", JSON.stringify(annotation)); + query.push("openAnnotation=true"); + } + return `/editor?${query.join("&")}`; + }; + const { t: translate } = useTranslation(); + const navigate = useNavigate(); return ( <> @@ -85,7 +102,7 @@ export const DatasetImageListItem = ({ )} { - openInEditor(image, null); + navigate(configureEditorUrl(image, null)); }} > {image.dataUri} @@ -110,7 +127,7 @@ export const DatasetImageListItem = ({ { - openInEditor(image, annotation); + navigate(configureEditorUrl(image, annotation)); }} > {annotation.dataUri} diff --git a/apps/editor/src/components/menu/util/openInEditor.tsx b/apps/editor/src/components/menu/util/openInEditor.tsx deleted file mode 100644 index 7464899ef..000000000 --- a/apps/editor/src/components/menu/util/openInEditor.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Annotation, Image } from "../../../types"; - -export function openInEditor( - image: Image | null, - annotation: Annotation | null, -) { - // save image id in local storage - const query: string[] = []; - if (image) { - sessionStorage.setItem("ImageToOpen", JSON.stringify(image)); - query.push("openImage=true"); - } - if (annotation) { - sessionStorage.setItem("AnnotationToOpen", JSON.stringify(annotation)); - query.push("openAnnotation=true"); - } - // redirect to editor page - window.location.assign(`/editor?${query.join("&")}`); -} From aa99123a62b4c53eaef8b87768fb82ce665a2d4f Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Thu, 9 Feb 2023 14:49:44 +0100 Subject: [PATCH 057/500] feat: add css styling prototype --- .../menu/dataset-list/dataset-list-item.tsx | 38 +++++++++------ .../menu/dataset-list/dataset-list.tsx | 48 +++++++++++++++---- .../menu/project-tab/project-tab.tsx | 13 +++-- apps/editor/src/screens/project-screen.tsx | 2 +- apps/editor/src/screens/projects-screen.tsx | 2 +- 5 files changed, 73 insertions(+), 30 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx index 0ab057521..e86d74f94 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx @@ -1,31 +1,39 @@ -import { FlexRow, ListItem, Text } from "@visian/ui-shared"; +import { ListItem, Text } from "@visian/ui-shared"; import { useNavigate } from "react-router-dom"; import styled from "styled-components"; -import hubBaseUrl from "../../../queries/hub-base-url"; import { Dataset } from "../../../types"; -const Spacer = styled.div` - width: 10px; +const StyledListItem = styled(ListItem)` + display: flex; + justify-content: center; + align-items: center; + width: 400px; + height: 200px; + background-color: lightgray; + border-radius: 5%; + cursor: pointer; + + @media (max-width: 800px) { + width: 200px; + height: 100px; + } `; -const ModelFlexRow = styled(FlexRow)` - margin-right: auto; - cursor: pointer; +const StyledText = styled(Text)` + font-size: 16px; + color: black; `; export const DatasetListItem = ({ dataset }: { dataset: Dataset }) => { const navigate = useNavigate(); return ( - - navigate(`/project/${dataset.project}/${dataset.id}`)} - > - {dataset.name} - - - + navigate(`/project/${dataset.project}/${dataset.id}`)} + > + {dataset.name} + ); }; diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx index f335c2427..cfd4010db 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx @@ -4,16 +4,48 @@ import styled from "styled-components"; import { Dataset } from "../../../types"; import { DatasetListItem } from "./dataset-list-item"; -const StyledProjectList = styled(List)` - width: 100%; - height: 400px; +const StyledDatasetListContainer = styled.div` + // width: 90vw; + // margin: 0 auto; + // margin-top: 2%; + border: 1px solid silver; +`; + +const StyledDatasetList = styled(List)` + width: 90vw; + height: 70vh; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(20rem, 0.5fr)); + grid-gap: 10vw; + justify-items: center; + // border: 1px solid silver; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: #ccc #fff; + + &::-webkit-scrollbar { + width: 8px; + } + &::-webkit-scrollbar-thumb { + background-color: #ccc; + border-radius: 10px; + } + &::-webkit-scrollbar-track { + background-color: #fff; + border-radius: 10px; + } +`; + +const StyledDatasetListItem = styled(DatasetListItem)` + height: 100px; `; export const DatasetList = ({ datasets }: { datasets: Dataset[] }) => ( - - {datasets.map((dataset: Dataset) => ( - - ))} - + + + {datasets.map((dataset: Dataset) => ( + + ))} + + ); diff --git a/apps/editor/src/components/menu/project-tab/project-tab.tsx b/apps/editor/src/components/menu/project-tab/project-tab.tsx index 0353db6e2..7ce44d924 100644 --- a/apps/editor/src/components/menu/project-tab/project-tab.tsx +++ b/apps/editor/src/components/menu/project-tab/project-tab.tsx @@ -3,9 +3,12 @@ import React from "react"; import { useNavigate } from "react-router-dom"; import styled, { useTheme } from "styled-components"; -const StyledBox = styled(Box)` - width: 50%; - height: 10%; +const StyledTab = styled(Box)` + display: flex; + justify-content: center; + align-items: center; + width: 20%; + margin: auto; `; const projectTabSwitchOptions = [ @@ -26,7 +29,7 @@ export const ProjectTab = () => { }; return ( - + { value={defaultTabSelection} onChange={(newValue) => handleChange(newValue)} /> - + ); }; diff --git a/apps/editor/src/screens/project-screen.tsx b/apps/editor/src/screens/project-screen.tsx index e852ca610..439b67623 100644 --- a/apps/editor/src/screens/project-screen.tsx +++ b/apps/editor/src/screens/project-screen.tsx @@ -12,7 +12,7 @@ const Main = styled(Box)` display: flex; justify-content: center; height: 100%; - padding: 5rem 10rem; + padding: 5%; `; const StyledModal = styled(Modal)` diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/projects-screen.tsx index 859cde675..aede19028 100644 --- a/apps/editor/src/screens/projects-screen.tsx +++ b/apps/editor/src/screens/projects-screen.tsx @@ -10,7 +10,7 @@ const Main = styled(Box)` display: flex; justify-content: center; height: 100%; - padding: 5rem 10rem; + //padding: 5rem 10rem; `; const StyledModal = styled(Modal)` From 6b04daf01b122ff409d0a20ae61031f3de3a11cf Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Fri, 10 Feb 2023 00:17:39 +0100 Subject: [PATCH 058/500] refactor: dataset grid --- .../menu/dataset-list/dataset-list-item.tsx | 27 ++++++++++-------- .../menu/dataset-list/dataset-list.tsx | 28 +++++++------------ 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx index e86d74f94..53f005ae1 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx @@ -1,4 +1,4 @@ -import { ListItem, Text } from "@visian/ui-shared"; +import { color, fontSize, fontWeight, ListItem, Text } from "@visian/ui-shared"; import { useNavigate } from "react-router-dom"; import styled from "styled-components"; @@ -6,23 +6,27 @@ import { Dataset } from "../../../types"; const StyledListItem = styled(ListItem)` display: flex; + flex-direction: column; justify-content: center; align-items: center; - width: 400px; - height: 200px; - background-color: lightgray; + width: 20vw; + height: 15vw; + background-color: ${color("sheet")}; border-radius: 5%; cursor: pointer; - - @media (max-width: 800px) { - width: 200px; - height: 100px; - } + position: relative; `; const StyledText = styled(Text)` - font-size: 16px; - color: black; + font-size: ${fontSize("navigation")}; + font-weight: ${fontWeight("regular")}; + position: absolute; + left: 0; + right: 0; + bottom: 8vw; + display: flex; + justify-content: center; + align-items: center; `; export const DatasetListItem = ({ dataset }: { dataset: Dataset }) => { @@ -31,6 +35,7 @@ export const DatasetListItem = ({ dataset }: { dataset: Dataset }) => { return ( navigate(`/project/${dataset.project}/${dataset.id}`)} + isLast > {dataset.name} diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx index cfd4010db..271304f12 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx @@ -4,21 +4,15 @@ import styled from "styled-components"; import { Dataset } from "../../../types"; import { DatasetListItem } from "./dataset-list-item"; -const StyledDatasetListContainer = styled.div` - // width: 90vw; - // margin: 0 auto; - // margin-top: 2%; - border: 1px solid silver; -`; - const StyledDatasetList = styled(List)` width: 90vw; - height: 70vh; + height: 70vw; display: grid; - grid-template-columns: repeat(auto-fill, minmax(20rem, 0.5fr)); - grid-gap: 10vw; + grid-template-columns: 1fr 1fr 1fr; + column-gap: 30px; + row-gap: 60px; justify-items: center; - // border: 1px solid silver; + margin-top: 2%; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #ccc #fff; @@ -41,11 +35,9 @@ const StyledDatasetListItem = styled(DatasetListItem)` `; export const DatasetList = ({ datasets }: { datasets: Dataset[] }) => ( - - - {datasets.map((dataset: Dataset) => ( - - ))} - - + + {datasets.map((dataset: Dataset) => ( + + ))} + ); From e8681fcabf03be91d4774cc8158f1da99a2f000d Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Fri, 10 Feb 2023 00:18:56 +0100 Subject: [PATCH 059/500] refactor: refactor and rename project view switch --- apps/editor/src/app/app.tsx | 5 ++--- .../src/components/menu/project-tab/index.ts | 1 - .../menu/project-view-switch/index.ts | 1 + .../project-view-switch.tsx} | 20 +++++++++---------- apps/editor/src/screens/project-screen.tsx | 16 +++++++++++---- 5 files changed, 25 insertions(+), 18 deletions(-) delete mode 100644 apps/editor/src/components/menu/project-tab/index.ts create mode 100644 apps/editor/src/components/menu/project-view-switch/index.ts rename apps/editor/src/components/menu/{project-tab/project-tab.tsx => project-view-switch/project-view-switch.tsx} (74%) diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index 36f2aa59b..07b0399af 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -14,7 +14,7 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; import { Route, Routes } from "react-router-dom"; -import { ProjectTab } from "../components/menu/project-tab"; +import { ProjectViewSwitch } from "../components/menu/project-view-switch"; import { whoAwsConfigDeployment, whoAwsConfigDevelopment, @@ -92,14 +92,13 @@ function App(): JSX.Element { /> } + element={} /> } /> } /> - } />
) : ( diff --git a/apps/editor/src/components/menu/project-tab/index.ts b/apps/editor/src/components/menu/project-tab/index.ts deleted file mode 100644 index 26e983acb..000000000 --- a/apps/editor/src/components/menu/project-tab/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./project-tab"; diff --git a/apps/editor/src/components/menu/project-view-switch/index.ts b/apps/editor/src/components/menu/project-view-switch/index.ts new file mode 100644 index 000000000..05767f03d --- /dev/null +++ b/apps/editor/src/components/menu/project-view-switch/index.ts @@ -0,0 +1 @@ +export * from "./project-view-switch"; diff --git a/apps/editor/src/components/menu/project-tab/project-tab.tsx b/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx similarity index 74% rename from apps/editor/src/components/menu/project-tab/project-tab.tsx rename to apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx index 7ce44d924..2e94a7ae9 100644 --- a/apps/editor/src/components/menu/project-tab/project-tab.tsx +++ b/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx @@ -3,21 +3,21 @@ import React from "react"; import { useNavigate } from "react-router-dom"; import styled, { useTheme } from "styled-components"; -const StyledTab = styled(Box)` +const StyledSwitch = styled(Box)` display: flex; justify-content: center; align-items: center; - width: 20%; - margin: auto; + width: 20vw; `; -const projectTabSwitchOptions = [ +const projectViewSwitchOptions = [ { labelTx: "Datasets", value: "datasets" }, { labelTx: "Jobs", value: "jobs" }, ]; -const defaultTabSelection = "datasets"; -export const ProjectTab = () => { +const defaultSwitchSelection = "datasets"; + +export const ProjectViewSwitch = () => { const theme = useTheme() as Theme; const navigate = useNavigate(); @@ -29,14 +29,14 @@ export const ProjectTab = () => { }; return ( - + handleChange(newValue)} /> - + ); }; diff --git a/apps/editor/src/screens/project-screen.tsx b/apps/editor/src/screens/project-screen.tsx index 439b67623..5076b6790 100644 --- a/apps/editor/src/screens/project-screen.tsx +++ b/apps/editor/src/screens/project-screen.tsx @@ -5,14 +5,14 @@ import { useParams } from "react-router-dom"; import styled from "styled-components"; import { DatasetList } from "../components/menu/dataset-list"; -import { ProjectTab } from "../components/menu/project-tab"; +import { ProjectViewSwitch } from "../components/menu/project-view-switch"; import useDatasetsBy from "../queries/use-datasets-by"; const Main = styled(Box)` display: flex; justify-content: center; - height: 100%; - padding: 5%; + height: 90vh; + padding: 3%; `; const StyledModal = styled(Modal)` @@ -20,6 +20,12 @@ const StyledModal = styled(Modal)` width: 100%; `; +const StyledProjectViewSwitch = styled(Box)` + display flex; + justify-content: center; + width: 100%; +`; + export const ProjectScreen: React.FC = observer(() => { const projectId = useParams().projectId || ""; @@ -40,7 +46,9 @@ export const ProjectScreen: React.FC = observer(() => {
)} - + + + {datasets && }
From 675bbdd1f81b4823293d7ddceadc2804d9cb979d Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Fri, 10 Feb 2023 01:23:47 +0100 Subject: [PATCH 060/500] refactor: Dataset Overview Page Route --- apps/editor/src/app/app.tsx | 2 +- .../src/components/menu/dataset-list/dataset-list-item.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index ba79c2994..928946ecf 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -95,7 +95,7 @@ function App(): JSX.Element { element={} /> } /> } /> diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx index 53f005ae1..670954e2d 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx @@ -34,7 +34,9 @@ export const DatasetListItem = ({ dataset }: { dataset: Dataset }) => { return ( navigate(`/project/${dataset.project}/${dataset.id}`)} + onClick={() => + navigate(`/project/${dataset.project}/datasets/${dataset.id}`) + } isLast > {dataset.name} From 7783f2fcb64f35e8c4b1f29f831b22d1cfbdc40c Mon Sep 17 00:00:00 2001 From: Tonybodo Date: Fri, 10 Feb 2023 01:51:41 +0100 Subject: [PATCH 061/500] feat: Adding Home Button --- apps/editor/src/screens/dataset-screen.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index ada02e508..566813ab3 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,4 +1,11 @@ -import { Box, Modal, Screen, Text, useTranslation } from "@visian/ui-shared"; +import { + Box, + InvisibleButton, + Modal, + Screen, + Text, + useTranslation, +} from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; import { useParams } from "react-router-dom"; @@ -6,12 +13,13 @@ import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; import { useDataset } from "../queries"; +import { useNavigate } from "react-router-dom"; const Main = styled(Box)` display: flex; justify-content: center; height: 100%; - padding: 5rem 10rem; + padding: 1rem 10rem; `; const StyledModal = styled(Modal)` @@ -19,12 +27,18 @@ const StyledModal = styled(Modal)` width: 100%; `; +const IconButton = styled(InvisibleButton)` + width: 40px; + margin: 5px; +`; + export const DatasetScreen: React.FC = observer(() => { const datasetId = useParams().datasetId || ""; const { dataset, datasetError, isErrorDataset, isLoadingDataset } = useDataset(datasetId); + const navigate = useNavigate(); const { t: translate } = useTranslation(); return ( @@ -39,6 +53,7 @@ export const DatasetScreen: React.FC = observer(() => { : "" }`} > + navigate(`/project`)} />
{isLoadingDataset && } {isErrorDataset && ( From ef591e3322c75a2eb78535ea3bd097d5176e352a Mon Sep 17 00:00:00 2001 From: johannaschlimme Date: Mon, 13 Feb 2023 15:28:59 +0100 Subject: [PATCH 062/500] refactor: align screens, add home button to project page --- .../menu/dataset-list/dataset-list-item.tsx | 1 - .../menu/dataset-list/dataset-list.tsx | 2 -- apps/editor/src/screens/dataset-screen.tsx | 4 ++-- apps/editor/src/screens/project-screen.tsx | 23 +++++++++++++++---- apps/editor/src/screens/projects-screen.tsx | 1 + apps/editor/src/types/ml-model-types.tsx | 12 +++++----- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx index 670954e2d..4bbc1bd7f 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx @@ -23,7 +23,6 @@ const StyledText = styled(Text)` position: absolute; left: 0; right: 0; - bottom: 8vw; display: flex; justify-content: center; align-items: center; diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx index 271304f12..0569859a3 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx @@ -5,8 +5,6 @@ import { Dataset } from "../../../types"; import { DatasetListItem } from "./dataset-list-item"; const StyledDatasetList = styled(List)` - width: 90vw; - height: 70vw; display: grid; grid-template-columns: 1fr 1fr 1fr; column-gap: 30px; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 566813ab3..af7c7fb4f 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -8,18 +8,18 @@ import { } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import styled from "styled-components"; import { DatasetModal } from "../components/menu/dataset-modal"; import { useDataset } from "../queries"; -import { useNavigate } from "react-router-dom"; const Main = styled(Box)` display: flex; justify-content: center; height: 100%; padding: 1rem 10rem; + padding-bottom: 5rem; `; const StyledModal = styled(Modal)` diff --git a/apps/editor/src/screens/project-screen.tsx b/apps/editor/src/screens/project-screen.tsx index 5076b6790..11ada74a3 100644 --- a/apps/editor/src/screens/project-screen.tsx +++ b/apps/editor/src/screens/project-screen.tsx @@ -1,7 +1,14 @@ -import { Box, Modal, Screen, Text, useTranslation } from "@visian/ui-shared"; +import { + Box, + InvisibleButton, + Modal, + Screen, + Text, + useTranslation, +} from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import styled from "styled-components"; import { DatasetList } from "../components/menu/dataset-list"; @@ -11,8 +18,9 @@ import useDatasetsBy from "../queries/use-datasets-by"; const Main = styled(Box)` display: flex; justify-content: center; - height: 90vh; - padding: 3%; + height: 100%; + padding: 1rem 10rem; + padding-bottom: 5rem; `; const StyledModal = styled(Modal)` @@ -26,16 +34,23 @@ const StyledProjectViewSwitch = styled(Box)` width: 100%; `; +const IconButton = styled(InvisibleButton)` + width: 40px; + margin: 5px; +`; + export const ProjectScreen: React.FC = observer(() => { const projectId = useParams().projectId || ""; const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = useDatasetsBy(projectId); + const navigate = useNavigate(); const { t: translate } = useTranslation(); return ( + navigate(`/project`)} />
{isLoadingDatasets && } {isErrorDatasets && ( diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/projects-screen.tsx index 859cde675..5e8b914fc 100644 --- a/apps/editor/src/screens/projects-screen.tsx +++ b/apps/editor/src/screens/projects-screen.tsx @@ -11,6 +11,7 @@ const Main = styled(Box)` justify-content: center; height: 100%; padding: 5rem 10rem; + padding-top: 4rem; `; const StyledModal = styled(Modal)` diff --git a/apps/editor/src/types/ml-model-types.tsx b/apps/editor/src/types/ml-model-types.tsx index 5df83f8e3..48fc22baa 100644 --- a/apps/editor/src/types/ml-model-types.tsx +++ b/apps/editor/src/types/ml-model-types.tsx @@ -1,7 +1,7 @@ export interface MlModel { - name: string; - version: string; - description: string; - createdAt: string; - updatedAt: string; - } \ No newline at end of file + name: string; + version: string; + description: string; + createdAt: string; + updatedAt: string; +} From ed58d2a8d90df9a5f9ef7272e4c42a25a5bf3542 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 14 Feb 2023 16:56:28 +0100 Subject: [PATCH 063/500] feat: save button to post annotation file to server --- .../editor/ui-overlay/ui-overlay.tsx | 36 ++++++++++++++- apps/editor/src/models/editor/document.ts | 44 +++++++++++++++++++ apps/editor/src/models/root.ts | 12 +++++ apps/editor/src/screens/dataset-screen.tsx | 2 +- 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index a66210b9a..4acf4bd1d 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -15,6 +15,7 @@ import styled from "styled-components"; import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; import { importFilesToDocument } from "../../../import-handling"; +import { hubBaseUrl } from "../../../querys/hub-base-url"; import { fetchAnnotation, fetchImage } from "../../../querys/use-files"; import { DilateErodeModal, @@ -206,6 +207,27 @@ export const UIOverlay = observer( return storedObject; }; + const postToURL = () => { + if (sessionStorage.getItem("ImageToOpen")) { + // const annotation = loadSessionStorage("AnnotationToOpen"); + const image = loadSessionStorage("ImageToOpen"); + const url = `${hubBaseUrl}annotations/new`; + if (url) { + store?.setProgress({ labelTx: "exporting" }); + store?.editor.activeDocument + ?.postToURL(url, image.id, true) + .catch() + .then(() => { + store?.setProgress(); + sessionStorage.removeItem("ImageToOpen"); + sessionStorage.removeItem("AnnotationToOpen"); + // TODO: use navigate + store?.destroyRedirect("/", true); + }); + } + } + }; + const [searchParams] = useSearchParams(); const loadImagesAndAnnotations = () => { async function asyncfunc() { @@ -217,7 +239,7 @@ export const UIOverlay = observer( let shouldImport = false; if (openImage && sessionStorage.getItem("ImageToOpen")) { const image = loadSessionStorage("ImageToOpen"); - sessionStorage.removeItem("ImageToOpen"); + // sessionStorage.removeItem("ImageToOpen"); const imageFile = await fetchImage(image); dT.items.add(imageFile); shouldImport = true; @@ -225,7 +247,7 @@ export const UIOverlay = observer( const openAnnotation = searchParams.get("openAnnotation"); if (openAnnotation && sessionStorage.getItem("AnnotationToOpen")) { const annotation = loadSessionStorage("AnnotationToOpen"); - sessionStorage.removeItem("AnnotationToOpen"); + // sessionStorage.removeItem("AnnotationToOpen"); const annotationFile = await fetchAnnotation(annotation); dT.items.add(annotationFile); shouldImport = true; @@ -300,6 +322,16 @@ export const UIOverlay = observer( + { + postToURL(); + console.log("saved"); + }} + isActive={false} + /> {!isFromWHO() && ( { + const zip = new Zip(); + + // TODO: Rework for group layers + const files = await Promise.all( + this.layers + .filter((layer) => !limitToAnnotations || layer.isAnnotation) + .map((layer) => layer.toFile()), + ); + files.forEach((file, index) => { + if (!file) return; + zip.setFile(`${`00${index}`.slice(-2)}_${file.name}`, file); + }); + + if (this.context?.getTracker()?.isActive) { + const trackingFile = this.context.getTracker()?.toFile(); + if (trackingFile) zip.setFile(trackingFile.name, trackingFile); + } + const fileBlob: any = await zip.toBlob(); + // console.log("") + + const formData = new FormData(); + formData.append("fileName", `${this.title}.zip`); + formData.append("image", imageId); + formData.append("file", fileBlob); + axios + .post(url, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }) + .then((response) => { + console.log(response); + }) + .catch((error) => { + console.log(error); + }); + }; + public getFileForLayer = async (idOrLayer: string | ILayer) => { const layerId = typeof idOrLayer === "string" ? idOrLayer : idOrLayer.id; const layer = this.layerMap[layerId]; diff --git a/apps/editor/src/models/root.ts b/apps/editor/src/models/root.ts index b9cdd06ca..fc7f1d4ce 100644 --- a/apps/editor/src/models/root.ts +++ b/apps/editor/src/models/root.ts @@ -348,4 +348,16 @@ export class RootStore implements ISerializable, IDisposable { } return false; }; + + public destroyRedirect = async ( + redirect: string, + forceDestroy?: boolean, + ): Promise => { + if (await this.destroyLayers(forceDestroy)) { + const redirectURl = new URL(window.location.origin + redirect); + window.location.href = redirectURl.href; + return true; + } + return false; + }; } diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index cc06850e3..6b2a0f35f 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -18,7 +18,7 @@ const StyledModal = styled(Modal)` width: 100%; `; -const datasetId = "ed74a5b0-bb97-4f92-9bc9-50d47fafc17f"; +const datasetId = "ef77afe5-e228-4df3-9ade-875bf20d1c6f"; export const DatasetScreen: React.FC = observer(() => { const { dataset, datasetError, isErrorDataset, isLoadingDataset } = From bab17047ddaa4be94dd9be62db5205ee14949cac Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:23:08 +0000 Subject: [PATCH 064/500] refactor: Rename /project to /projects --- .../src/components/menu/dataset-list/dataset-list-item.tsx | 2 +- .../src/components/menu/projects-list/project-list-item.tsx | 4 +++- apps/editor/src/screens/dataset-screen.tsx | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx index 4bbc1bd7f..b88fbfc28 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list-item.tsx @@ -34,7 +34,7 @@ export const DatasetListItem = ({ dataset }: { dataset: Dataset }) => { return ( - navigate(`/project/${dataset.project}/datasets/${dataset.id}`) + navigate(`/projects/${dataset.project}/datasets/${dataset.id}`) } isLast > diff --git a/apps/editor/src/components/menu/projects-list/project-list-item.tsx b/apps/editor/src/components/menu/projects-list/project-list-item.tsx index 1de60a0df..26a035ea0 100644 --- a/apps/editor/src/components/menu/projects-list/project-list-item.tsx +++ b/apps/editor/src/components/menu/projects-list/project-list-item.tsx @@ -18,7 +18,9 @@ export const ProjectListItem = ({ project }: { project: Project }) => { return ( - navigate(`/project/${project.id}/datasets`)}> + navigate(`/projects/${project.id}/datasets`)} + > {project.name} diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index af7c7fb4f..4f3d8b850 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -53,7 +53,7 @@ export const DatasetScreen: React.FC = observer(() => { : "" }`} > - navigate(`/project`)} /> + navigate(`/projects`)} />
{isLoadingDataset && } {isErrorDataset && ( From ed188bc1e70042d1d59915faa8e530197b15b3ed Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:23:53 +0000 Subject: [PATCH 065/500] refactor: Project-Switch default selection --- .../menu/project-view-switch/project-view-switch.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx b/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx index 2e94a7ae9..20de7468e 100644 --- a/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx +++ b/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx @@ -15,9 +15,11 @@ const projectViewSwitchOptions = [ { labelTx: "Jobs", value: "jobs" }, ]; -const defaultSwitchSelection = "datasets"; - -export const ProjectViewSwitch = () => { +export const ProjectViewSwitch = ({ + defaultSwitchSelection, +}: { + defaultSwitchSelection: string; +}) => { const theme = useTheme() as Theme; const navigate = useNavigate(); @@ -33,7 +35,6 @@ export const ProjectViewSwitch = () => { handleChange(newValue)} /> From 25543b245142ff9ed153db155eea97719773488c Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:24:30 +0000 Subject: [PATCH 066/500] refactor: split project-screen into two screens --- apps/editor/src/app/app.tsx | 16 ++-- .../menu/datasets-grid/datasets-grid.tsx | 54 ++++++++++++++ .../components/menu/datasets-grid/index.ts | 1 + .../menu/job-history/job-history.tsx | 31 +++----- apps/editor/src/screens/index.ts | 3 +- .../src/screens/project-datasets-screen.tsx | 59 +++++++++++++++ .../src/screens/project-jobs-screen.tsx | 59 +++++++++++++++ apps/editor/src/screens/project-screen.tsx | 74 ------------------- 8 files changed, 193 insertions(+), 104 deletions(-) create mode 100644 apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx create mode 100644 apps/editor/src/components/menu/datasets-grid/index.ts create mode 100644 apps/editor/src/screens/project-datasets-screen.tsx create mode 100644 apps/editor/src/screens/project-jobs-screen.tsx delete mode 100644 apps/editor/src/screens/project-screen.tsx diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index 928946ecf..decea1982 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -14,7 +14,6 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; import { Route, Routes } from "react-router-dom"; -import { JobHistory } from "../components/menu/job-history"; import { whoAwsConfigDeployment, whoAwsConfigDevelopment, @@ -26,7 +25,8 @@ import hubBaseUrl from "../queries/hub-base-url"; import { DatasetScreen, EditorScreen, - ProjectScreen, + ProjectDatasetsScreen, + ProjectJobsScreen, ProjectsScreen, } from "../screens"; import { setupRootStore, StoreProvider } from "./root-store"; @@ -85,17 +85,17 @@ function App(): JSX.Element { {hubBaseUrl ? ( - } /> + } /> } + path="/projects/:projectId/datasets" + element={} /> } + path="/projects/:projectId/jobs" + element={} /> } /> } /> diff --git a/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx new file mode 100644 index 000000000..5d4812ebd --- /dev/null +++ b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx @@ -0,0 +1,54 @@ +import { + Box, + InvisibleButton, + Modal, + Screen, + Text, + useTranslation, +} from "@visian/ui-shared"; +import { useParams } from "react-router-dom"; +import styled from "styled-components"; + +import useDatasetsBy from "../../../queries/use-datasets-by"; +import { DatasetList } from "../dataset-list"; + +const StyledModal = styled(Modal)` + vertical-align: middle; + width: 100%; + position: relative; +`; + +export const DatasetsGrid = () => { + const projectId = useParams().projectId || ""; + + const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = + useDatasetsBy(projectId); + const { t: translate } = useTranslation(); + + return ( + // eslint-disable-next-line react/jsx-no-useless-fragment + <> + {isLoadingDatasets || isErrorDatasets ? ( + + {isLoadingDatasets ? ( + {translate("project-loading")} + ) : ( + + {`${translate("project-loading-error")} ${ + datasetsError?.response?.statusText + } (${datasetsError?.response?.status})`} + + )} + + ) : ( + + {datasets && datasets.length > 0 ? ( + + ) : ( + {translate("no-datasets-available")} + )} + + )} + + ); +}; diff --git a/apps/editor/src/components/menu/datasets-grid/index.ts b/apps/editor/src/components/menu/datasets-grid/index.ts new file mode 100644 index 000000000..0c76528c7 --- /dev/null +++ b/apps/editor/src/components/menu/datasets-grid/index.ts @@ -0,0 +1 @@ +export * from "./datasets-grid"; diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index 50b25a428..299473e76 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -5,16 +5,10 @@ import { useJobs } from "../../../queries"; import { ProjectViewSwitch } from "../project-view-switch"; import { JobList } from "./job-list"; -const Main = styled(Box)` - display: flex; - justify-content: center; - height: 90vh; - padding: 3%; -`; - const StyledModal = styled(Modal)` vertical-align: middle; width: 100%; + position: relative; `; const StyledProjectViewSwitch = styled(Box)` @@ -29,19 +23,14 @@ export const JobHistory = () => { const { t: translate } = useTranslation(); return ( -
- - {isLoadingJobs && } - {isErrorJobs && ( - {`${translate("images-loading-error")} ${ - jobsError?.response?.statusText - } (${jobsError?.response?.status})`} - )} - - - - {jobs && } - -
+ + {isLoadingJobs && } + {isErrorJobs && ( + {`${translate("jobs-loading-error")} ${ + jobsError?.response?.statusText + } (${jobsError?.response?.status})`} + )} + {jobs && } + ); }; diff --git a/apps/editor/src/screens/index.ts b/apps/editor/src/screens/index.ts index f980d1bd6..502233f74 100644 --- a/apps/editor/src/screens/index.ts +++ b/apps/editor/src/screens/index.ts @@ -1,4 +1,5 @@ export * from "./editor-screen"; export * from "./dataset-screen"; -export * from "./project-screen"; +export * from "./project-datasets-screen"; +export * from "./project-jobs-screen"; export * from "./projects-screen"; diff --git a/apps/editor/src/screens/project-datasets-screen.tsx b/apps/editor/src/screens/project-datasets-screen.tsx new file mode 100644 index 000000000..65938f782 --- /dev/null +++ b/apps/editor/src/screens/project-datasets-screen.tsx @@ -0,0 +1,59 @@ +import { + Box, + InvisibleButton, + Modal, + Screen, + Text, + useTranslation, +} from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import styled from "styled-components"; + +import { DatasetsGrid } from "../components/menu/datasets-grid"; +import { ProjectViewSwitch } from "../components/menu/project-view-switch"; + +const Main = styled(Box)` + display: flex; + justify-content: center; + height: 100%; + padding: 1rem 10rem; + padding-bottom: 5rem; +`; + +const StyledProjectViewSwitch = styled(Box)` + display: flex; + justify-content: center; + width: 100%; + position: absolute; + top: 20px; +`; + +const IconButton = styled(InvisibleButton)` + width: 40px; + margin: 5px; + z-index: 51; +`; + +//TODO z-index logic + +export const ProjectDatasetsScreen: React.FC = observer(() => { + const navigate = useNavigate(); + + const { t: translate } = useTranslation(); + + return ( + + navigate(`/projects`)} /> +
+ + + + +
+
+ ); +}); + +export default ProjectDatasetsScreen; diff --git a/apps/editor/src/screens/project-jobs-screen.tsx b/apps/editor/src/screens/project-jobs-screen.tsx new file mode 100644 index 000000000..36e70cb66 --- /dev/null +++ b/apps/editor/src/screens/project-jobs-screen.tsx @@ -0,0 +1,59 @@ +import { + Box, + InvisibleButton, + Modal, + Screen, + Text, + useTranslation, +} from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import React from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import styled from "styled-components"; + +import { JobHistory } from "../components/menu/job-history"; +import { ProjectViewSwitch } from "../components/menu/project-view-switch"; + +const Main = styled(Box)` + display: flex; + justify-content: center; + height: 100%; + padding: 1rem 10rem; + padding-bottom: 5rem; +`; + +const StyledProjectViewSwitch = styled(Box)` + display: flex; + justify-content: center; + width: 100%; + position: absolute; + top: 20px; +`; + +const IconButton = styled(InvisibleButton)` + width: 40px; + margin: 5px; + z-index: 51; +`; + +//TODO z-index logic + +export const ProjectJobsScreen: React.FC = observer(() => { + const navigate = useNavigate(); + + const { t: translate } = useTranslation(); + + return ( + + navigate(`/projects`)} /> +
+ + + + +
+
+ ); +}); + +export default ProjectJobsScreen; diff --git a/apps/editor/src/screens/project-screen.tsx b/apps/editor/src/screens/project-screen.tsx deleted file mode 100644 index 11ada74a3..000000000 --- a/apps/editor/src/screens/project-screen.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { - Box, - InvisibleButton, - Modal, - Screen, - Text, - useTranslation, -} from "@visian/ui-shared"; -import { observer } from "mobx-react-lite"; -import React from "react"; -import { useNavigate, useParams } from "react-router-dom"; -import styled from "styled-components"; - -import { DatasetList } from "../components/menu/dataset-list"; -import { ProjectViewSwitch } from "../components/menu/project-view-switch"; -import useDatasetsBy from "../queries/use-datasets-by"; - -const Main = styled(Box)` - display: flex; - justify-content: center; - height: 100%; - padding: 1rem 10rem; - padding-bottom: 5rem; -`; - -const StyledModal = styled(Modal)` - vertical-align: middle; - width: 100%; -`; - -const StyledProjectViewSwitch = styled(Box)` - display flex; - justify-content: center; - width: 100%; -`; - -const IconButton = styled(InvisibleButton)` - width: 40px; - margin: 5px; -`; - -export const ProjectScreen: React.FC = observer(() => { - const projectId = useParams().projectId || ""; - - const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = - useDatasetsBy(projectId); - - const navigate = useNavigate(); - const { t: translate } = useTranslation(); - - return ( - - navigate(`/project`)} /> -
- {isLoadingDatasets && } - {isErrorDatasets && ( - - {`${translate("project-loading-error")} ${ - datasetsError?.response?.statusText - } (${datasetsError?.response?.status})`} - - )} - - - - - {datasets && } - -
-
- ); -}); - -export default ProjectScreen; From 2b27fcd014188a893b7198c700285c35a50a9eb4 Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:32:33 +0000 Subject: [PATCH 067/500] refactor: Rename dataset modal to dataset exlorer --- .../dataset-explorer.tsx} | 2 +- apps/editor/src/components/menu/dataset-explorer/index.ts | 1 + apps/editor/src/components/menu/dataset-modal/index.ts | 1 - apps/editor/src/screens/dataset-screen.tsx | 4 ++-- apps/editor/src/screens/project-datasets-screen.tsx | 4 +--- 5 files changed, 5 insertions(+), 7 deletions(-) rename apps/editor/src/components/menu/{dataset-modal/dataset-modal.tsx => dataset-explorer/dataset-explorer.tsx} (98%) create mode 100644 apps/editor/src/components/menu/dataset-explorer/index.ts delete mode 100644 apps/editor/src/components/menu/dataset-modal/index.ts diff --git a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx b/apps/editor/src/components/menu/dataset-explorer/dataset-explorer.tsx similarity index 98% rename from apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx rename to apps/editor/src/components/menu/dataset-explorer/dataset-explorer.tsx index 2080d6594..d932fb257 100644 --- a/apps/editor/src/components/menu/dataset-modal/dataset-modal.tsx +++ b/apps/editor/src/components/menu/dataset-explorer/dataset-explorer.tsx @@ -15,7 +15,7 @@ const StyledModal = styled(Modal)` `; // TODO: z-index logic -export const DatasetModal = ({ dataset }: { dataset: Dataset }) => { +export const DatasetExplorer = ({ dataset }: { dataset: Dataset }) => { const [isInSelectMode, setIsInSelectMode] = useState(false); const { images, imagesError, isErrorImages, isLoadingImages, refetchImages } = diff --git a/apps/editor/src/components/menu/dataset-explorer/index.ts b/apps/editor/src/components/menu/dataset-explorer/index.ts new file mode 100644 index 000000000..80a7e3de1 --- /dev/null +++ b/apps/editor/src/components/menu/dataset-explorer/index.ts @@ -0,0 +1 @@ +export * from "./dataset-explorer"; diff --git a/apps/editor/src/components/menu/dataset-modal/index.ts b/apps/editor/src/components/menu/dataset-modal/index.ts deleted file mode 100644 index c187da890..000000000 --- a/apps/editor/src/components/menu/dataset-modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./dataset-modal"; diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 4f3d8b850..61abbbcd0 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -11,7 +11,7 @@ import React from "react"; import { useNavigate, useParams } from "react-router-dom"; import styled from "styled-components"; -import { DatasetModal } from "../components/menu/dataset-modal"; +import { DatasetExplorer } from "../components/menu/dataset-explorer"; import { useDataset } from "../queries"; const Main = styled(Box)` @@ -63,7 +63,7 @@ export const DatasetScreen: React.FC = observer(() => { } (${datasetError?.response?.status})`}
)} - {dataset && } + {dataset && }
); diff --git a/apps/editor/src/screens/project-datasets-screen.tsx b/apps/editor/src/screens/project-datasets-screen.tsx index 65938f782..df72c103f 100644 --- a/apps/editor/src/screens/project-datasets-screen.tsx +++ b/apps/editor/src/screens/project-datasets-screen.tsx @@ -1,14 +1,12 @@ import { Box, InvisibleButton, - Modal, Screen, - Text, useTranslation, } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import styled from "styled-components"; import { DatasetsGrid } from "../components/menu/datasets-grid"; From 810496ac535527338a029c864ebb327a49473ef4 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 21 Feb 2023 18:45:03 +0100 Subject: [PATCH 068/500] refactor: save image and annotation IDs in layers instead of sessionStore --- .vscode/launch.json | 12 ++++ apps/editor/src/assets/de.json | 2 + apps/editor/src/assets/en.json | 2 + .../editor/ui-overlay/ui-overlay.tsx | 60 +++++++++---------- .../dataset-image-list-item.tsx | 6 +- apps/editor/src/models/editor/document.ts | 11 ++++ apps/editor/src/querys/use-annotations-by.tsx | 7 +++ apps/editor/src/querys/use-files.tsx | 32 +++++++--- apps/editor/src/querys/use-images-by.tsx | 7 +++ apps/editor/src/types/file-types.tsx | 5 ++ apps/editor/src/types/index.ts | 1 + libs/ui-shared/src/lib/types/editor/layers.ts | 2 + 12 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 apps/editor/src/types/file-types.tsx diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..ba6ce3fc6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch chrome against localhost", + "url": "http://localhost:4200", + "webRoot": "${workspaceFolder}/apps/editor" + } + ] +} diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index a455f0935..f7cf0dd9a 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -276,9 +276,11 @@ "images-loading": "Images werden geladen...", "images-loading-error": "Fehler beim Laden der Images:", + "image-open-error": "Fehler beim Öffnen des Bildes", "annotations-loading": "Annotationen werden geladen...", "annotations-loading-error": "Fehler beim Laden der Annotationen:", + "annotation-open-error": "Fehler beim Öffnen der Annotation", "ml-model-selection-description": "Wähle das Modell mit dem die Bilder annotiert werden sollen", "ml-model-selection-title": "Modell Auswahl", diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 3f2073e15..b6ffa7762 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -276,9 +276,11 @@ "images-loading": "Loading Images...", "images-loading-error": "Error on loading Images:", + "image-open-error": "Error on opening image", "annotations-loading": "Loading Annotations...", "annotations-loading-error": "Error on loading Annotations:", + "annotation-open-error": "Error on opening annotation", "ml-model-selection-description": "Select the model you want to use to annotate the images", "ml-model-selection-title": "Model Selection", diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index a66210b9a..de116f6db 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -15,7 +15,7 @@ import styled from "styled-components"; import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; import { importFilesToDocument } from "../../../import-handling"; -import { fetchAnnotation, fetchImage } from "../../../querys/use-files"; +import { fetchAnnotationFile, fetchImageFile } from "../../../querys/use-files"; import { DilateErodeModal, MeasurementModal, @@ -196,48 +196,46 @@ export const UIOverlay = observer( }); }, [store]); - // Displaying images and annotations from Backend - const loadSessionStorage = (key: string) => { - let storedObject = null; - const storedObjectJSON = sessionStorage.getItem(key); - if (storedObjectJSON) { - storedObject = JSON.parse(storedObjectJSON); - } - return storedObject; - }; - const [searchParams] = useSearchParams(); const loadImagesAndAnnotations = () => { async function asyncfunc() { if (store?.editor.activeDocument?.layers.length !== 0) { return store?.destroyReload(); } - const openImage = searchParams.get("openImage"); - const dT = new DataTransfer(); - let shouldImport = false; - if (openImage && sessionStorage.getItem("ImageToOpen")) { - const image = loadSessionStorage("ImageToOpen"); - sessionStorage.removeItem("ImageToOpen"); - const imageFile = await fetchImage(image); - dT.items.add(imageFile); - shouldImport = true; + const fileTransfer = new DataTransfer(); + const imageIdToOpen = searchParams.get("imageId"); + if (imageIdToOpen) { + try { + const imageFile = await fetchImageFile(imageIdToOpen); + fileTransfer.items.add(imageFile); + } catch (error) { + store?.setError({ + titleTx: "import-error", + descriptionTx: "image-open-error", + }); + } } - const openAnnotation = searchParams.get("openAnnotation"); - if (openAnnotation && sessionStorage.getItem("AnnotationToOpen")) { - const annotation = loadSessionStorage("AnnotationToOpen"); - sessionStorage.removeItem("AnnotationToOpen"); - const annotationFile = await fetchAnnotation(annotation); - dT.items.add(annotationFile); - shouldImport = true; + const annotationIdToOpen = searchParams.get("annotationId"); + if (annotationIdToOpen) { + try { + const annotationFile = await fetchAnnotationFile( + annotationIdToOpen, + ); + fileTransfer.items.add(annotationFile); + } catch (error) { + store?.setError({ + titleTx: "import-error", + descriptionTx: "annotation-open-error", + }); + } } - if (store && shouldImport) { - importFilesToDocument(dT.files, store); + if (store && fileTransfer.files.length) { + importFilesToDocument(fileTransfer.files, store); } } asyncfunc(); }; - - useEffect(loadImagesAndAnnotations, [searchParams]); + useEffect(loadImagesAndAnnotations, [searchParams, store]); return ( { const query: string[] = []; if (img) { - sessionStorage.setItem("ImageToOpen", JSON.stringify(img)); - query.push("openImage=true"); + query.push(`imageId=${img.id}`); } if (annotation) { - sessionStorage.setItem("AnnotationToOpen", JSON.stringify(annotation)); - query.push("openAnnotation=true"); + query.push(`annotationId=${annotation.id}`); } return `/editor?${query.join("&")}`; }; diff --git a/apps/editor/src/models/editor/document.ts b/apps/editor/src/models/editor/document.ts index 6925fc31e..978763784 100644 --- a/apps/editor/src/models/editor/document.ts +++ b/apps/editor/src/models/editor/document.ts @@ -39,6 +39,7 @@ import { generalTextures2d, generalTextures3d, } from "../../constants"; +import { FileWithMetadata } from "../../types"; import { readTrackingLog, TrackingData } from "../tracking"; import { StoreContext } from "../types"; import { Clipboard } from "./clipboard"; @@ -716,6 +717,7 @@ export class Document { ...imageWithUnit, name: `${value}_${image.name}` }, value, ); + this.addFileMetaData(createdLayerId, files); }); } } @@ -731,6 +733,7 @@ export class Document this.history.clear(); } + this.addFileMetaData(createdLayerId, files); return createdLayerId; } @@ -859,6 +862,14 @@ export class Document ) as unknown as IImageLayer[]; } + private addFileMetaData(createdLayerId: string, file: File | File[]) { + const layer = this.getLayer(createdLayerId); + if (layer && "metadata" in file) { + const fileWithMetaData = file as FileWithMetadata; + layer.metaDataId = fileWithMetaData.metadata.id; + } + } + // Proxies public get sliceRenderer(): ISliceRenderer | undefined { return this.editor.sliceRenderer; diff --git a/apps/editor/src/querys/use-annotations-by.tsx b/apps/editor/src/querys/use-annotations-by.tsx index 491a84a1c..cac4ac282 100644 --- a/apps/editor/src/querys/use-annotations-by.tsx +++ b/apps/editor/src/querys/use-annotations-by.tsx @@ -16,6 +16,13 @@ const getAnnotationsBy = async (imageId: string) => { return annotationsResponse.data; }; +export const getAnnotation = async (annotationId: string) => { + const annotationsResponse = await axios.get( + `${hubBaseUrl}annotations/${annotationId}`, + ); + return annotationsResponse.data; +}; + export const useAnnotationsBy = (imageId: string) => { const { data, error, isError, isLoading, refetch, remove } = useQuery< Annotation[], diff --git a/apps/editor/src/querys/use-files.tsx b/apps/editor/src/querys/use-files.tsx index 559cd4e20..449abc8b3 100644 --- a/apps/editor/src/querys/use-files.tsx +++ b/apps/editor/src/querys/use-files.tsx @@ -1,7 +1,9 @@ import path from "path"; -import { Annotation, Image } from "../types"; +import { FileWithMetadata } from "../types"; import hubBaseUrl from "./hub-base-url"; +import { getAnnotation } from "./use-annotations-by"; +import { getImage } from "./use-images-by"; const fetchFile = async ( id: string, @@ -20,14 +22,30 @@ const fetchFile = async ( }), ); -export const fetchImage = async (image: Image): Promise => { +export const fetchImageFile = async ( + imageId: string, +): Promise => { + const image = await getImage(imageId); const fileName: string = path.basename(image.dataUri); - return fetchFile(image.id, "images", fileName); + const imageFile = (await fetchFile( + image.id, + "images", + fileName, + )) as FileWithMetadata; + imageFile.metadata = image; + return imageFile; }; -export const fetchAnnotation = async ( - annotation: Annotation, -): Promise => { +export const fetchAnnotationFile = async ( + annotationId: string, +): Promise => { + const annotation = await getAnnotation(annotationId); const fileName: string = path.basename(annotation.dataUri); - return fetchFile(annotation.id, "annotations", fileName); + const annotationFile = (await fetchFile( + annotation.id, + "annotations", + fileName, + )) as FileWithMetadata; + annotationFile.metadata = annotation; + return annotationFile; }; diff --git a/apps/editor/src/querys/use-images-by.tsx b/apps/editor/src/querys/use-images-by.tsx index 38c4f93ab..948373d08 100644 --- a/apps/editor/src/querys/use-images-by.tsx +++ b/apps/editor/src/querys/use-images-by.tsx @@ -13,6 +13,13 @@ const getImagesBy = async (datasetId: string) => { return imagesResponse.data; }; +export const getImage = async (imageId: string) => { + const imageResponse = await axios.get( + `${hubBaseUrl}images/${imageId}`, + ); + return imageResponse.data; +}; + export const useImagesBy = (datasetId: string) => { const { data, error, isError, isLoading, refetch, remove } = useQuery< Image[], diff --git a/apps/editor/src/types/file-types.tsx b/apps/editor/src/types/file-types.tsx new file mode 100644 index 000000000..617259140 --- /dev/null +++ b/apps/editor/src/types/file-types.tsx @@ -0,0 +1,5 @@ +import { Annotation, Image } from "./dataset-types"; + +export interface FileWithMetadata extends File { + metadata: Annotation | Image; +} diff --git a/apps/editor/src/types/index.ts b/apps/editor/src/types/index.ts index 5b699617c..b036d0817 100644 --- a/apps/editor/src/types/index.ts +++ b/apps/editor/src/types/index.ts @@ -1,2 +1,3 @@ export * from "./dataset-types"; export * from "./ml-model-types"; +export * from "./file-types"; diff --git a/libs/ui-shared/src/lib/types/editor/layers.ts b/libs/ui-shared/src/lib/types/editor/layers.ts index 2f2e2325c..e2432ffbb 100644 --- a/libs/ui-shared/src/lib/types/editor/layers.ts +++ b/libs/ui-shared/src/lib/types/editor/layers.ts @@ -70,6 +70,8 @@ export interface ILayer { /** The layer's transform matrix used to position it during rendering. */ transformation?: Matrix4; + /** The layer's meta data ID. */ + metaDataId?: string; /** * Returns all slice markers, aggregated for the layer and given view type. From e78bd0907d149b9bd12d02a96eb49429cc4988aa Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 23 Feb 2023 11:35:27 +0100 Subject: [PATCH 069/500] feat: add job properties to jobs history --- .../menu/job-history/job-list-item.tsx | 11 ++++++-- .../src/components/menu/util/display-date.tsx | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 apps/editor/src/components/menu/util/display-date.tsx diff --git a/apps/editor/src/components/menu/job-history/job-list-item.tsx b/apps/editor/src/components/menu/job-history/job-list-item.tsx index 7e3baab71..cbf566716 100644 --- a/apps/editor/src/components/menu/job-history/job-list-item.tsx +++ b/apps/editor/src/components/menu/job-history/job-list-item.tsx @@ -2,6 +2,7 @@ import { ListItem, Text } from "@visian/ui-shared"; import styled from "styled-components"; import { Job } from "../../../types"; +import { getDisplayDate } from "../util/display-date"; const Spacer = styled.div` width: 10px; @@ -15,8 +16,14 @@ export const JobListItem = ({ job }: { job: Job }) => ( {job.modelName} - {job.modelVersion} - + v{job.modelVersion} + + {job.startedAt ? getDisplayDate(new Date(job.startedAt)) : ""} + + + {job.finishedAt ? getDisplayDate(new Date(job.finishedAt)) : ""} + + {job.status} ); diff --git a/apps/editor/src/components/menu/util/display-date.tsx b/apps/editor/src/components/menu/util/display-date.tsx new file mode 100644 index 000000000..da1ea6598 --- /dev/null +++ b/apps/editor/src/components/menu/util/display-date.tsx @@ -0,0 +1,25 @@ +/** + * Converts a date to a string for either german or english format. + * @param date The date to be converted. + * @returns The date as a string in german format if the timezone is Europe/Berlin, otherwise in english format. + */ +export function getDisplayDate(date: Date): string { + if (Intl.DateTimeFormat().resolvedOptions().timeZone == "Europe/Berlin") { + return date.toLocaleDateString("de-DE", { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "2-digit", + hour12: false, + minute: "2-digit", + }); + } + return date.toLocaleDateString("en-US", { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "2-digit", + hour12: true, + minute: "2-digit", + }); +} From 8844cd6109dbe7284eb608937605b0a4ab4150bb Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 23 Feb 2023 16:47:24 +0100 Subject: [PATCH 070/500] refactor: use react-table to display jobs-history --- .../menu/job-history/job-history.tsx | 11 +--- .../menu/job-history/job-list-item.tsx | 23 ++++--- .../components/menu/job-history/job-list.tsx | 61 ++++++++++++++----- .../menu/job-history/jobs-table-layout.tsx | 52 ++++++++++++++++ package.json | 2 + yarn.lock | 30 +++++++++ 6 files changed, 145 insertions(+), 34 deletions(-) create mode 100644 apps/editor/src/components/menu/job-history/jobs-table-layout.tsx diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index 299473e76..4d5743b57 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -2,8 +2,7 @@ import { Box, Modal, Text, useTranslation } from "@visian/ui-shared"; import styled from "styled-components"; import { useJobs } from "../../../queries"; -import { ProjectViewSwitch } from "../project-view-switch"; -import { JobList } from "./job-list"; +import { JobsTable } from "./job-list"; const StyledModal = styled(Modal)` vertical-align: middle; @@ -11,12 +10,6 @@ const StyledModal = styled(Modal)` position: relative; `; -const StyledProjectViewSwitch = styled(Box)` - display flex; - justify-content: center; - width: 100%; -`; - export const JobHistory = () => { const { jobs, jobsError, isErrorJobs, isLoadingJobs } = useJobs(); @@ -30,7 +23,7 @@ export const JobHistory = () => { jobsError?.response?.statusText } (${jobsError?.response?.status})`} )} - {jobs && } + {jobs && }
); }; diff --git a/apps/editor/src/components/menu/job-history/job-list-item.tsx b/apps/editor/src/components/menu/job-history/job-list-item.tsx index cbf566716..b67a4c029 100644 --- a/apps/editor/src/components/menu/job-history/job-list-item.tsx +++ b/apps/editor/src/components/menu/job-history/job-list-item.tsx @@ -2,7 +2,6 @@ import { ListItem, Text } from "@visian/ui-shared"; import styled from "styled-components"; import { Job } from "../../../types"; -import { getDisplayDate } from "../util/display-date"; const Spacer = styled.div` width: 10px; @@ -12,18 +11,22 @@ const ExpandedSpacer = styled.div` margin-right: auto; `; -export const JobListItem = ({ job }: { job: Job }) => ( - - {job.modelName} +export const JobListItem = ({ + data, + isHeader, +}: { + data: Job; + isHeader?: boolean; +}) => ( + + {data.modelName} - v{job.modelVersion} + {data.modelVersion} - {job.startedAt ? getDisplayDate(new Date(job.startedAt)) : ""} + {data.startedAt} - - {job.finishedAt ? getDisplayDate(new Date(job.finishedAt)) : ""} - + {data.finishedAt} - {job.status} + {data.status} ); diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-list.tsx index 1d11b12f7..5c6026e93 100644 --- a/apps/editor/src/components/menu/job-history/job-list.tsx +++ b/apps/editor/src/components/menu/job-history/job-list.tsx @@ -1,18 +1,49 @@ -import { List, stopPropagation } from "@visian/ui-shared"; -import styled from "styled-components"; - import { Job } from "../../../types"; -import { JobListItem } from "./job-list-item"; +import { getDisplayDate } from "../util/display-date"; + +import { JobsTableLayout } from "./jobs-table-layout"; +import { + createColumnHelper, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; + +function getDisplayJob(job: Job): Job { + return { + ...job, + modelVersion: `v${job.modelVersion}`, + startedAt: job.startedAt ? getDisplayDate(new Date(job.startedAt)) : "", + finishedAt: job.finishedAt ? getDisplayDate(new Date(job.finishedAt)) : "", + }; +} + +const columnHelper = createColumnHelper(); + +const columns = [ + columnHelper.accessor("modelName", { + header: "Model", + }), + columnHelper.accessor("modelVersion", { + header: "Version", + }), + columnHelper.accessor("startedAt", { + header: "Started At", + }), + columnHelper.accessor("finishedAt", { + header: "Finished At", + }), + columnHelper.accessor("status", { + header: "Status", + }), +]; -const StyledList = styled(List)` - width: 100%; - overflow-y: auto; -`; +export const JobsTable = ({ jobs }: { jobs: Job[] }) => { + const data = jobs.map((job: Job) => getDisplayJob(job)); -export const JobList = ({ jobs }: { jobs: Job[] }) => ( - - {jobs.map((job: Job) => ( - - ))} - -); + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + return ; +}; diff --git a/apps/editor/src/components/menu/job-history/jobs-table-layout.tsx b/apps/editor/src/components/menu/job-history/jobs-table-layout.tsx new file mode 100644 index 000000000..aadb8d7ea --- /dev/null +++ b/apps/editor/src/components/menu/job-history/jobs-table-layout.tsx @@ -0,0 +1,52 @@ +import { Table, flexRender } from "@tanstack/react-table"; +import { Job } from "../../../types"; + +export const JobsTableLayout = ({ table }: { table: Table }) => { + return ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((header) => ( + + ))} + + ))} + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.footer, + header.getContext(), + )} +
+ ); +}; diff --git a/package.json b/package.json index 6383ee878..e254ec882 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,8 @@ "packageManager": "yarn@3.2.4", "dependencies": { "@aws-amplify/ui-react": "^1.2.15", + "@tanstack/react-table": "^8.7.9", + "@types/react-table": "^7.7.14", "aws-amplify": "^4.2.9", "axios": "^1.2.2", "core-js": "^3.6.5", diff --git a/yarn.lock b/yarn.lock index e7e53e151..10ec289f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6910,6 +6910,25 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-table@npm:^8.7.9": + version: 8.7.9 + resolution: "@tanstack/react-table@npm:8.7.9" + dependencies: + "@tanstack/table-core": 8.7.9 + peerDependencies: + react: ">=16" + react-dom: ">=16" + checksum: 3c704ac903405972641c9857e1466025bcdac04ee4890d64b018386cad5a778aca3a8d3f78542ae7a0ed609841d32ef3a7e563b079e87fa93b9ae8570a310499 + languageName: node + linkType: hard + +"@tanstack/table-core@npm:8.7.9": + version: 8.7.9 + resolution: "@tanstack/table-core@npm:8.7.9" + checksum: 78d2314928c29559088e4bada0248cc7f94e93756e1a2c1f37a651db30276e9ae960d647bd3a61b67b3f0f9f7e4dec5dd58eb49b8adb80ee5952ef417b6e581f + languageName: node + linkType: hard + "@testing-library/dom@npm:^8.0.0": version: 8.19.0 resolution: "@testing-library/dom@npm:8.19.0" @@ -7571,6 +7590,15 @@ __metadata: languageName: node linkType: hard +"@types/react-table@npm:^7.7.14": + version: 7.7.14 + resolution: "@types/react-table@npm:7.7.14" + dependencies: + "@types/react": "*" + checksum: 238047beca9abecc4b3e1e377b823b492b1a00d011b8456012bbcbd2682d5a7e0c0b1cca6384ccc41e724156e991d35e5725a5a3c4b01ff7bbaf095643569da9 + languageName: node + linkType: hard + "@types/react-test-renderer@npm:18.0.0": version: 18.0.0 resolution: "@types/react-test-renderer@npm:18.0.0" @@ -24138,6 +24166,7 @@ __metadata: "@storybook/manager-webpack5": ~6.5.9 "@storybook/react": ~6.5.9 "@svgr/webpack": ^5.4.0 + "@tanstack/react-table": ^8.7.9 "@testing-library/react": 12.1.2 "@types/babel__core": 7.1.19 "@types/core-js": ^2.5.5 @@ -24155,6 +24184,7 @@ __metadata: "@types/react-dom": 17.0.9 "@types/react-is": 17.0.3 "@types/react-router-dom": 5.3.3 + "@types/react-table": ^7.7.14 "@types/react-test-renderer": 18.0.0 "@types/styled-components": 5.1.26 "@types/three": 0.135.0 From a53c061c6555bf2df7f1bf8ccc448394def4f578 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 23 Feb 2023 18:12:24 +0100 Subject: [PATCH 071/500] refactor: extract table into shared component --- .../components/menu/job-history/job-list.tsx | 20 ++++-- .../menu/job-history/jobs-table-layout.tsx | 52 -------------- .../src/lib/components/table/index.ts | 1 + .../src/lib/components/table/table.spec.tsx | 46 +++++++++++++ .../src/lib/components/table/table.tsx | 68 +++++++++++++++++++ 5 files changed, 128 insertions(+), 59 deletions(-) delete mode 100644 apps/editor/src/components/menu/job-history/jobs-table-layout.tsx create mode 100644 libs/ui-shared/src/lib/components/table/index.ts create mode 100644 libs/ui-shared/src/lib/components/table/table.spec.tsx create mode 100644 libs/ui-shared/src/lib/components/table/table.tsx diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-list.tsx index 5c6026e93..2187e944a 100644 --- a/apps/editor/src/components/menu/job-history/job-list.tsx +++ b/apps/editor/src/components/menu/job-history/job-list.tsx @@ -1,12 +1,13 @@ import { Job } from "../../../types"; import { getDisplayDate } from "../util/display-date"; -import { JobsTableLayout } from "./jobs-table-layout"; +import { TableLayout } from "../../../../../../libs/ui-shared/src/lib/components/table"; import { createColumnHelper, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; +import { ListItemLabel } from "@visian/ui-shared"; function getDisplayJob(job: Job): Job { return { @@ -21,19 +22,24 @@ const columnHelper = createColumnHelper(); const columns = [ columnHelper.accessor("modelName", { - header: "Model", + header: () => , + cell: (props) => , }), columnHelper.accessor("modelVersion", { - header: "Version", + header: () => , + cell: (props) => , }), columnHelper.accessor("startedAt", { - header: "Started At", + header: () => , + cell: (props) => , }), columnHelper.accessor("finishedAt", { - header: "Finished At", + header: () => , + cell: (props) => , }), columnHelper.accessor("status", { - header: "Status", + header: () => , + cell: (props) => , }), ]; @@ -45,5 +51,5 @@ export const JobsTable = ({ jobs }: { jobs: Job[] }) => { columns, getCoreRowModel: getCoreRowModel(), }); - return ; + return ; }; diff --git a/apps/editor/src/components/menu/job-history/jobs-table-layout.tsx b/apps/editor/src/components/menu/job-history/jobs-table-layout.tsx deleted file mode 100644 index aadb8d7ea..000000000 --- a/apps/editor/src/components/menu/job-history/jobs-table-layout.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Table, flexRender } from "@tanstack/react-table"; -import { Job } from "../../../types"; - -export const JobsTableLayout = ({ table }: { table: Table }) => { - return ( - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - ))} - - ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((header) => ( - - ))} - - ))} - -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} -
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.footer, - header.getContext(), - )} -
- ); -}; diff --git a/libs/ui-shared/src/lib/components/table/index.ts b/libs/ui-shared/src/lib/components/table/index.ts new file mode 100644 index 000000000..0e948df9e --- /dev/null +++ b/libs/ui-shared/src/lib/components/table/index.ts @@ -0,0 +1 @@ +export * from "./table"; diff --git a/libs/ui-shared/src/lib/components/table/table.spec.tsx b/libs/ui-shared/src/lib/components/table/table.spec.tsx new file mode 100644 index 000000000..d635f2bcf --- /dev/null +++ b/libs/ui-shared/src/lib/components/table/table.spec.tsx @@ -0,0 +1,46 @@ +import { render } from "@testing-library/react"; +import { + createColumnHelper, + useReactTable, + getCoreRowModel, +} from "@tanstack/react-table"; +import TableLayout from "./table"; + +describe("Table", () => { + it("should render successfully", () => { + type TestType = { + property1: string; + property2: string; + }; + const columnHelper = createColumnHelper(); + + const columns = [ + columnHelper.accessor("property1", { + header: "Property 1", + }), + columnHelper.accessor("property2", { + header: "Property 2", + }), + ]; + + const data: TestType[] = [ + { + property1: "value1", + property2: "value2", + }, + { + property1: "value3", + property2: "value4", + }, + ]; + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + const { baseElement } = render(); + expect(baseElement).toBeTruthy(); + }); +}); diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx new file mode 100644 index 000000000..cfde52d0f --- /dev/null +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -0,0 +1,68 @@ +import { Table, flexRender } from "@tanstack/react-table"; +import styled from "styled-components"; +import { size } from "../../theme"; +import { List, ListDivider } from "../list"; + +export const CenteredCell = styled.div` + text-align: center; + height: ${size("listElementHeight")}; + overflow: hidden; +`; + +export const TableLayout = ({ table }: { table: Table }) => { + return ( + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + + + {table.getFooterGroups().map((footerGroup) => ( + + {footerGroup.headers.map((header) => ( + + ))} + + ))} + +
+ + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + +
+ + {flexRender(cell.column.columnDef.cell, cell.getContext())} + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.footer, + header.getContext(), + )} +
+
+ ); +}; + +export default TableLayout; From b44c9ae52beb65afa36450c26679c768677d72bf Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Tue, 28 Feb 2023 09:48:28 +0000 Subject: [PATCH 072/500] refactor: nested routes --- apps/editor/src/app/app.tsx | 25 +++++----- .../menu/datasets-grid/datasets-grid.tsx | 50 +++++++------------ 2 files changed, 31 insertions(+), 44 deletions(-) diff --git a/apps/editor/src/app/app.tsx b/apps/editor/src/app/app.tsx index decea1982..f0f79bced 100644 --- a/apps/editor/src/app/app.tsx +++ b/apps/editor/src/app/app.tsx @@ -85,19 +85,18 @@ function App(): JSX.Element { {hubBaseUrl ? ( - } /> - } - /> - } - /> - } - /> + + } /> + + } /> + } /> + + } + /> + + } /> ) : ( diff --git a/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx index 5d4812ebd..238cb6d22 100644 --- a/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx +++ b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx @@ -1,11 +1,4 @@ -import { - Box, - InvisibleButton, - Modal, - Screen, - Text, - useTranslation, -} from "@visian/ui-shared"; +import { Modal, Text, useTranslation } from "@visian/ui-shared"; import { useParams } from "react-router-dom"; import styled from "styled-components"; @@ -25,30 +18,25 @@ export const DatasetsGrid = () => { useDatasetsBy(projectId); const { t: translate } = useTranslation(); - return ( - // eslint-disable-next-line react/jsx-no-useless-fragment - <> - {isLoadingDatasets || isErrorDatasets ? ( - - {isLoadingDatasets ? ( - {translate("project-loading")} - ) : ( - - {`${translate("project-loading-error")} ${ - datasetsError?.response?.statusText - } (${datasetsError?.response?.status})`} - - )} - + return isLoadingDatasets || isErrorDatasets ? ( + + {isLoadingDatasets ? ( + {translate("project-loading")} ) : ( - - {datasets && datasets.length > 0 ? ( - - ) : ( - {translate("no-datasets-available")} - )} - + + {`${translate("project-loading-error")} ${ + datasetsError?.response?.statusText + } (${datasetsError?.response?.status})`} + )} - + + ) : ( + + {datasets && datasets.length > 0 ? ( + + ) : ( + {translate("no-datasets-available")} + )} + ); }; From f05c9cc1c28a21ae17351c7409d00d675e250155 Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Tue, 28 Feb 2023 17:51:48 +0000 Subject: [PATCH 073/500] refactor: Fix linting bugs --- .../src/components/menu/job-history/job-history.tsx | 9 +-------- apps/editor/src/screens/project-datasets-screen.tsx | 2 +- apps/editor/src/screens/project-jobs-screen.tsx | 6 ++---- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index 299473e76..5e94862eb 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -1,8 +1,7 @@ -import { Box, Modal, Text, useTranslation } from "@visian/ui-shared"; +import { Modal, Text, useTranslation } from "@visian/ui-shared"; import styled from "styled-components"; import { useJobs } from "../../../queries"; -import { ProjectViewSwitch } from "../project-view-switch"; import { JobList } from "./job-list"; const StyledModal = styled(Modal)` @@ -11,12 +10,6 @@ const StyledModal = styled(Modal)` position: relative; `; -const StyledProjectViewSwitch = styled(Box)` - display flex; - justify-content: center; - width: 100%; -`; - export const JobHistory = () => { const { jobs, jobsError, isErrorJobs, isLoadingJobs } = useJobs(); diff --git a/apps/editor/src/screens/project-datasets-screen.tsx b/apps/editor/src/screens/project-datasets-screen.tsx index df72c103f..e68082a75 100644 --- a/apps/editor/src/screens/project-datasets-screen.tsx +++ b/apps/editor/src/screens/project-datasets-screen.tsx @@ -34,7 +34,7 @@ const IconButton = styled(InvisibleButton)` z-index: 51; `; -//TODO z-index logic +// TODO z-index logic export const ProjectDatasetsScreen: React.FC = observer(() => { const navigate = useNavigate(); diff --git a/apps/editor/src/screens/project-jobs-screen.tsx b/apps/editor/src/screens/project-jobs-screen.tsx index 36e70cb66..3185647b0 100644 --- a/apps/editor/src/screens/project-jobs-screen.tsx +++ b/apps/editor/src/screens/project-jobs-screen.tsx @@ -1,14 +1,12 @@ import { Box, InvisibleButton, - Modal, Screen, - Text, useTranslation, } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import styled from "styled-components"; import { JobHistory } from "../components/menu/job-history"; @@ -36,7 +34,7 @@ const IconButton = styled(InvisibleButton)` z-index: 51; `; -//TODO z-index logic +// TODO z-index logic export const ProjectJobsScreen: React.FC = observer(() => { const navigate = useNavigate(); From 6568f57c3e7e4b4144ca9337a21789403fe4cb4b Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 7 Mar 2023 11:39:42 +0100 Subject: [PATCH 074/500] feat: save popup UI --- apps/editor/src/assets/de.json | 1 + apps/editor/src/assets/en.json | 1 + .../src/components/editor/save-popup/index.ts | 2 + .../editor/save-popup/save-popup.props.ts | 3 + .../editor/save-popup/save-popup.tsx | 81 +++++++++++++++++++ .../editor/ui-overlay/ui-overlay.tsx | 30 ++++--- .../src/lib/components/icon/icons/index.ts | 1 + .../src/lib/components/icon/icons/save.svg | 1 + 8 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 apps/editor/src/components/editor/save-popup/index.ts create mode 100644 apps/editor/src/components/editor/save-popup/save-popup.props.ts create mode 100644 apps/editor/src/components/editor/save-popup/save-popup.tsx create mode 100644 libs/ui-shared/src/lib/components/icon/icons/save.svg diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index f7cf0dd9a..a52aca3a6 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -281,6 +281,7 @@ "annotations-loading": "Annotationen werden geladen...", "annotations-loading-error": "Fehler beim Laden der Annotationen:", "annotation-open-error": "Fehler beim Öffnen der Annotation", + "save-annotation": "Annotationsebene speichern", "ml-model-selection-description": "Wähle das Modell mit dem die Bilder annotiert werden sollen", "ml-model-selection-title": "Modell Auswahl", diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index b6ffa7762..32236e6cb 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -281,6 +281,7 @@ "annotations-loading": "Loading Annotations...", "annotations-loading-error": "Error on loading Annotations:", "annotation-open-error": "Error on opening annotation", + "save-annotation": "Save annotation layer", "ml-model-selection-description": "Select the model you want to use to annotate the images", "ml-model-selection-title": "Model Selection", diff --git a/apps/editor/src/components/editor/save-popup/index.ts b/apps/editor/src/components/editor/save-popup/index.ts new file mode 100644 index 000000000..d579ee08c --- /dev/null +++ b/apps/editor/src/components/editor/save-popup/index.ts @@ -0,0 +1,2 @@ +export * from "./save-popup"; +export * from "./save-popup.props"; diff --git a/apps/editor/src/components/editor/save-popup/save-popup.props.ts b/apps/editor/src/components/editor/save-popup/save-popup.props.ts new file mode 100644 index 000000000..d1a400795 --- /dev/null +++ b/apps/editor/src/components/editor/save-popup/save-popup.props.ts @@ -0,0 +1,3 @@ +import type { StatefulPopUpProps } from "@visian/ui-shared"; + +export type SavePopUpProps = StatefulPopUpProps; diff --git a/apps/editor/src/components/editor/save-popup/save-popup.tsx b/apps/editor/src/components/editor/save-popup/save-popup.tsx new file mode 100644 index 000000000..c8cb4a0f0 --- /dev/null +++ b/apps/editor/src/components/editor/save-popup/save-popup.tsx @@ -0,0 +1,81 @@ +/* eslint-disable max-len */ +import { Button, PopUp, Text, TextField } from "@visian/ui-shared"; +import { observer } from "mobx-react-lite"; +import React, { useState } from "react"; +import styled from "styled-components"; + +import { useStore } from "../../../app/root-store"; +import { SavePopUpProps } from "./save-popup.props"; + +const SectionLabel = styled(Text)` + font-size: 14px; + margin-bottom: 8px; +`; + +const SaveInput = styled(TextField)` + margin: 0px 10px 0px 0px; + width: 100%; +`; + +const SaveButton = styled(Button)` + min-width: 110px; +`; + +const InlineRow = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + width: 100%; + margin-bottom: 28px; +`; + +const InlineRowLast = styled(InlineRow)` + margin-bottom: 10px; +`; + +const SavePopUpContainer = styled(PopUp)` + align-items: left; + width: 500px; +`; + +export const SavePopUp = observer(({ isOpen, onClose }) => { + const store = useStore(); + const [newAnnotationURI, setnewAnnotationURI] = useState(""); + + const saveAnnotation = () => { + console.log("saved annotation"); + }; + + return ( + + {store?.editor.activeDocument?.activeLayer?.metaDataId && ( + <> + + + + + + + )} + + + + + + + ); +}); diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index 946a02a37..0fbba3958 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -31,6 +31,7 @@ import { Layers } from "../layers"; import { MeasurementPopUp } from "../measurement-popup"; import { Menu } from "../menu"; import { ProgressPopUp } from "../progress-popup"; +import { SavePopUp } from "../save-popup"; import { ServerPopUp } from "../server-popup"; import { SettingsPopUp } from "../settings-popup"; import { ShortcutPopUp } from "../shortcut-popup"; @@ -186,6 +187,15 @@ export const UIOverlay = observer( store?.editor.activeDocument?.tools.setIsCursorOverFloatingUI(false); }, [store]); + // Save Pop Up Toggling + const [isSavePopUpOpen, setIsSavePopUpOpen] = useState(false); + const openSavePopUp = useCallback(() => { + setIsSavePopUpOpen(true); + }, []); + const closeSavePopUp = useCallback(() => { + setIsSavePopUpOpen(false); + }, []); + // Export Button const exportZip = useCallback(() => { store?.setProgress({ labelTx: "exporting" }); @@ -330,16 +340,15 @@ export const UIOverlay = observer( - { - postToURL(); - console.log("saved"); - }} - isActive={false} - /> + {store?.editor.activeDocument?.activeLayer?.isAnnotation && ( + + )} {!isFromWHO() && ( ( )} onClose={store?.editor.activeDocument?.setMeasurementDisplayLayer} /> + {isDraggedOver && } {store?.progress && ( \ No newline at end of file From 6bae91d89e9f4d13d7e6a888c36060c33d083a86 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Tue, 7 Mar 2023 14:35:01 +0100 Subject: [PATCH 075/500] TEMP: BUILD OWN TABLE WITH VISIAN COMPONENTS --- .../menu/job-history/job-list-item.tsx | 4 - .../components/menu/job-history/job-list.tsx | 25 ++- libs/ui-shared/src/lib/components/index.ts | 1 + .../src/lib/components/table/table.tsx | 183 ++++++++++++------ 4 files changed, 144 insertions(+), 69 deletions(-) diff --git a/apps/editor/src/components/menu/job-history/job-list-item.tsx b/apps/editor/src/components/menu/job-history/job-list-item.tsx index b67a4c029..47c2278fb 100644 --- a/apps/editor/src/components/menu/job-history/job-list-item.tsx +++ b/apps/editor/src/components/menu/job-history/job-list-item.tsx @@ -7,10 +7,6 @@ const Spacer = styled.div` width: 10px; `; -const ExpandedSpacer = styled.div` - margin-right: auto; -`; - export const JobListItem = ({ data, isHeader, diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-list.tsx index 2187e944a..c310b6cfb 100644 --- a/apps/editor/src/components/menu/job-history/job-list.tsx +++ b/apps/editor/src/components/menu/job-history/job-list.tsx @@ -1,13 +1,12 @@ import { Job } from "../../../types"; import { getDisplayDate } from "../util/display-date"; -import { TableLayout } from "../../../../../../libs/ui-shared/src/lib/components/table"; import { createColumnHelper, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; -import { ListItemLabel } from "@visian/ui-shared"; +import { TableLayout, ListItemLabel, createColumn } from "@visian/ui-shared"; function getDisplayJob(job: Job): Job { return { @@ -20,7 +19,7 @@ function getDisplayJob(job: Job): Job { const columnHelper = createColumnHelper(); -const columns = [ +const columns2 = [ columnHelper.accessor("modelName", { header: () => , cell: (props) => , @@ -43,13 +42,21 @@ const columns = [ }), ]; +const columns = [ + { accessor: "modelName", header: "Model", width: 20 }, + { accessor: "modelVersion", header: "Version", width: 10 }, + { accessor: "startedAt", header: "Started At", width: 25 }, + { accessor: "finishedAt", header: "Finished At", width: 25 }, + { accessor: "status", header: "Status", width: 20 }, +]; + export const JobsTable = ({ jobs }: { jobs: Job[] }) => { const data = jobs.map((job: Job) => getDisplayJob(job)); - const table = useReactTable({ - data, - columns, - getCoreRowModel: getCoreRowModel(), - }); - return ; + // const table = useReactTable({ + // data, + // columns, + // getCoreRowModel: getCoreRowModel(), + // }); + return ; }; diff --git a/libs/ui-shared/src/lib/components/index.ts b/libs/ui-shared/src/lib/components/index.ts index 658d5933c..6c04cec2f 100644 --- a/libs/ui-shared/src/lib/components/index.ts +++ b/libs/ui-shared/src/lib/components/index.ts @@ -17,6 +17,7 @@ export * from "./screen"; export * from "./sheet"; export * from "./slider"; export * from "./switch"; +export * from "./table"; export * from "./text"; export * from "./text-field"; export * from "./text-input"; diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx index cfde52d0f..db5b24c0d 100644 --- a/libs/ui-shared/src/lib/components/table/table.tsx +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -1,68 +1,139 @@ -import { Table, flexRender } from "@tanstack/react-table"; import styled from "styled-components"; -import { size } from "../../theme"; -import { List, ListDivider } from "../list"; +import { fontWeight } from "../../theme"; +import { List, ListItem, ListItemLabel } from "../list"; -export const CenteredCell = styled.div` +export const TableCell = styled.div.attrs((props: { width?: number }) => props)` + width: ${(props) => (props.width ? props.width : 20)}%; text-align: center; - height: ${size("listElementHeight")}; - overflow: hidden; + margin: auto; `; -export const TableLayout = ({ table }: { table: Table }) => { +export const HeaderLabel = styled(ListItemLabel)` + font-weight: ${fontWeight("bold")}; +`; + +export const TableRow = ({ + data, + columns, +}: { + data: any; + columns: Column[]; +}) => { + const widths = columns.map((column: Column) => column.width); + + return ( + + + {data.modelName} + + + {data.modelVersion} + + + {data.startedAt} + + + {data.finishedAt} + + + {data.status} + + + ); +}; + +export const TableHeader = ({ columns }: { columns: Column[] }) => { + const headers = columns.map((column: Column) => column.header); + return ( + + {headers.map((header: string) => ( + + {header} + + ))} + + ); +}; + +export type Column = { + accessor: string; + header: string; + cell?: any; + width?: number; +}; + +export const TableLayout = ({ + columns, + data, +}: { + columns: Column[]; + data: any; +}) => { return ( - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - ))} - - ))} - - - {table.getFooterGroups().map((footerGroup) => ( - - {footerGroup.headers.map((header) => ( - - ))} - - ))} - -
- - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - -
- - {flexRender(cell.column.columnDef.cell, cell.getContext())} - -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.footer, - header.getContext(), - )} -
+ + + {data.map((rowData: any) => ( + + ))}
); }; +// export const TableLayout = ({ table }: { table: Table }) => { +// return ( +// +// +// +// {table.getHeaderGroups().map((headerGroup) => ( +// +// {headerGroup.headers.map((header) => ( +// +// ))} +// +// ))} +// +// +// {table.getRowModel().rows.map((row) => ( +// +// {row.getVisibleCells().map((cell) => ( +// +// ))} +// +// ))} +// +// +// {table.getFooterGroups().map((footerGroup) => ( +// +// {footerGroup.headers.map((header) => ( +// +// ))} +// +// ))} +// +//
+// +// {header.isPlaceholder +// ? null +// : flexRender( +// header.column.columnDef.header, +// header.getContext(), +// )} +// +//
+// +// {flexRender(cell.column.columnDef.cell, cell.getContext())} +// +//
+// {header.isPlaceholder +// ? null +// : flexRender( +// header.column.columnDef.footer, +// header.getContext(), +// )} +//
+//
+// ); +// }; + export default TableLayout; From 021fdaaaea8408b5ef2c3b1247db77f6605a386e Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Tue, 7 Mar 2023 16:04:11 +0100 Subject: [PATCH 076/500] refactor: use visian components for table component --- .../components/menu/job-history/job-list.tsx | 43 +++-- .../src/lib/components/table/table.tsx | 158 +++++++----------- 2 files changed, 78 insertions(+), 123 deletions(-) diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-list.tsx index c310b6cfb..6e4aaeff8 100644 --- a/apps/editor/src/components/menu/job-history/job-list.tsx +++ b/apps/editor/src/components/menu/job-history/job-list.tsx @@ -1,12 +1,13 @@ import { Job } from "../../../types"; import { getDisplayDate } from "../util/display-date"; - +import styled from "styled-components"; +import { fontWeight } from "@visian/ui-shared"; import { createColumnHelper, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; -import { TableLayout, ListItemLabel, createColumn } from "@visian/ui-shared"; +import { TableLayout, ListItemLabel } from "@visian/ui-shared"; function getDisplayJob(job: Job): Job { return { @@ -17,46 +18,44 @@ function getDisplayJob(job: Job): Job { }; } +export const HeaderLabel = styled(ListItemLabel)` + font-weight: ${fontWeight("bold")}; +`; + const columnHelper = createColumnHelper(); -const columns2 = [ +const columns = [ columnHelper.accessor("modelName", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("modelVersion", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("startedAt", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("finishedAt", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("status", { - header: () => , + header: () => , cell: (props) => , }), ]; -const columns = [ - { accessor: "modelName", header: "Model", width: 20 }, - { accessor: "modelVersion", header: "Version", width: 10 }, - { accessor: "startedAt", header: "Started At", width: 25 }, - { accessor: "finishedAt", header: "Finished At", width: 25 }, - { accessor: "status", header: "Status", width: 20 }, -]; - export const JobsTable = ({ jobs }: { jobs: Job[] }) => { const data = jobs.map((job: Job) => getDisplayJob(job)); - // const table = useReactTable({ - // data, - // columns, - // getCoreRowModel: getCoreRowModel(), - // }); - return ; + const columnWidths = [20, 10, 25, 25, 20]; + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + return ; }; diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx index db5b24c0d..4c3a494c3 100644 --- a/libs/ui-shared/src/lib/components/table/table.tsx +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -1,6 +1,6 @@ +import { Cell, flexRender, Header, Table } from "@tanstack/react-table"; import styled from "styled-components"; -import { fontWeight } from "../../theme"; -import { List, ListItem, ListItemLabel } from "../list"; +import { List, ListItem } from "../list"; export const TableCell = styled.div.attrs((props: { width?: number }) => props)` width: ${(props) => (props.width ? props.width : 20)}%; @@ -8,132 +8,88 @@ export const TableCell = styled.div.attrs((props: { width?: number }) => props)` margin: auto; `; -export const HeaderLabel = styled(ListItemLabel)` - font-weight: ${fontWeight("bold")}; -`; +const distributeColumns = ( + columnWidths: number[] | undefined, + columnCount: number, +) => { + if (columnWidths && columnWidths.length === columnCount) { + const sum = columnWidths.reduce((partSum, width) => partSum + width, 0); + if (sum == 100) { + return columnWidths; + } + } + return Array(columnCount).fill(100 / columnCount); +}; export const TableRow = ({ - data, - columns, + cells, + columnWidths, }: { - data: any; - columns: Column[]; + cells: Cell[]; + columnWidths: number[]; }) => { - const widths = columns.map((column: Column) => column.width); - return ( - - {data.modelName} - - - {data.modelVersion} - - - {data.startedAt} - - - {data.finishedAt} - - - {data.status} - + {cells.map((cell, index) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} ); }; -export const TableHeader = ({ columns }: { columns: Column[] }) => { - const headers = columns.map((column: Column) => column.header); +export const TableHeader = ({ + headers, + columnWidths, +}: { + headers: Header[]; + columnWidths: number[]; +}) => { return ( - {headers.map((header: string) => ( - - {header} + {headers.map((header, index) => ( + + {header.isPlaceholder + ? null + : flexRender(header.column.columnDef.header, header.getContext())} ))} ); }; -export type Column = { - accessor: string; - header: string; - cell?: any; - width?: number; -}; - export const TableLayout = ({ - columns, - data, + table, + columnWidths, }: { - columns: Column[]; - data: any; + table: Table; + columnWidths?: number[]; }) => { + // We support only rendering the first header group + const mainHeaderGroup = table.getHeaderGroups()[0]; + const widths = distributeColumns( + columnWidths, + mainHeaderGroup.headers.length, + ); + return ( - + - {data.map((rowData: any) => ( - + {table.getRowModel().rows.map((row) => ( + ))} ); }; -// export const TableLayout = ({ table }: { table: Table }) => { -// return ( -// -// -// -// {table.getHeaderGroups().map((headerGroup) => ( -// -// {headerGroup.headers.map((header) => ( -// -// ))} -// -// ))} -// -// -// {table.getRowModel().rows.map((row) => ( -// -// {row.getVisibleCells().map((cell) => ( -// -// ))} -// -// ))} -// -// -// {table.getFooterGroups().map((footerGroup) => ( -// -// {footerGroup.headers.map((header) => ( -// -// ))} -// -// ))} -// -//
-// -// {header.isPlaceholder -// ? null -// : flexRender( -// header.column.columnDef.header, -// header.getContext(), -// )} -// -//
-// -// {flexRender(cell.column.columnDef.cell, cell.getContext())} -// -//
-// {header.isPlaceholder -// ? null -// : flexRender( -// header.column.columnDef.footer, -// header.getContext(), -// )} -//
-//
-// ); -// }; - export default TableLayout; From 599d5aa625056c316922ba94d5ef52056702715c Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Tue, 7 Mar 2023 16:40:27 +0100 Subject: [PATCH 077/500] feat: add internationalization to jobs history --- apps/editor/src/assets/de.json | 15 +++++++++++- apps/editor/src/assets/en.json | 15 +++++++++++- .../components/menu/job-history/job-list.tsx | 24 +++++++++---------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index 6e1d06f1e..ea0bc064c 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -291,5 +291,18 @@ "ml-model-selection-description": "Wähle das Modell mit dem die Bilder annotiert werden sollen", "ml-model-selection-title": "Modell Auswahl", "ml-models-loading": "Modelle werden geladen...", - "ml-models-loading-error": "Fehler beim Laden der Modelle:" + "ml-models-loading-error": "Fehler beim Laden der Modelle:", + + "jobs-loading": "Jobs werden geladen...", + "jobs-loading-error": "Fehler beim Laden der Jobs:", + "job-model-name": "Modell", + "job-model-version": "Version", + "job-started": "Gestartet am", + "job-finished": "Beendet am", + "job-status": "Status", + "job-status-queued": "In Warteschlange", + "job-status-running": "Wird ausgeführt", + "job-status-succeeded": "Erfolgreich", + "job-status-canceled": "Abgebrochen", + "job-status-failed": "Fehlgeschlagen" } diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 56e7c7164..00c0175c7 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -291,5 +291,18 @@ "ml-model-selection-description": "Select the model you want to use to annotate the images", "ml-model-selection-title": "Model Selection", "ml-models-loading": "models loading...", - "ml-models-loading-error": "Error on loading ml models:" + "ml-models-loading-error": "Error on loading ml models:", + + "jobs-loading": "Loading Jobs...", + "jobs-loading-error": "Error on loading Jobs:", + "job-model-name": "Model", + "job-model-version": "Version", + "job-started": "Started At", + "job-finished": "Finished At", + "job-status": "Status", + "job-status-queued": "queued", + "job-status-running": "running", + "job-status-succeeded": "succeeded", + "job-status-canceled": "canceled", + "job-status-failed": "failed" } diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-list.tsx index 6e4aaeff8..c46cc6fc2 100644 --- a/apps/editor/src/components/menu/job-history/job-list.tsx +++ b/apps/editor/src/components/menu/job-history/job-list.tsx @@ -9,6 +9,12 @@ import { } from "@tanstack/react-table"; import { TableLayout, ListItemLabel } from "@visian/ui-shared"; +export const HeaderLabel = styled(ListItemLabel)` + font-weight: ${fontWeight("bold")}; +`; + +const columnHelper = createColumnHelper(); + function getDisplayJob(job: Job): Job { return { ...job, @@ -18,32 +24,26 @@ function getDisplayJob(job: Job): Job { }; } -export const HeaderLabel = styled(ListItemLabel)` - font-weight: ${fontWeight("bold")}; -`; - -const columnHelper = createColumnHelper(); - const columns = [ columnHelper.accessor("modelName", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("modelVersion", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("startedAt", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("finishedAt", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("status", { - header: () => , - cell: (props) => , + header: () => , + cell: (props) => , }), ]; From 7d0a04eb5a19a5faf2453a5b710b8f67be49b0a2 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Tue, 7 Mar 2023 18:43:48 +0100 Subject: [PATCH 078/500] feat: posting annotations and patching annotation files on server --- apps/editor/src/assets/de.json | 5 +- apps/editor/src/assets/en.json | 5 +- .../editor/save-popup/save-popup.tsx | 110 ++++++++++++++++-- .../editor/ui-overlay/ui-overlay.tsx | 25 +--- apps/editor/src/models/editor/document.ts | 45 +------ apps/editor/src/querys/index.ts | 1 + apps/editor/src/querys/use-annotations-by.tsx | 33 ++++-- apps/editor/src/querys/use-files.tsx | 19 ++- libs/ui-shared/src/lib/types/editor/layers.ts | 2 +- 9 files changed, 157 insertions(+), 88 deletions(-) diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index a52aca3a6..400968f9d 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -139,6 +139,7 @@ "server-import-error": "Beim Laden der Datei vom Server ist ein Fehler aufgetreten: \"{{statusText}}\"", "export-error": "Exportfehler", + "saving-error": "Speicherfehler", "file-upload-error": "Die Datei konnte nicht hochgeladen werden.", "tracking-data-mismatch-error": "Der Tracking-Log passt nicht zum geladenen Bild.", @@ -281,7 +282,9 @@ "annotations-loading": "Annotationen werden geladen...", "annotations-loading-error": "Fehler beim Laden der Annotationen:", "annotation-open-error": "Fehler beim Öffnen der Annotation", - "save-annotation": "Annotationsebene speichern", + "annotation-saving": "Annotationsebene speichern", + "annotation-saving-error": "Fehler beim Speichern der Annotationsebene", + "annotation-saving-as-error": "Fehler beim Speichern Unter der Annotationsebene", "ml-model-selection-description": "Wähle das Modell mit dem die Bilder annotiert werden sollen", "ml-model-selection-title": "Modell Auswahl", diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 32236e6cb..fb99503e4 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -139,6 +139,7 @@ "server-import-error": "An error occurred while getting the file from the server: \"{{statusText}}\"", "export-error": "Export Error", + "saving-error": "Saving Error", "file-upload-error": "The file could not be uploaded.", "tracking-data-mismatch-error": "The tracking log does not belong to the loaded image.", @@ -281,7 +282,9 @@ "annotations-loading": "Loading Annotations...", "annotations-loading-error": "Error on loading Annotations:", "annotation-open-error": "Error on opening annotation", - "save-annotation": "Save annotation layer", + "annotation-saving": "Save active annotation layer", + "annotation-saving-error": "Error saving annotation layer", + "annotation-saving-as-error": "Error saving new annotaion layer", "ml-model-selection-description": "Select the model you want to use to annotate the images", "ml-model-selection-title": "Model Selection", diff --git a/apps/editor/src/components/editor/save-popup/save-popup.tsx b/apps/editor/src/components/editor/save-popup/save-popup.tsx index c8cb4a0f0..a6716486b 100644 --- a/apps/editor/src/components/editor/save-popup/save-popup.tsx +++ b/apps/editor/src/components/editor/save-popup/save-popup.tsx @@ -1,10 +1,17 @@ -/* eslint-disable max-len */ import { Button, PopUp, Text, TextField } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; -import React, { useState } from "react"; +import path from "path"; +import React, { useCallback, useEffect, useState } from "react"; +import { useSearchParams } from "react-router-dom"; import styled from "styled-components"; import { useStore } from "../../../app/root-store"; +import { + deleteAnnotation, + patchAnnotationFile, + postAnnotation, +} from "../../../querys"; +import { Annotation } from "../../../types"; import { SavePopUpProps } from "./save-popup.props"; const SectionLabel = styled(Text)` @@ -41,12 +48,85 @@ const SavePopUpContainer = styled(PopUp)` export const SavePopUp = observer(({ isOpen, onClose }) => { const store = useStore(); + const [searchParams] = useSearchParams(); const [newAnnotationURI, setnewAnnotationURI] = useState(""); - const saveAnnotation = () => { - console.log("saved annotation"); + const saveAnnotation = async (annotation: Annotation | undefined) => { + const annotationLayer = store?.editor.activeDocument?.activeLayer; + if (annotationLayer) { + const annotationFile = await annotationLayer.toFile(); + const annotationMeta = annotation || annotationLayer.metaData; + try { + if (!annotationMeta || !annotationFile) + throw new Error("Could not create annotation file"); + if ( + path.extname(annotationMeta.dataUri) !== + path.extname(annotationFile.name) + ) { + throw new Error("URI does not match file type"); + } + const response = await patchAnnotationFile( + annotationMeta as Annotation, + annotationFile, + ); + onClose?.(); + return response; + } catch (error: any) { + const description = error.response?.data?.message + ? error.response.data.message + : error.message + ? error.message + : "annotation-saving-error"; + store?.setError({ + titleTx: "saving-error", + descriptionTx: description, + }); + } + } }; + const saveAnnotationAs = async (uri: string) => { + const imageId = searchParams.get("imageId"); + const annotationLayer = store?.editor.activeDocument?.activeLayer; + try { + if (!imageId || !annotationLayer || !annotationLayer.isAnnotation) { + throw new Error("No image or annotation layer selected"); + } + const annotation = await postAnnotation(imageId, uri); + const savedAnnotation = await saveAnnotation(annotation); + if (!savedAnnotation) { + await deleteAnnotation(annotation.id); + } else { + store?.destroyRedirect("/", true); + } + } catch (error: any) { + const description = error.response?.data?.message + ? error.response.data.message + : error.message + ? error.message + : "annotation-saving-as-error"; + store?.setError({ + titleTx: "saving-error", + descriptionTx: description, + }); + } + }; + + const getAnnotationURISuggestion = useCallback(() => { + const annotationLayerName = + store?.editor.activeDocument?.activeLayer?.title; + const imageURI = + store?.editor.activeDocument?.mainImageLayer?.metaData?.dataUri; + const imageName = path.basename(imageURI); + return `/annotations/${imageName}/${annotationLayerName}`; + }, [store]); + + useEffect(() => { + if (isOpen) { + setnewAnnotationURI(getAnnotationURISuggestion()); + } + }, [isOpen, getAnnotationURISuggestion]); + return ( (({ isOpen, onClose }) => { dismiss={onClose} shouldDismissOnOutsidePress > - {store?.editor.activeDocument?.activeLayer?.metaDataId && ( + {store?.editor.activeDocument?.activeLayer?.metaData?.dataUri && ( <> - + { + saveAnnotation(undefined); + store?.destroyRedirect("/", true); + }} + /> )} @@ -74,7 +161,12 @@ export const SavePopUp = observer(({ isOpen, onClose }) => { value={newAnnotationURI} onChangeText={setnewAnnotationURI} /> - + { + saveAnnotationAs(newAnnotationURI); + }} + /> ); diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index 0fbba3958..343dd4331 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -15,8 +15,8 @@ import styled from "styled-components"; import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; import { importFilesToDocument } from "../../../import-handling"; +import { fetchAnnotationFile, fetchImageFile } from "../../../querys"; import { hubBaseUrl } from "../../../querys/hub-base-url"; -import { fetchAnnotationFile, fetchImageFile } from "../../../querys/use-files"; import { DilateErodeModal, MeasurementModal, @@ -217,27 +217,6 @@ export const UIOverlay = observer( return storedObject; }; - const postToURL = () => { - if (sessionStorage.getItem("ImageToOpen")) { - // const annotation = loadSessionStorage("AnnotationToOpen"); - const image = loadSessionStorage("ImageToOpen"); - const url = `${hubBaseUrl}annotations/new`; - if (url) { - store?.setProgress({ labelTx: "exporting" }); - store?.editor.activeDocument - ?.postToURL(url, image.id, true) - .catch() - .then(() => { - store?.setProgress(); - sessionStorage.removeItem("ImageToOpen"); - sessionStorage.removeItem("AnnotationToOpen"); - // TODO: use navigate - store?.destroyRedirect("/", true); - }); - } - } - }; - const [searchParams] = useSearchParams(); const loadImagesAndAnnotations = () => { async function asyncfunc() { @@ -343,7 +322,7 @@ export const UIOverlay = observer( {store?.editor.activeDocument?.activeLayer?.isAnnotation && ( { - const zip = new Zip(); - - // TODO: Rework for group layers - const files = await Promise.all( - this.layers - .filter((layer) => !limitToAnnotations || layer.isAnnotation) - .map((layer) => layer.toFile()), - ); - files.forEach((file, index) => { - if (!file) return; - zip.setFile(`${`00${index}`.slice(-2)}_${file.name}`, file); - }); - - if (this.context?.getTracker()?.isActive) { - const trackingFile = this.context.getTracker()?.toFile(); - if (trackingFile) zip.setFile(trackingFile.name, trackingFile); - } - const fileBlob: any = await zip.toBlob(); - // console.log("") - - const formData = new FormData(); - formData.append("fileName", `${this.title}.zip`); - formData.append("image", imageId); - formData.append("file", fileBlob); - axios - .post(url, formData, { - headers: { - "Content-Type": "multipart/form-data", - }, - }) - .then((response) => { - console.log(response); - }) - .catch((error) => { - console.log(error); - }); - }; - public getFileForLayer = async (idOrLayer: string | ILayer) => { const layerId = typeof idOrLayer === "string" ? idOrLayer : idOrLayer.id; const layer = this.layerMap[layerId]; @@ -910,7 +867,7 @@ export class Document const layer = this.getLayer(createdLayerId); if (layer && "metadata" in file) { const fileWithMetaData = file as FileWithMetadata; - layer.metaDataId = fileWithMetaData.metadata.id; + layer.metaData = fileWithMetaData.metadata; } } diff --git a/apps/editor/src/querys/index.ts b/apps/editor/src/querys/index.ts index 94667f858..475527533 100644 --- a/apps/editor/src/querys/index.ts +++ b/apps/editor/src/querys/index.ts @@ -2,3 +2,4 @@ export * from "./use-annotations-by"; export * from "./use-dataset"; export * from "./use-images-by"; export * from "./use-ml-models"; +export * from "./use-files"; diff --git a/apps/editor/src/querys/use-annotations-by.tsx b/apps/editor/src/querys/use-annotations-by.tsx index cac4ac282..cbe5e59d5 100644 --- a/apps/editor/src/querys/use-annotations-by.tsx +++ b/apps/editor/src/querys/use-annotations-by.tsx @@ -16,13 +16,6 @@ const getAnnotationsBy = async (imageId: string) => { return annotationsResponse.data; }; -export const getAnnotation = async (annotationId: string) => { - const annotationsResponse = await axios.get( - `${hubBaseUrl}annotations/${annotationId}`, - ); - return annotationsResponse.data; -}; - export const useAnnotationsBy = (imageId: string) => { const { data, error, isError, isLoading, refetch, remove } = useQuery< Annotation[], @@ -41,5 +34,29 @@ export const useAnnotationsBy = (imageId: string) => { removeAnnotations: remove, }; }; - export default useAnnotationsBy; + +export const getAnnotation = async (annotationId: string) => { + const annotationsResponse = await axios.get( + `${hubBaseUrl}annotations/${annotationId}`, + ); + return annotationsResponse.data; +}; + +export const postAnnotation = async (imageId: string, dataUri: string) => { + const annotationsResponse = await axios.post( + `${hubBaseUrl}annotations`, + { + image: imageId, + dataUri, + }, + ); + return annotationsResponse.data; +}; + +export const deleteAnnotation = async (annotationId: string) => { + const annotationsResponse = await axios.delete( + `${hubBaseUrl}annotations/${annotationId}`, + ); + return annotationsResponse; +}; diff --git a/apps/editor/src/querys/use-files.tsx b/apps/editor/src/querys/use-files.tsx index 449abc8b3..d59835a6c 100644 --- a/apps/editor/src/querys/use-files.tsx +++ b/apps/editor/src/querys/use-files.tsx @@ -1,6 +1,7 @@ +import axios from "axios"; import path from "path"; -import { FileWithMetadata } from "../types"; +import { Annotation, FileWithMetadata } from "../types"; import hubBaseUrl from "./hub-base-url"; import { getAnnotation } from "./use-annotations-by"; import { getImage } from "./use-images-by"; @@ -49,3 +50,19 @@ export const fetchAnnotationFile = async ( annotationFile.metadata = annotation; return annotationFile; }; + +export const patchAnnotationFile = async ( + annotation: Annotation, + file: File, +): Promise => { + const apiEndpoint = `${hubBaseUrl}annotations/${annotation.id}/file`; + const formData = new FormData(); + formData.append("file", file); + formData.append("dataUri", annotation.dataUri); + const response = await axios.patch(apiEndpoint, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + return response.data; +}; diff --git a/libs/ui-shared/src/lib/types/editor/layers.ts b/libs/ui-shared/src/lib/types/editor/layers.ts index e2432ffbb..a2bc2bd67 100644 --- a/libs/ui-shared/src/lib/types/editor/layers.ts +++ b/libs/ui-shared/src/lib/types/editor/layers.ts @@ -71,7 +71,7 @@ export interface ILayer { /** The layer's transform matrix used to position it during rendering. */ transformation?: Matrix4; /** The layer's meta data ID. */ - metaDataId?: string; + metaData?: { id: string; [key: string]: any }; /** * Returns all slice markers, aggregated for the layer and given view type. From 7c82f2f7fbc189776076a4021d8f47df2ce59889 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Wed, 8 Mar 2023 10:27:42 +0100 Subject: [PATCH 079/500] feat: add job status badge to jobs history --- .../menu/job-history/job-history.tsx | 2 +- .../menu/job-history/job-list-item.tsx | 28 ------------------- .../{job-list.tsx => job-table.tsx} | 17 +++++++++-- libs/ui-shared/src/lib/components/index.ts | 1 + .../src/lib/components/status-badge/index.ts | 1 + .../status-badge/status-badge.props.ts | 5 ++++ .../components/status-badge/status-badge.tsx | 27 ++++++++++++++++++ .../src/lib/components/table/table.tsx | 2 ++ libs/ui-shared/src/lib/theme/theme.ts | 8 ++++++ 9 files changed, 60 insertions(+), 31 deletions(-) delete mode 100644 apps/editor/src/components/menu/job-history/job-list-item.tsx rename apps/editor/src/components/menu/job-history/{job-list.tsx => job-table.tsx} (81%) create mode 100644 libs/ui-shared/src/lib/components/status-badge/index.ts create mode 100644 libs/ui-shared/src/lib/components/status-badge/status-badge.props.ts create mode 100644 libs/ui-shared/src/lib/components/status-badge/status-badge.tsx diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index 4d5743b57..66b41adeb 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -2,7 +2,7 @@ import { Box, Modal, Text, useTranslation } from "@visian/ui-shared"; import styled from "styled-components"; import { useJobs } from "../../../queries"; -import { JobsTable } from "./job-list"; +import { JobsTable } from "./job-table"; const StyledModal = styled(Modal)` vertical-align: middle; diff --git a/apps/editor/src/components/menu/job-history/job-list-item.tsx b/apps/editor/src/components/menu/job-history/job-list-item.tsx deleted file mode 100644 index 47c2278fb..000000000 --- a/apps/editor/src/components/menu/job-history/job-list-item.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { ListItem, Text } from "@visian/ui-shared"; -import styled from "styled-components"; - -import { Job } from "../../../types"; - -const Spacer = styled.div` - width: 10px; -`; - -export const JobListItem = ({ - data, - isHeader, -}: { - data: Job; - isHeader?: boolean; -}) => ( - - {data.modelName} - - {data.modelVersion} - - {data.startedAt} - - {data.finishedAt} - - {data.status} - -); diff --git a/apps/editor/src/components/menu/job-history/job-list.tsx b/apps/editor/src/components/menu/job-history/job-table.tsx similarity index 81% rename from apps/editor/src/components/menu/job-history/job-list.tsx rename to apps/editor/src/components/menu/job-history/job-table.tsx index c46cc6fc2..2078268f6 100644 --- a/apps/editor/src/components/menu/job-history/job-list.tsx +++ b/apps/editor/src/components/menu/job-history/job-table.tsx @@ -7,7 +7,7 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table"; -import { TableLayout, ListItemLabel } from "@visian/ui-shared"; +import { TableLayout, ListItemLabel, StatusBadge } from "@visian/ui-shared"; export const HeaderLabel = styled(ListItemLabel)` font-weight: ${fontWeight("bold")}; @@ -24,6 +24,14 @@ function getDisplayJob(job: Job): Job { }; } +const badgeColors: Record = { + queued: "veryLightGray", + running: "blueSheet", + succeeded: "greenSheet", + canceled: "orangeBorder", + failed: "redBorder", +}; + const columns = [ columnHelper.accessor("modelName", { header: () => , @@ -43,7 +51,12 @@ const columns = [ }), columnHelper.accessor("status", { header: () => , - cell: (props) => , + cell: (props) => ( + + ), }), ]; diff --git a/libs/ui-shared/src/lib/components/index.ts b/libs/ui-shared/src/lib/components/index.ts index 6c04cec2f..c22305b59 100644 --- a/libs/ui-shared/src/lib/components/index.ts +++ b/libs/ui-shared/src/lib/components/index.ts @@ -16,6 +16,7 @@ export * from "./popup"; export * from "./screen"; export * from "./sheet"; export * from "./slider"; +export * from "./status-badge"; export * from "./switch"; export * from "./table"; export * from "./text"; diff --git a/libs/ui-shared/src/lib/components/status-badge/index.ts b/libs/ui-shared/src/lib/components/status-badge/index.ts new file mode 100644 index 000000000..26625564b --- /dev/null +++ b/libs/ui-shared/src/lib/components/status-badge/index.ts @@ -0,0 +1 @@ +export * from "./status-badge"; diff --git a/libs/ui-shared/src/lib/components/status-badge/status-badge.props.ts b/libs/ui-shared/src/lib/components/status-badge/status-badge.props.ts new file mode 100644 index 000000000..828317cb6 --- /dev/null +++ b/libs/ui-shared/src/lib/components/status-badge/status-badge.props.ts @@ -0,0 +1,5 @@ +export interface StatusBadgeProps { + color: string; + text?: string; + tx?: string; +} diff --git a/libs/ui-shared/src/lib/components/status-badge/status-badge.tsx b/libs/ui-shared/src/lib/components/status-badge/status-badge.tsx new file mode 100644 index 000000000..b17dc34ed --- /dev/null +++ b/libs/ui-shared/src/lib/components/status-badge/status-badge.tsx @@ -0,0 +1,27 @@ +import styled from "styled-components"; +import { color, radius, Theme } from "../../theme"; +import { ListItemLabel } from "../list"; +import { StatusBadgeProps } from "./status-badge.props"; + +export const StatusBadgeContainer = styled.div>` + box-sizing: border-box; + padding: 1% 9%; + width: 10em; + height: fit-content; + border-radius: ${radius("default")}; + background-color: ${(props) => color(props.color as keyof Theme["colors"])}; +`; + +export const StatusBadge: React.FC = ({ + color, + text, + tx, +}) => { + return ( + + + + ); +}; + +export default StatusBadge; diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx index 4c3a494c3..b41ca74ee 100644 --- a/libs/ui-shared/src/lib/components/table/table.tsx +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -5,6 +5,8 @@ import { List, ListItem } from "../list"; export const TableCell = styled.div.attrs((props: { width?: number }) => props)` width: ${(props) => (props.width ? props.width : 20)}%; text-align: center; + display: flex; + justify-content: center; margin: auto; `; diff --git a/libs/ui-shared/src/lib/theme/theme.ts b/libs/ui-shared/src/lib/theme/theme.ts index 29198b9c5..c1bd11ff5 100644 --- a/libs/ui-shared/src/lib/theme/theme.ts +++ b/libs/ui-shared/src/lib/theme/theme.ts @@ -42,6 +42,10 @@ const colorModes = { redBorder: "rgba(202,51,69,0.5)", blueSheet: "rgba(0,133,255,0.4)", blueBorder: "rgba(0,133,255,0.6)", + greenSheet: "rgba(4,156,109,0.3)", + greenBorder: "rgba(4,156,109,0.5)", + orangeSheet: "rgba(255,107,0,0.3)", + orangeBorder: "rgba(255,107,0,0.5)", sideViewSheet: "rgba(200,200,200,0.4)", sideViewBorder: "rgba(0, 0, 0, 0.3)", @@ -68,6 +72,10 @@ const colorModes = { redBorder: "rgba(202,51,69,0.5)", blueSheet: "rgba(0,133,255,0.4)", blueBorder: "rgba(0,133,255,0.6)", + greenSheet: "rgba(4,156,109,0.4)", + greenBorder: "rgba(4,156,109,0.6)", + orangeSheet: "rgba(255,107,0,0.3)", + orangeBorder: "rgba(255,107,0,0.5)", sideViewSheet: "rgba(78, 80, 89, 0.2)", sideViewBorder: "rgba(255, 255, 255, 0.3)", From f5b7ee25ed06117f8d15f41a58b044c980a45f42 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Wed, 8 Mar 2023 14:45:25 +0100 Subject: [PATCH 080/500] feat: add default sorting to jobs history --- .../components/menu/job-history/job-table.tsx | 36 ++++++++++++++++--- apps/editor/src/types/jobs-history-types.tsx | 4 +-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/apps/editor/src/components/menu/job-history/job-table.tsx b/apps/editor/src/components/menu/job-history/job-table.tsx index 2078268f6..e6c22929c 100644 --- a/apps/editor/src/components/menu/job-history/job-table.tsx +++ b/apps/editor/src/components/menu/job-history/job-table.tsx @@ -5,22 +5,27 @@ import { fontWeight } from "@visian/ui-shared"; import { createColumnHelper, getCoreRowModel, + getSortedRowModel, + SortingState, useReactTable, } from "@tanstack/react-table"; import { TableLayout, ListItemLabel, StatusBadge } from "@visian/ui-shared"; +import React from "react"; export const HeaderLabel = styled(ListItemLabel)` font-weight: ${fontWeight("bold")}; `; -const columnHelper = createColumnHelper(); - function getDisplayJob(job: Job): Job { return { ...job, modelVersion: `v${job.modelVersion}`, - startedAt: job.startedAt ? getDisplayDate(new Date(job.startedAt)) : "", - finishedAt: job.finishedAt ? getDisplayDate(new Date(job.finishedAt)) : "", + startedAt: job.startedAt + ? getDisplayDate(new Date(job.startedAt)) + : undefined, + finishedAt: job.finishedAt + ? getDisplayDate(new Date(job.finishedAt)) + : undefined, }; } @@ -32,6 +37,8 @@ const badgeColors: Record = { failed: "redBorder", }; +const columnHelper = createColumnHelper(); + const columns = [ columnHelper.accessor("modelName", { header: () => , @@ -44,10 +51,14 @@ const columns = [ columnHelper.accessor("startedAt", { header: () => , cell: (props) => , + sortingFn: "datetime", + sortUndefined: -1, }), columnHelper.accessor("finishedAt", { header: () => , cell: (props) => , + sortingFn: "datetime", + sortUndefined: -1, }), columnHelper.accessor("status", { header: () => , @@ -57,6 +68,11 @@ const columns = [ tx={`job-status-${props.getValue()}`} /> ), + // Make sure that queued jobs are at the top + sortingFn: (rowA, rowB, id) => { + if (rowA.getValue(id) == "queued") return -1; + return 0; + }, }), ]; @@ -65,10 +81,22 @@ export const JobsTable = ({ jobs }: { jobs: Job[] }) => { const columnWidths = [20, 10, 25, 25, 20]; + const [sorting, setSorting] = React.useState([ + { id: "status", desc: false }, + { id: "finishedAt", desc: true }, + { id: "startedAt", desc: true }, + ]); + const table = useReactTable({ data, columns, + state: { sorting }, + initialState: { sorting }, + enableSorting: true, + enableMultiSort: true, + onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), }); return ; }; diff --git a/apps/editor/src/types/jobs-history-types.tsx b/apps/editor/src/types/jobs-history-types.tsx index b9026b8cf..cc42e196c 100644 --- a/apps/editor/src/types/jobs-history-types.tsx +++ b/apps/editor/src/types/jobs-history-types.tsx @@ -2,7 +2,7 @@ export interface Job { id: string; modelName: string; modelVersion: string; - startedAt: string; - finishedAt: string; + startedAt: string | undefined; + finishedAt: string | undefined; status: string; } From 19c55afbad899118116d766311cc0a8806206a4e Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Thu, 9 Mar 2023 11:25:01 +0100 Subject: [PATCH 081/500] fix: package and yarn lock file cleanup --- package.json | 3 +-- yarn.lock | 11 ----------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/package.json b/package.json index 6383ee878..59caa611a 100644 --- a/package.json +++ b/package.json @@ -134,8 +134,7 @@ "tslib": "^2.3.0", "uuid": "^9.0.0", "webxr-polyfill": "^2.0.3", - "worker-rpc": "^0.2.0", - "yarn": "^1.22.19" + "worker-rpc": "^0.2.0" }, "resolutions": { "@types/hoist-non-react-statics/@types/react": "17.0.30", diff --git a/yarn.lock b/yarn.lock index e7e53e151..dbc23dbd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24226,7 +24226,6 @@ __metadata: webxr-polyfill: ^2.0.3 worker-loader: ^3.0.8 worker-rpc: ^0.2.0 - yarn: ^1.22.19 languageName: unknown linkType: soft @@ -25105,16 +25104,6 @@ __metadata: languageName: node linkType: hard -"yarn@npm:^1.22.19": - version: 1.22.19 - resolution: "yarn@npm:1.22.19" - bin: - yarn: bin/yarn.js - yarnpkg: bin/yarn.js - checksum: b43d2cc5fee7e933beb12a8aee7dfceca9e9ef2dd17c5d04d15a12ab7bec5f5744ea34a07b86e013da7f291a18c4e1ad8f70e150f5ed2f4666e6723c7f0a8452 - languageName: node - linkType: hard - "yauzl@npm:^2.10.0": version: 2.10.0 resolution: "yauzl@npm:2.10.0" From a13ee14600e2994cf14267028af43ac29172cea0 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 9 Mar 2023 11:34:17 +0100 Subject: [PATCH 082/500] test: remove test for table component --- .../src/lib/components/table/table.spec.tsx | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 libs/ui-shared/src/lib/components/table/table.spec.tsx diff --git a/libs/ui-shared/src/lib/components/table/table.spec.tsx b/libs/ui-shared/src/lib/components/table/table.spec.tsx deleted file mode 100644 index d635f2bcf..000000000 --- a/libs/ui-shared/src/lib/components/table/table.spec.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { render } from "@testing-library/react"; -import { - createColumnHelper, - useReactTable, - getCoreRowModel, -} from "@tanstack/react-table"; -import TableLayout from "./table"; - -describe("Table", () => { - it("should render successfully", () => { - type TestType = { - property1: string; - property2: string; - }; - const columnHelper = createColumnHelper(); - - const columns = [ - columnHelper.accessor("property1", { - header: "Property 1", - }), - columnHelper.accessor("property2", { - header: "Property 2", - }), - ]; - - const data: TestType[] = [ - { - property1: "value1", - property2: "value2", - }, - { - property1: "value3", - property2: "value4", - }, - ]; - - const table = useReactTable({ - data, - columns, - getCoreRowModel: getCoreRowModel(), - }); - - const { baseElement } = render(); - expect(baseElement).toBeTruthy(); - }); -}); From 89ee303cd16dd4f7a25dc882af33ca988df929e1 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 9 Mar 2023 11:36:31 +0100 Subject: [PATCH 083/500] test: add test for status badge --- .../lib/components/status-badge/status-badge.spec.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx diff --git a/libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx b/libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx new file mode 100644 index 000000000..a108833ac --- /dev/null +++ b/libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx @@ -0,0 +1,11 @@ +import { render } from "@testing-library/react"; +import React from "react"; + +import { StatusBadge } from "./status-badge"; + +describe("Status Badge", () => { + it("should render successfully", () => { + const { baseElement } = render(); + expect(baseElement).toBeTruthy(); + }); +}); From cfc30ba15055379f446d8243a836ccc984c6769f Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Thu, 9 Mar 2023 12:05:04 +0100 Subject: [PATCH 084/500] fix: multi language --- apps/editor/src/assets/de.json | 11 +++++-- apps/editor/src/assets/en.json | 9 ++++-- .../editor/save-popup/save-popup.tsx | 10 +++---- .../editor/ui-overlay/ui-overlay.tsx | 30 +++++++------------ apps/editor/src/models/editor/document.ts | 1 - 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index 400968f9d..cfd3da1e8 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -139,7 +139,6 @@ "server-import-error": "Beim Laden der Datei vom Server ist ein Fehler aufgetreten: \"{{statusText}}\"", "export-error": "Exportfehler", - "saving-error": "Speicherfehler", "file-upload-error": "Die Datei konnte nicht hochgeladen werden.", "tracking-data-mismatch-error": "Der Tracking-Log passt nicht zum geladenen Bild.", @@ -282,12 +281,18 @@ "annotations-loading": "Annotationen werden geladen...", "annotations-loading-error": "Fehler beim Laden der Annotationen:", "annotation-open-error": "Fehler beim Öffnen der Annotation", - "annotation-saving": "Annotationsebene speichern", + "annotation-saving": "Aktive Annotationsebene speichern", + "annotation-saving-overrwite": "Existierende Annotationsdatei überschreiben", + "annotation-saving-as": "Neue Annotationsdatei erstellen", "annotation-saving-error": "Fehler beim Speichern der Annotationsebene", "annotation-saving-as-error": "Fehler beim Speichern Unter der Annotationsebene", "ml-model-selection-description": "Wähle das Modell mit dem die Bilder annotiert werden sollen", "ml-model-selection-title": "Modell Auswahl", "ml-models-loading": "Modelle werden geladen...", - "ml-models-loading-error": "Fehler beim Laden der Modelle:" + "ml-models-loading-error": "Fehler beim Laden der Modelle:", + + "save": "Speichern", + "save-as": "Speichern Unter", + "saving-error": "Fehler beim Speichern" } diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index fb99503e4..64973667f 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -139,7 +139,6 @@ "server-import-error": "An error occurred while getting the file from the server: \"{{statusText}}\"", "export-error": "Export Error", - "saving-error": "Saving Error", "file-upload-error": "The file could not be uploaded.", "tracking-data-mismatch-error": "The tracking log does not belong to the loaded image.", @@ -283,11 +282,17 @@ "annotations-loading-error": "Error on loading Annotations:", "annotation-open-error": "Error on opening annotation", "annotation-saving": "Save active annotation layer", + "annotation-saving-overrwite": "Overwrite existing annotation file", + "annotation-saving-as": "Create new annotation file", "annotation-saving-error": "Error saving annotation layer", "annotation-saving-as-error": "Error saving new annotaion layer", "ml-model-selection-description": "Select the model you want to use to annotate the images", "ml-model-selection-title": "Model Selection", "ml-models-loading": "models loading...", - "ml-models-loading-error": "Error on loading ml models:" + "ml-models-loading-error": "Error on loading ml models:", + + "save": "Save", + "save-as": "Save as", + "saving-error": "Saving Error" } diff --git a/apps/editor/src/components/editor/save-popup/save-popup.tsx b/apps/editor/src/components/editor/save-popup/save-popup.tsx index a6716486b..168a7aac5 100644 --- a/apps/editor/src/components/editor/save-popup/save-popup.tsx +++ b/apps/editor/src/components/editor/save-popup/save-popup.tsx @@ -129,14 +129,14 @@ export const SavePopUp = observer(({ isOpen, onClose }) => { return ( {store?.editor.activeDocument?.activeLayer?.metaData?.dataUri && ( <> - + (({ isOpen, onClose }) => { readOnly /> { saveAnnotation(undefined); store?.destroyRedirect("/", true); @@ -154,7 +154,7 @@ export const SavePopUp = observer(({ isOpen, onClose }) => { )} - + (({ isOpen, onClose }) => { onChangeText={setnewAnnotationURI} /> { saveAnnotationAs(newAnnotationURI); }} diff --git a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx index 343dd4331..33f4876de 100644 --- a/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx +++ b/apps/editor/src/components/editor/ui-overlay/ui-overlay.tsx @@ -16,7 +16,6 @@ import { useStore } from "../../../app/root-store"; import { whoHome } from "../../../constants"; import { importFilesToDocument } from "../../../import-handling"; import { fetchAnnotationFile, fetchImageFile } from "../../../querys"; -import { hubBaseUrl } from "../../../querys/hub-base-url"; import { DilateErodeModal, MeasurementModal, @@ -207,16 +206,6 @@ export const UIOverlay = observer( }); }, [store]); - // Displaying images and annotations from Backend - const loadSessionStorage = (key: string) => { - let storedObject = null; - const storedObjectJSON = sessionStorage.getItem(key); - if (storedObjectJSON) { - storedObject = JSON.parse(storedObjectJSON); - } - return storedObject; - }; - const [searchParams] = useSearchParams(); const loadImagesAndAnnotations = () => { async function asyncfunc() { @@ -319,15 +308,16 @@ export const UIOverlay = observer( - {store?.editor.activeDocument?.activeLayer?.isAnnotation && ( - - )} + {store?.editor.activeDocument?.activeLayer?.isAnnotation && + searchParams.get("imageId") && ( + + )} {!isFromWHO() && ( Date: Thu, 9 Mar 2023 11:10:49 +0000 Subject: [PATCH 085/500] refactor: Error and loading messages --- apps/editor/src/assets/de.json | 6 ++++-- apps/editor/src/assets/en.json | 20 ++++++++++--------- .../menu/datasets-grid/datasets-grid.tsx | 11 ++++------ .../src/screens/project-datasets-screen.tsx | 6 ++++-- apps/editor/src/screens/projects-screen.tsx | 16 ++++++++++----- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/apps/editor/src/assets/de.json b/apps/editor/src/assets/de.json index 6e1d06f1e..5dc70ed0b 100644 --- a/apps/editor/src/assets/de.json +++ b/apps/editor/src/assets/de.json @@ -275,9 +275,11 @@ "projects-loading": "Projekte werden geladen...", "projects-loading-error": "Fehler beim Laden der Projekte:", + "no-projects-available": "Aktuell sind keine Projekte vorhanden", - "project-loading": "Projekt wird geladen...", - "project-loading-error": "Fehler beim Laden des Projekts:", + "datasets-loading": "Datensätze werden geladen...", + "datasets-loading-error": "Fehler beim Laden der Datensätze:", + "no-datasets-available": "Aktuell sind keine Datensätze vorhanden", "dataset-loading": "Dataset wird geladen...", "dataset-loading-error": "Fehler beim Laden des Datensatzes:", diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index 56e7c7164..03d4eb2d3 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -274,19 +274,21 @@ "dataset-base-title": "VISIAN Dataset", "projects-loading": "Loading projects...", - "projects-loading-error": "Error on loading Projects:", + "projects-loading-error": "Error on loading projects:", + "no-projects-available": "Currently no projects are available", - "project-loading": "Loading Project", - "project-loading-error": "Error on loading Datasets:", + "datasets-loading": "Loading Datasets...", + "datasets-loading-error": "Error on loading datasets:", + "no-datasets-available": "Currently no datasets are available", - "dataset-loading": "Loading Dataset...", - "dataset-loading-error": "Error on loading Dataset:", + "dataset-loading": "Loading dataset...", + "dataset-loading-error": "Error on loading dataset:", - "images-loading": "Loading Images...", - "images-loading-error": "Error on loading Images:", + "images-loading": "Loading images...", + "images-loading-error": "Error on loading images:", - "annotations-loading": "Loading Annotations...", - "annotations-loading-error": "Error on loading Annotations:", + "annotations-loading": "Loading annotations...", + "annotations-loading-error": "Error on loading annotations:", "ml-model-selection-description": "Select the model you want to use to annotate the images", "ml-model-selection-title": "Model Selection", diff --git a/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx index 238cb6d22..471f5cf4c 100644 --- a/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx +++ b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx @@ -1,5 +1,4 @@ import { Modal, Text, useTranslation } from "@visian/ui-shared"; -import { useParams } from "react-router-dom"; import styled from "styled-components"; import useDatasetsBy from "../../../queries/use-datasets-by"; @@ -11,20 +10,18 @@ const StyledModal = styled(Modal)` position: relative; `; -export const DatasetsGrid = () => { - const projectId = useParams().projectId || ""; - +export const DatasetsGrid = ({ projectId }: { projectId: string }) => { const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = useDatasetsBy(projectId); const { t: translate } = useTranslation(); return isLoadingDatasets || isErrorDatasets ? ( - + {isLoadingDatasets ? ( - {translate("project-loading")} + {translate("dataset-loading")} ) : ( - {`${translate("project-loading-error")} ${ + {`${translate("dataset-loading-error")} ${ datasetsError?.response?.statusText } (${datasetsError?.response?.status})`} diff --git a/apps/editor/src/screens/project-datasets-screen.tsx b/apps/editor/src/screens/project-datasets-screen.tsx index e68082a75..b09178a24 100644 --- a/apps/editor/src/screens/project-datasets-screen.tsx +++ b/apps/editor/src/screens/project-datasets-screen.tsx @@ -6,7 +6,7 @@ import { } from "@visian/ui-shared"; import { observer } from "mobx-react-lite"; import React from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import styled from "styled-components"; import { DatasetsGrid } from "../components/menu/datasets-grid"; @@ -39,6 +39,8 @@ const IconButton = styled(InvisibleButton)` export const ProjectDatasetsScreen: React.FC = observer(() => { const navigate = useNavigate(); + const projectId = useParams().projectId || ""; + const { t: translate } = useTranslation(); return ( @@ -48,7 +50,7 @@ export const ProjectDatasetsScreen: React.FC = observer(() => { - + {projectId && }
); diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/projects-screen.tsx index 5e8b914fc..9ae6d58f0 100644 --- a/apps/editor/src/screens/projects-screen.tsx +++ b/apps/editor/src/screens/projects-screen.tsx @@ -27,17 +27,23 @@ export const ProjectsScreen: React.FC = observer(() => { return (
- {isLoadingProjects && } - {isErrorProjects && ( + {isLoadingProjects ? ( + + ) : isErrorProjects ? ( {`${translate("projects-loading-error")} ${ projectsError?.response?.statusText } (${projectsError?.response?.status})`} + ) : ( + + {projects && projects.length > 0 ? ( + + ) : ( + {translate("no-projects-available")} + )} + )} - - {projects && } -
); From 321aaa2904548868ab4910d2935903a066ff0287 Mon Sep 17 00:00:00 2001 From: Daniel dJ Date: Thu, 9 Mar 2023 12:30:05 +0100 Subject: [PATCH 086/500] fix: await saving before destroying layers --- .../editor/src/components/editor/save-popup/save-popup.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/editor/src/components/editor/save-popup/save-popup.tsx b/apps/editor/src/components/editor/save-popup/save-popup.tsx index 168a7aac5..9b2d869b3 100644 --- a/apps/editor/src/components/editor/save-popup/save-popup.tsx +++ b/apps/editor/src/components/editor/save-popup/save-popup.tsx @@ -146,9 +146,10 @@ export const SavePopUp = observer(({ isOpen, onClose }) => { /> { - saveAnnotation(undefined); - store?.destroyRedirect("/", true); + onPointerDown={async () => { + if (await saveAnnotation(undefined)) { + store?.destroyRedirect("/", true); + } }} /> From ad5f4c52d07da43095bcce4076830f37beb5b1b5 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 9 Mar 2023 12:40:15 +0100 Subject: [PATCH 087/500] refactor: make job history scrollable --- apps/editor/src/screens/project-jobs-screen.tsx | 3 +-- libs/ui-shared/src/lib/components/table/table.tsx | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/editor/src/screens/project-jobs-screen.tsx b/apps/editor/src/screens/project-jobs-screen.tsx index 36e70cb66..ff60b824b 100644 --- a/apps/editor/src/screens/project-jobs-screen.tsx +++ b/apps/editor/src/screens/project-jobs-screen.tsx @@ -17,9 +17,8 @@ import { ProjectViewSwitch } from "../components/menu/project-view-switch"; const Main = styled(Box)` display: flex; justify-content: center; - height: 100%; + height: 90%; padding: 1rem 10rem; - padding-bottom: 5rem; `; const StyledProjectViewSwitch = styled(Box)` diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx index b41ca74ee..9c2c26afc 100644 --- a/libs/ui-shared/src/lib/components/table/table.tsx +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -1,5 +1,6 @@ import { Cell, flexRender, Header, Table } from "@tanstack/react-table"; import styled from "styled-components"; +import { stopPropagation } from "../../event-handling"; import { List, ListItem } from "../list"; export const TableCell = styled.div.attrs((props: { width?: number }) => props)` @@ -10,6 +11,10 @@ export const TableCell = styled.div.attrs((props: { width?: number }) => props)` margin: auto; `; +const TableList = styled(List)` + overflow-y: auto; +`; + const distributeColumns = ( columnWidths: number[] | undefined, columnCount: number, @@ -76,7 +81,7 @@ export const TableLayout = ({ ); return ( - + ))} - + ); }; From ab877264e52308cd77fac4c0c9802fa1141d5b03 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 9 Mar 2023 15:32:46 +0100 Subject: [PATCH 088/500] refactor: adjust styling of table header --- .../components/menu/job-history/job-table.tsx | 13 +++++----- .../src/lib/components/table/table.tsx | 26 ++++++++++++++----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/apps/editor/src/components/menu/job-history/job-table.tsx b/apps/editor/src/components/menu/job-history/job-table.tsx index e6c22929c..c890c9f75 100644 --- a/apps/editor/src/components/menu/job-history/job-table.tsx +++ b/apps/editor/src/components/menu/job-history/job-table.tsx @@ -1,7 +1,5 @@ import { Job } from "../../../types"; import { getDisplayDate } from "../util/display-date"; -import styled from "styled-components"; -import { fontWeight } from "@visian/ui-shared"; import { createColumnHelper, getCoreRowModel, @@ -9,13 +7,14 @@ import { SortingState, useReactTable, } from "@tanstack/react-table"; -import { TableLayout, ListItemLabel, StatusBadge } from "@visian/ui-shared"; +import { + TableLayout, + ListItemLabel, + StatusBadge, + HeaderLabel, +} from "@visian/ui-shared"; import React from "react"; -export const HeaderLabel = styled(ListItemLabel)` - font-weight: ${fontWeight("bold")}; -`; - function getDisplayJob(job: Job): Job { return { ...job, diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx index 9c2c26afc..83cd29502 100644 --- a/libs/ui-shared/src/lib/components/table/table.tsx +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -1,7 +1,23 @@ import { Cell, flexRender, Header, Table } from "@tanstack/react-table"; import styled from "styled-components"; import { stopPropagation } from "../../event-handling"; -import { List, ListItem } from "../list"; +import { color, radius, fontWeight } from "../../theme"; +import { List, ListItem, ListItemLabel } from "../list"; + +const TableList = styled(List)` + overflow-y: auto; +`; + +const HeaderListItem = styled(ListItem)` + border-radius: ${radius("default")}; + background-color: ${color("veryLightGray")}; + border: 1px solid; + border-color: ${color("sheetBorder")}; +`; + +export const HeaderLabel = styled(ListItemLabel)` + font-weight: ${fontWeight("bold")}; +`; export const TableCell = styled.div.attrs((props: { width?: number }) => props)` width: ${(props) => (props.width ? props.width : 20)}%; @@ -11,10 +27,6 @@ export const TableCell = styled.div.attrs((props: { width?: number }) => props)` margin: auto; `; -const TableList = styled(List)` - overflow-y: auto; -`; - const distributeColumns = ( columnWidths: number[] | undefined, columnCount: number, @@ -54,7 +66,7 @@ export const TableHeader = ({ columnWidths: number[]; }) => { return ( - + {headers.map((header, index) => ( {header.isPlaceholder @@ -62,7 +74,7 @@ export const TableHeader = ({ : flexRender(header.column.columnDef.header, header.getContext())} ))} - + ); }; From cd6bbd25f1f12711213013c3c69cb653f19b4000 Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Thu, 9 Mar 2023 14:56:37 +0000 Subject: [PATCH 089/500] refactor: fix window width --- .../src/components/menu/dataset-list/dataset-list.tsx | 6 +++--- .../src/components/menu/datasets-grid/datasets-grid.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx index 0569859a3..52961fffb 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx @@ -7,7 +7,7 @@ import { DatasetListItem } from "./dataset-list-item"; const StyledDatasetList = styled(List)` display: grid; grid-template-columns: 1fr 1fr 1fr; - column-gap: 30px; + column-gap: 3vw; row-gap: 60px; justify-items: center; margin-top: 2%; @@ -20,11 +20,11 @@ const StyledDatasetList = styled(List)` } &::-webkit-scrollbar-thumb { background-color: #ccc; - border-radius: 10px; + border-radius: 15px; } &::-webkit-scrollbar-track { background-color: #fff; - border-radius: 10px; + border-radius: 15px; } `; diff --git a/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx index 471f5cf4c..5cc4aa734 100644 --- a/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx +++ b/apps/editor/src/components/menu/datasets-grid/datasets-grid.tsx @@ -6,7 +6,7 @@ import { DatasetList } from "../dataset-list"; const StyledModal = styled(Modal)` vertical-align: middle; - width: 100%; + width: 100vw; position: relative; `; From 2c99e152883ddaaad73bb20f1e9a3429f1e4e6e5 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Thu, 9 Mar 2023 18:46:30 +0100 Subject: [PATCH 090/500] feat: add basic file explorer to job creation popup --- .../dataset-explorer/dataset-explorer.tsx | 1 + .../menu/job-history/job-history.tsx | 44 ++++++++++- .../ml-model-selection-popup.props.ts | 3 +- .../ml-model-selection-popup.tsx | 76 ++++++++++++++++--- .../src/screens/project-jobs-screen.tsx | 4 +- 5 files changed, 113 insertions(+), 15 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-explorer/dataset-explorer.tsx b/apps/editor/src/components/menu/dataset-explorer/dataset-explorer.tsx index d932fb257..a12fadec6 100644 --- a/apps/editor/src/components/menu/dataset-explorer/dataset-explorer.tsx +++ b/apps/editor/src/components/menu/dataset-explorer/dataset-explorer.tsx @@ -128,6 +128,7 @@ export const DatasetExplorer = ({ dataset }: { dataset: Dataset }) => { isOpen={isModelSelectionPopUpOpen} onClose={closeModelSelectionPopUp} activeImageSelection={activeImageSelection} + projectId={dataset.project} />
); diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index 66b41adeb..a3ebfad79 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -1,22 +1,55 @@ -import { Box, Modal, Text, useTranslation } from "@visian/ui-shared"; +import { + Box, + Modal, + SquareButton, + Text, + useTranslation, +} from "@visian/ui-shared"; +import { useCallback, useState } from "react"; import styled from "styled-components"; import { useJobs } from "../../../queries"; +import { ModelSelectionPopup } from "../ml-model-popup"; import { JobsTable } from "./job-table"; const StyledModal = styled(Modal)` vertical-align: middle; width: 100%; position: relative; + z-index: 49; `; -export const JobHistory = () => { +const StyledButton = styled(SquareButton)` + margin-left: 10px; +`; + +export const JobHistory = ({ projectId }: { projectId: string }) => { const { jobs, jobsError, isErrorJobs, isLoadingJobs } = useJobs(); const { t: translate } = useTranslation(); + // model selection popup + const [isModelSelectionPopUpOpen, setIsModelSelectionPopUpOpen] = + useState(false); + const openModelSelectionPopUp = useCallback(() => { + setIsModelSelectionPopUpOpen(true); + }, []); + const closeModelSelectionPopUp = useCallback(() => { + setIsModelSelectionPopUpOpen(false); + }, []); + return ( - + + } + > {isLoadingJobs && } {isErrorJobs && ( {`${translate("jobs-loading-error")} ${ @@ -24,6 +57,11 @@ export const JobHistory = () => { } (${jobsError?.response?.status})`} )} {jobs && } + ); }; diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts index 9a2d932c8..fa059d408 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.props.ts @@ -1,5 +1,6 @@ import type { StatefulPopUpProps } from "@visian/ui-shared"; export interface ModelPopUpProps extends StatefulPopUpProps { - activeImageSelection: string[]; + projectId: string; + activeImageSelection?: string[]; } diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx index 10d09440d..7c5d66464 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx @@ -1,10 +1,22 @@ -import { PopUp, Text, useTranslation } from "@visian/ui-shared"; +import { + color, + FlexColumn, + FlexRow, + Icon, + List, + ListItem, + PopUp, + Spacer, + Text, + useTranslation, +} from "@visian/ui-shared"; import axios from "axios"; import { observer } from "mobx-react-lite"; import styled from "styled-components"; -import { useMlModels } from "../../../queries"; +import { useDataset, useMlModels } from "../../../queries"; import { hubBaseUrl } from "../../../queries/hub-base-url"; +import useDatasetsBy from "../../../queries/use-datasets-by"; import { MlModel } from "../../../types"; import { MlModelList } from "../ml-model-list"; import { ModelPopUpProps } from "./ml-model-selection-popup.props"; @@ -16,11 +28,38 @@ const SectionLabel = styled(Text)` const ModelSelectionPopupContainer = styled(PopUp)` align-items: left; - width: 400px; + width: 50vw; + height: 70vh; +`; + +const FileExplorer = styled(FlexRow)` + width: 100%; + height: 50%; +`; + +const StyledList = styled(List)` + overflow-y: auto; +`; + +const StyledIcon = styled(Icon)` + width: 2rem; + height: 2rem; + padding-right: 0.8rem; +`; + +const VerticalLine = styled.div` + border-left: 1px solid ${color("sheetBorder")}; + margin: 0 1vw; `; export const ModelSelectionPopup = observer( - ({ isOpen, onClose, activeImageSelection }) => { + ({ isOpen, onClose, activeImageSelection, projectId }) => { + const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = + useDatasetsBy(projectId); + + // const { dataset, datasetError, isErrorDataset, isLoadingDataset } = + // useDataset(datasetId); + const { mlModels, mlModelsError, isErrorMlModels, isLoadingMlModels } = useMlModels(); @@ -55,12 +94,29 @@ export const ModelSelectionPopup = observer( mlModelsError?.response?.statusText } (${mlModelsError?.response?.status})`} )} - {mlModels && ( - - )} + + {datasets && ( + + {datasets.map((dataset) => ( + + + {dataset.name} + + ))} + + )} + + {datasets && ( + + {datasets.map((dataset) => ( + + + {dataset.name} + + ))} + + )} + ); }, diff --git a/apps/editor/src/screens/project-jobs-screen.tsx b/apps/editor/src/screens/project-jobs-screen.tsx index ff60b824b..9c39db5f3 100644 --- a/apps/editor/src/screens/project-jobs-screen.tsx +++ b/apps/editor/src/screens/project-jobs-screen.tsx @@ -38,6 +38,8 @@ const IconButton = styled(InvisibleButton)` //TODO z-index logic export const ProjectJobsScreen: React.FC = observer(() => { + const projectId = useParams().projectId || ""; + const navigate = useNavigate(); const { t: translate } = useTranslation(); @@ -49,7 +51,7 @@ export const ProjectJobsScreen: React.FC = observer(() => { - +
); From b3179dbf23aa97879140affa69d064ebf9c53169 Mon Sep 17 00:00:00 2001 From: Johanna Schlimme <78924632+johannaschlimme@users.noreply.github.com> Date: Thu, 9 Mar 2023 17:48:29 +0000 Subject: [PATCH 091/500] add back and home button to project pages --- apps/editor/src/screens/dataset-screen.tsx | 38 +++++++++++++---- .../src/screens/project-datasets-screen.tsx | 42 +++++++++++-------- .../src/screens/project-jobs-screen.tsx | 39 ++++++++++------- apps/editor/src/screens/projects-screen.tsx | 12 ++++-- 4 files changed, 86 insertions(+), 45 deletions(-) diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 61abbbcd0..511f343be 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -1,6 +1,6 @@ import { Box, - InvisibleButton, + FloatingUIButton, Modal, Screen, Text, @@ -14,12 +14,15 @@ import styled from "styled-components"; import { DatasetExplorer } from "../components/menu/dataset-explorer"; import { useDataset } from "../queries"; +const Container = styled(Screen)` + padding: 20px; +`; + const Main = styled(Box)` display: flex; justify-content: center; height: 100%; - padding: 1rem 10rem; - padding-bottom: 5rem; + padding: 1rem 10rem 5rem; `; const StyledModal = styled(Modal)` @@ -27,13 +30,19 @@ const StyledModal = styled(Modal)` width: 100%; `; -const IconButton = styled(InvisibleButton)` - width: 40px; - margin: 5px; +const IconButton = styled(FloatingUIButton)` + margin-right: 16px; +`; + +const MenuRow = styled.div` + display: flex; + flex-direction: row; + justify-content: flex-start; `; export const DatasetScreen: React.FC = observer(() => { const datasetId = useParams().datasetId || ""; + const projectId = useParams().projectId || ""; const { dataset, datasetError, isErrorDataset, isLoadingDataset } = useDataset(datasetId); @@ -42,7 +51,7 @@ export const DatasetScreen: React.FC = observer(() => { const { t: translate } = useTranslation(); return ( - { : "" }`} > - navigate(`/projects`)} /> + + navigate(`/projects`)} + /> + navigate(`/projects/${projectId}/datasets`)} + /> +
{isLoadingDataset && } {isErrorDataset && ( @@ -65,7 +85,7 @@ export const DatasetScreen: React.FC = observer(() => { )} {dataset && }
-
+ ); }); diff --git a/apps/editor/src/screens/project-datasets-screen.tsx b/apps/editor/src/screens/project-datasets-screen.tsx index fb1868bc5..b889b3992 100644 --- a/apps/editor/src/screens/project-datasets-screen.tsx +++ b/apps/editor/src/screens/project-datasets-screen.tsx @@ -1,6 +1,6 @@ import { Box, - InvisibleButton, + FloatingUIButton, Screen, useTranslation, } from "@visian/ui-shared"; @@ -12,29 +12,33 @@ import styled from "styled-components"; import { DatasetsGrid } from "../components/menu/datasets-grid"; import { ProjectViewSwitch } from "../components/menu/project-view-switch"; +const Container = styled(Screen)` + padding: 20px; +`; + const Main = styled(Box)` display: flex; justify-content: center; - height: 90%; - padding: 1rem 10rem; + height: 100%; + padding: 1rem 10rem 5rem; +`; + +const MenuRow = styled.div` + display: flex; + flex-direction: row; + justify-content: flex-start; `; const StyledProjectViewSwitch = styled(Box)` display: flex; justify-content: center; width: 100%; - position: absolute; - top: 20px; `; -const IconButton = styled(InvisibleButton)` - width: 40px; - margin: 5px; - z-index: 51; +const IconButton = styled(FloatingUIButton)` + margin-right: 16px; `; -// TODO z-index logic - export const ProjectDatasetsScreen: React.FC = observer(() => { const navigate = useNavigate(); @@ -43,15 +47,19 @@ export const ProjectDatasetsScreen: React.FC = observer(() => { const { t: translate } = useTranslation(); return ( - - navigate(`/projects`)} /> -
+ + + navigate(`/projects`)} + /> - {projectId && } -
-
+ +
{projectId && }
+ ); }); diff --git a/apps/editor/src/screens/project-jobs-screen.tsx b/apps/editor/src/screens/project-jobs-screen.tsx index 3185647b0..123cbafb3 100644 --- a/apps/editor/src/screens/project-jobs-screen.tsx +++ b/apps/editor/src/screens/project-jobs-screen.tsx @@ -1,6 +1,6 @@ import { Box, - InvisibleButton, + FloatingUIButton, Screen, useTranslation, } from "@visian/ui-shared"; @@ -12,45 +12,54 @@ import styled from "styled-components"; import { JobHistory } from "../components/menu/job-history"; import { ProjectViewSwitch } from "../components/menu/project-view-switch"; +const Container = styled(Screen)` + padding: 20px; +`; + const Main = styled(Box)` display: flex; justify-content: center; height: 100%; - padding: 1rem 10rem; - padding-bottom: 5rem; + padding: 1rem 10rem 5rem; +`; + +const MenuRow = styled.div` + display: flex; + flex-direction: row; + justify-content: flex-start; `; const StyledProjectViewSwitch = styled(Box)` display: flex; justify-content: center; width: 100%; - position: absolute; - top: 20px; `; -const IconButton = styled(InvisibleButton)` - width: 40px; - margin: 5px; - z-index: 51; +const IconButton = styled(FloatingUIButton)` + margin-right: 16px; `; -// TODO z-index logic - export const ProjectJobsScreen: React.FC = observer(() => { const navigate = useNavigate(); const { t: translate } = useTranslation(); return ( - - navigate(`/projects`)} /> -
+ + + navigate(`/projects`)} + /> + +
- +
); }); diff --git a/apps/editor/src/screens/projects-screen.tsx b/apps/editor/src/screens/projects-screen.tsx index cab71ca27..a3dc1e001 100644 --- a/apps/editor/src/screens/projects-screen.tsx +++ b/apps/editor/src/screens/projects-screen.tsx @@ -6,12 +6,16 @@ import styled from "styled-components"; import { ProjectList } from "../components/menu/projects-list/project-list"; import { useProjects } from "../queries"; +const Container = styled(Screen)` + padding: 20px; +`; + const Main = styled(Box)` display: flex; justify-content: center; - height: 90%; + height: 100%; padding: 5rem 10rem; - padding-top: 4rem; + padding-top: 4.5rem; `; const StyledModal = styled(Modal)` @@ -25,7 +29,7 @@ export const ProjectsScreen: React.FC = observer(() => { const { t: translate } = useTranslation(); return ( - +
{isLoadingProjects ? ( @@ -45,7 +49,7 @@ export const ProjectsScreen: React.FC = observer(() => { )}
-
+ ); }); From b8eb4e0b777890ab3ae627388117d7afeec76f85 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Fri, 10 Mar 2023 10:01:39 +0100 Subject: [PATCH 092/500] fix: lint jobs-history --- .../menu/job-history/job-history.tsx | 1 + .../components/menu/job-history/job-table.tsx | 21 ++++---- .../src/components/menu/util/display-date.tsx | 2 +- .../status-badge/status-badge.spec.tsx | 2 +- .../components/status-badge/status-badge.tsx | 18 +++---- .../src/lib/components/table/table.tsx | 49 +++++++++---------- 6 files changed, 46 insertions(+), 47 deletions(-) diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index 6d2ec31c6..9b446c49f 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -1,5 +1,6 @@ import { Modal, Text, useTranslation } from "@visian/ui-shared"; import styled from "styled-components"; + import { useJobs } from "../../../queries"; import { JobsTable } from "./job-table"; diff --git a/apps/editor/src/components/menu/job-history/job-table.tsx b/apps/editor/src/components/menu/job-history/job-table.tsx index c890c9f75..90e0b1e63 100644 --- a/apps/editor/src/components/menu/job-history/job-table.tsx +++ b/apps/editor/src/components/menu/job-history/job-table.tsx @@ -1,5 +1,3 @@ -import { Job } from "../../../types"; -import { getDisplayDate } from "../util/display-date"; import { createColumnHelper, getCoreRowModel, @@ -8,13 +6,16 @@ import { useReactTable, } from "@tanstack/react-table"; import { - TableLayout, + HeaderLabel, ListItemLabel, StatusBadge, - HeaderLabel, + TableLayout, } from "@visian/ui-shared"; import React from "react"; +import { Job } from "../../../types"; +import { getDisplayDate } from "../util/display-date"; + function getDisplayJob(job: Job): Job { return { ...job, @@ -40,27 +41,27 @@ const columnHelper = createColumnHelper(); const columns = [ columnHelper.accessor("modelName", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("modelVersion", { - header: () => , + header: () => , cell: (props) => , }), columnHelper.accessor("startedAt", { - header: () => , + header: () => , cell: (props) => , sortingFn: "datetime", sortUndefined: -1, }), columnHelper.accessor("finishedAt", { - header: () => , + header: () => , cell: (props) => , sortingFn: "datetime", sortUndefined: -1, }), columnHelper.accessor("status", { - header: () => , + header: () => , cell: (props) => ( { - if (rowA.getValue(id) == "queued") return -1; + if (rowA.getValue(id) === "queued") return -1; return 0; }, }), diff --git a/apps/editor/src/components/menu/util/display-date.tsx b/apps/editor/src/components/menu/util/display-date.tsx index da1ea6598..9a1433aca 100644 --- a/apps/editor/src/components/menu/util/display-date.tsx +++ b/apps/editor/src/components/menu/util/display-date.tsx @@ -4,7 +4,7 @@ * @returns The date as a string in german format if the timezone is Europe/Berlin, otherwise in english format. */ export function getDisplayDate(date: Date): string { - if (Intl.DateTimeFormat().resolvedOptions().timeZone == "Europe/Berlin") { + if (Intl.DateTimeFormat().resolvedOptions().timeZone === "Europe/Berlin") { return date.toLocaleDateString("de-DE", { year: "numeric", month: "numeric", diff --git a/libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx b/libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx index a108833ac..5bafa0c7e 100644 --- a/libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx +++ b/libs/ui-shared/src/lib/components/status-badge/status-badge.spec.tsx @@ -5,7 +5,7 @@ import { StatusBadge } from "./status-badge"; describe("Status Badge", () => { it("should render successfully", () => { - const { baseElement } = render(); + const { baseElement } = render(); expect(baseElement).toBeTruthy(); }); }); diff --git a/libs/ui-shared/src/lib/components/status-badge/status-badge.tsx b/libs/ui-shared/src/lib/components/status-badge/status-badge.tsx index b17dc34ed..e1ef2cb81 100644 --- a/libs/ui-shared/src/lib/components/status-badge/status-badge.tsx +++ b/libs/ui-shared/src/lib/components/status-badge/status-badge.tsx @@ -1,5 +1,6 @@ import styled from "styled-components"; -import { color, radius, Theme } from "../../theme"; + +import { color as getColor, radius, Theme } from "../../theme"; import { ListItemLabel } from "../list"; import { StatusBadgeProps } from "./status-badge.props"; @@ -9,19 +10,18 @@ export const StatusBadgeContainer = styled.div>` width: 10em; height: fit-content; border-radius: ${radius("default")}; - background-color: ${(props) => color(props.color as keyof Theme["colors"])}; + background-color: ${(props) => + getColor(props.color as keyof Theme["colors"])}; `; export const StatusBadge: React.FC = ({ color, text, tx, -}) => { - return ( - - - - ); -}; +}) => ( + + + +); export default StatusBadge; diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx index 83cd29502..a6ae0729c 100644 --- a/libs/ui-shared/src/lib/components/table/table.tsx +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -1,7 +1,8 @@ import { Cell, flexRender, Header, Table } from "@tanstack/react-table"; import styled from "styled-components"; + import { stopPropagation } from "../../event-handling"; -import { color, radius, fontWeight } from "../../theme"; +import { color, fontWeight, radius } from "../../theme"; import { List, ListItem, ListItemLabel } from "../list"; const TableList = styled(List)` @@ -33,7 +34,7 @@ const distributeColumns = ( ) => { if (columnWidths && columnWidths.length === columnCount) { const sum = columnWidths.reduce((partSum, width) => partSum + width, 0); - if (sum == 100) { + if (sum === 100) { return columnWidths; } } @@ -46,17 +47,15 @@ export const TableRow = ({ }: { cells: Cell[]; columnWidths: number[]; -}) => { - return ( - - {cells.map((cell, index) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ); -}; +}) => ( + + {cells.map((cell, index) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + +); export const TableHeader = ({ headers, @@ -64,19 +63,17 @@ export const TableHeader = ({ }: { headers: Header[]; columnWidths: number[]; -}) => { - return ( - - {headers.map((header, index) => ( - - {header.isPlaceholder - ? null - : flexRender(header.column.columnDef.header, header.getContext())} - - ))} - - ); -}; +}) => ( + + {headers.map((header, index) => ( + + {header.isPlaceholder + ? null + : flexRender(header.column.columnDef.header, header.getContext())} + + ))} + +); export const TableLayout = ({ table, From 60e47cd8b6f8421ca8f26f0c44e6d3f28ba50de5 Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Fri, 10 Mar 2023 14:32:51 +0000 Subject: [PATCH 093/500] refactor: fix issues from review --- .../src/components/menu/dataset-list/dataset-list.tsx | 6 +----- .../menu/project-view-switch/project-view-switch.tsx | 6 +++--- .../src/components/menu/projects-list/project-list.tsx | 1 - 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx index 52961fffb..2eb998482 100644 --- a/apps/editor/src/components/menu/dataset-list/dataset-list.tsx +++ b/apps/editor/src/components/menu/dataset-list/dataset-list.tsx @@ -28,14 +28,10 @@ const StyledDatasetList = styled(List)` } `; -const StyledDatasetListItem = styled(DatasetListItem)` - height: 100px; -`; - export const DatasetList = ({ datasets }: { datasets: Dataset[] }) => ( {datasets.map((dataset: Dataset) => ( - + ))} ); diff --git a/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx b/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx index 20de7468e..7e82daeb5 100644 --- a/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx +++ b/apps/editor/src/components/menu/project-view-switch/project-view-switch.tsx @@ -24,9 +24,9 @@ export const ProjectViewSwitch = ({ const navigate = useNavigate(); // expect path like /project/projectId/datasets - const handleChange = (newValue: string) => { + const navigateToScreen = (screenName: string) => { const pathParts = window.location.pathname.split("/"); - pathParts[pathParts.length - 1] = newValue; + pathParts[pathParts.length - 1] = screenName; navigate(pathParts.join("/")); }; @@ -36,7 +36,7 @@ export const ProjectViewSwitch = ({ infoBaseZIndex={theme.zIndices.overlay} options={projectViewSwitchOptions} value={defaultSwitchSelection} - onChange={(newValue) => handleChange(newValue)} + onChange={(screenName) => navigateToScreen(screenName)} /> ); diff --git a/apps/editor/src/components/menu/projects-list/project-list.tsx b/apps/editor/src/components/menu/projects-list/project-list.tsx index 56e83d31e..7b2e2c505 100644 --- a/apps/editor/src/components/menu/projects-list/project-list.tsx +++ b/apps/editor/src/components/menu/projects-list/project-list.tsx @@ -6,7 +6,6 @@ import { ProjectListItem } from "./project-list-item"; const StyledProjectList = styled(List)` width: 100%; - height: 400px; overflow-y: auto; `; From daf0c0dd222d61bdb090b2f73952802105c1ef09 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Fri, 10 Mar 2023 20:32:07 +0100 Subject: [PATCH 094/500] refactor: minor styling, documentation and language adjustments to jobs history --- apps/editor/src/assets/en.json | 8 ++++---- .../src/components/menu/job-history/job-history.tsx | 2 +- libs/ui-shared/src/lib/components/table/table.tsx | 4 ++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/editor/src/assets/en.json b/apps/editor/src/assets/en.json index aae623e03..88edcacde 100644 --- a/apps/editor/src/assets/en.json +++ b/apps/editor/src/assets/en.json @@ -297,12 +297,12 @@ "ml-models-loading": "models loading...", "ml-models-loading-error": "Error on loading ml models:", - "jobs-loading": "Loading Jobs...", - "jobs-loading-error": "Error on loading Jobs:", + "jobs-loading": "Loading jobs...", + "jobs-loading-error": "Error on loading jobs:", "job-model-name": "Model", "job-model-version": "Version", - "job-started": "Started At", - "job-finished": "Finished At", + "job-started": "Started at", + "job-finished": "Finished at", "job-status": "Status", "job-status-queued": "queued", "job-status-running": "running", diff --git a/apps/editor/src/components/menu/job-history/job-history.tsx b/apps/editor/src/components/menu/job-history/job-history.tsx index 9b446c49f..ff08888c1 100644 --- a/apps/editor/src/components/menu/job-history/job-history.tsx +++ b/apps/editor/src/components/menu/job-history/job-history.tsx @@ -6,7 +6,7 @@ import { JobsTable } from "./job-table"; const StyledModal = styled(Modal)` vertical-align: middle; - width: 100%; + width: 100vw; position: relative; `; diff --git a/libs/ui-shared/src/lib/components/table/table.tsx b/libs/ui-shared/src/lib/components/table/table.tsx index a6ae0729c..658672f38 100644 --- a/libs/ui-shared/src/lib/components/table/table.tsx +++ b/libs/ui-shared/src/lib/components/table/table.tsx @@ -28,6 +28,10 @@ export const TableCell = styled.div.attrs((props: { width?: number }) => props)` margin: auto; `; +/* +Distributes the columns evenly if no column widths are provided or if the sum of the column widths is not 100. +Otherwise it distributes the columns according to the provided column widths. +*/ const distributeColumns = ( columnWidths: number[] | undefined, columnCount: number, From b6e48a1ee8f19ab83e8906ee33994245c16fd641 Mon Sep 17 00:00:00 2001 From: johannaschlimme Date: Mon, 13 Mar 2023 10:43:27 +0100 Subject: [PATCH 095/500] change home and back button icons --- apps/editor/src/screens/dataset-screen.tsx | 4 ++-- apps/editor/src/screens/project-datasets-screen.tsx | 2 +- apps/editor/src/screens/project-jobs-screen.tsx | 2 +- .../src/lib/components/icon/icons/arrow-back.svg | 12 ++++++++++++ .../ui-shared/src/lib/components/icon/icons/home.svg | 1 + .../ui-shared/src/lib/components/icon/icons/index.ts | 2 ++ 6 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 libs/ui-shared/src/lib/components/icon/icons/arrow-back.svg create mode 100644 libs/ui-shared/src/lib/components/icon/icons/home.svg diff --git a/apps/editor/src/screens/dataset-screen.tsx b/apps/editor/src/screens/dataset-screen.tsx index 511f343be..58a1b42cd 100755 --- a/apps/editor/src/screens/dataset-screen.tsx +++ b/apps/editor/src/screens/dataset-screen.tsx @@ -64,12 +64,12 @@ export const DatasetScreen: React.FC = observer(() => { > navigate(`/projects`)} /> navigate(`/projects/${projectId}/datasets`)} /> diff --git a/apps/editor/src/screens/project-datasets-screen.tsx b/apps/editor/src/screens/project-datasets-screen.tsx index b889b3992..2a76f0c86 100644 --- a/apps/editor/src/screens/project-datasets-screen.tsx +++ b/apps/editor/src/screens/project-datasets-screen.tsx @@ -50,7 +50,7 @@ export const ProjectDatasetsScreen: React.FC = observer(() => { navigate(`/projects`)} /> diff --git a/apps/editor/src/screens/project-jobs-screen.tsx b/apps/editor/src/screens/project-jobs-screen.tsx index 123cbafb3..e83a66106 100644 --- a/apps/editor/src/screens/project-jobs-screen.tsx +++ b/apps/editor/src/screens/project-jobs-screen.tsx @@ -48,7 +48,7 @@ export const ProjectJobsScreen: React.FC = observer(() => { navigate(`/projects`)} /> diff --git a/libs/ui-shared/src/lib/components/icon/icons/arrow-back.svg b/libs/ui-shared/src/lib/components/icon/icons/arrow-back.svg new file mode 100644 index 000000000..0499a53c1 --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/arrow-back.svg @@ -0,0 +1,12 @@ + + + + + + + + + + left-arrow Created with Sketch. + + \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/home.svg b/libs/ui-shared/src/lib/components/icon/icons/home.svg new file mode 100644 index 000000000..ef9be5bd1 --- /dev/null +++ b/libs/ui-shared/src/lib/components/icon/icons/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/ui-shared/src/lib/components/icon/icons/index.ts b/libs/ui-shared/src/lib/components/icon/icons/index.ts index 74fa3f846..eaee6de04 100644 --- a/libs/ui-shared/src/lib/components/icon/icons/index.ts +++ b/libs/ui-shared/src/lib/components/icon/icons/index.ts @@ -1,3 +1,4 @@ +export { ReactComponent as arrowBack } from "./arrow-back.svg"; export { ReactComponent as arrowDown } from "./arrow-down.svg"; export { ReactComponent as arrowLeft } from "./arrow-left.svg"; export { ReactComponent as arrowRight } from "./arrow-right.svg"; @@ -23,6 +24,7 @@ export { ReactComponent as eyeCrossed } from "./eye-crossed.svg"; export { ReactComponent as eye } from "./eye.svg"; export { ReactComponent as folder } from "./folder.svg"; export { ReactComponent as fullScreenSmall } from "./full-screen-small.svg"; +export { ReactComponent as home } from "./home.svg"; export { ReactComponent as image } from "./image.svg"; export { ReactComponent as import } from "./import.svg"; export { ReactComponent as info } from "./info.svg"; From 07648641256bb0b20a7838c5d4570de9ef6431c5 Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Mon, 13 Mar 2023 16:52:09 +0100 Subject: [PATCH 096/500] refactor: add medium sized dropdown --- .../drop-down/drop-down-options.tsx | 28 ++++++++++++------- .../components/drop-down/drop-down.props.ts | 4 +++ .../lib/components/drop-down/drop-down.tsx | 4 +++ libs/ui-shared/src/lib/theme/theme.ts | 1 + 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx b/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx index 3d815d00f..023127525 100644 --- a/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx +++ b/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx @@ -2,7 +2,7 @@ import React, { useRef } from "react"; import ReactDOM from "react-dom"; import styled, { css } from "styled-components"; -import { fontSize, zIndex } from "../../theme"; +import { size as getSize, fontSize, zIndex } from "../../theme"; import { useModalRoot } from "../box"; import { Icon } from "../icon"; import { Divider } from "../modal"; @@ -12,13 +12,17 @@ import { useOutsidePress } from "../utils"; import { DropDownOptionsProps } from "./drop-down.props"; import { useOptionsPosition } from "./utils"; -export const Option = styled.div<{ isSelected?: boolean }>` +export const Option = styled.div<{ + isSelected?: boolean; + size?: "small" | "medium"; +}>` align-items: center; border: 1px solid transparent; box-sizing: border-box; cursor: pointer; display: flex; - height: 24px; + height: ${(props) => + props.size == "medium" ? getSize("listElementHeight") : "24px"}; overflow: hidden; user-select: none; @@ -40,9 +44,10 @@ const ExpandedSelector = styled(Option)` margin: -1px -1px 6px -1px; `; -export const OptionText = styled(Text)` +export const OptionText = styled(Text)<{ size?: "small" | "medium" }>` flex: 1 0; - font-size: ${fontSize("small")}; + font-size: ${(props) => + props.size == "medium" ? fontSize("default") : fontSize("small")}; margin: 0 14px; overflow: hidden; text-overflow: ellipsis; @@ -60,10 +65,10 @@ const OptionDivider = styled(Divider)<{ isHidden?: boolean }>` `} `; -export const ExpandIcon = styled(Icon)` - height: 16px; +export const ExpandIcon = styled(Icon)<{ size?: "small" | "medium" }>` + height: ${(props) => (props.size === "medium" ? "32px" : "16px")}; margin-right: 10px; - width: 16px; + width: ${(props) => (props.size === "medium" ? "32px" : "16px")}; ; `; const Options = styled.div` @@ -72,7 +77,7 @@ const Options = styled.div` display: flex; flex-direction: column; pointer-events: auto; - z-index: ${zIndex("picker")}; + z-index: ${zIndex("overlayComponent")}; `; export const DropDownOptions: React.FC = ({ @@ -83,6 +88,7 @@ export const DropDownOptions: React.FC = ({ style, onChange, onDismiss, + size, ...rest }) => { const ref = useRef(null); @@ -101,11 +107,12 @@ export const DropDownOptions: React.FC = ({ const node = isOpen === false ? null : ( - + {activeOption && ( )} @@ -115,6 +122,7 @@ export const DropDownOptions: React.FC = ({
); From 64f455101d8a0a72c5456744fc7b6e3a1218a9ef Mon Sep 17 00:00:00 2001 From: Tim Riedel Date: Tue, 14 Mar 2023 12:04:35 +0100 Subject: [PATCH 099/500] feat: add query for annotations to filter by job --- .../dataset-image-list-item.tsx | 4 +- .../editor/src/queries/use-annotations-by.tsx | 39 +++++++++++++++++-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx index fa052310e..f17c5e808 100644 --- a/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx +++ b/apps/editor/src/components/menu/dataset-image-list/dataset-image-list-item.tsx @@ -9,7 +9,7 @@ import { useCallback, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import styled from "styled-components"; -import { useAnnotationsBy } from "../../../queries"; +import { useAnnotationsByImage } from "../../../queries"; import { Annotation, Image } from "../../../types"; const Spacer = styled.div` @@ -52,7 +52,7 @@ export const DatasetImageListItem = ({ isErrorAnnotations, isLoadingAnnotations, refetchAnnotations, - } = useAnnotationsBy(image.id); + } = useAnnotationsByImage(image.id); const [showAnnotations, setShowAnnotations] = useState(false); diff --git a/apps/editor/src/queries/use-annotations-by.tsx b/apps/editor/src/queries/use-annotations-by.tsx index cac4ac282..6b4b9f5fe 100644 --- a/apps/editor/src/queries/use-annotations-by.tsx +++ b/apps/editor/src/queries/use-annotations-by.tsx @@ -4,7 +4,7 @@ import { useQuery } from "react-query"; import { Annotation } from "../types"; import { hubBaseUrl } from "./hub-base-url"; -const getAnnotationsBy = async (imageId: string) => { +const getAnnotationsByImage = async (imageId: string) => { const annotationsResponse = await axios.get( `${hubBaseUrl}annotations`, { @@ -16,6 +16,18 @@ const getAnnotationsBy = async (imageId: string) => { return annotationsResponse.data; }; +const getAnnotationsByJob = async (jobId: string) => { + const annotationsResponse = await axios.get( + `${hubBaseUrl}annotations`, + { + params: { + job: jobId, + }, + }, + ); + return annotationsResponse.data; +}; + export const getAnnotation = async (annotationId: string) => { const annotationsResponse = await axios.get( `${hubBaseUrl}annotations/${annotationId}`, @@ -23,11 +35,30 @@ export const getAnnotation = async (annotationId: string) => { return annotationsResponse.data; }; -export const useAnnotationsBy = (imageId: string) => { +export const useAnnotationsByImage = (imageId: string) => { + const { data, error, isError, isLoading, refetch, remove } = useQuery< + Annotation[], + AxiosError + >(["annotationsByImage", imageId], () => getAnnotationsByImage(imageId), { + retry: 2, // retry twice if fetch fails + refetchInterval: 1000 * 20, // refetch every 20 seconds + }); + + return { + annotations: data, + annotationsError: error, + isErrorAnnotations: isError, + isLoadingAnnotations: isLoading, + refetchAnnotations: refetch, + removeAnnotations: remove, + }; +}; + +export const useAnnotationsByJob = (jobId: string) => { const { data, error, isError, isLoading, refetch, remove } = useQuery< Annotation[], AxiosError - >(["annotationsBy", imageId], () => getAnnotationsBy(imageId), { + >(["annotationsByJob", jobId], () => getAnnotationsByJob(jobId), { retry: 2, // retry twice if fetch fails refetchInterval: 1000 * 20, // refetch every 20 seconds }); @@ -42,4 +73,4 @@ export const useAnnotationsBy = (imageId: string) => { }; }; -export default useAnnotationsBy; +export default useAnnotationsByImage; From a559cd8f8412b57317b935d73e9dbe4621bfe4e7 Mon Sep 17 00:00:00 2001 From: Tonybodo <77063395+Tonybodo@users.noreply.github.com> Date: Tue, 14 Mar 2023 13:28:29 +0000 Subject: [PATCH 100/500] feat: add project-data-fileexplorer --- .../ml-model-selection-popup.tsx | 101 ++++++++-------- .../menu/project-data-explorer/index.ts | 1 + .../project-data-explorer.tsx | 112 ++++++++++++++++++ .../drop-down/drop-down-options.tsx | 6 +- 4 files changed, 169 insertions(+), 51 deletions(-) create mode 100644 apps/editor/src/components/menu/project-data-explorer/index.ts create mode 100644 apps/editor/src/components/menu/project-data-explorer/project-data-explorer.tsx diff --git a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx index 7c5d66464..b5fa61f50 100644 --- a/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx +++ b/apps/editor/src/components/menu/ml-model-popup/ml-model-selection-popup.tsx @@ -1,8 +1,11 @@ import { color, + DropDown, + EnumParam, FlexColumn, FlexRow, Icon, + IEnumParameterOption, List, ListItem, PopUp, @@ -12,13 +15,14 @@ import { } from "@visian/ui-shared"; import axios from "axios"; import { observer } from "mobx-react-lite"; +import { useMemo, useState } from "react"; import styled from "styled-components"; import { useDataset, useMlModels } from "../../../queries"; import { hubBaseUrl } from "../../../queries/hub-base-url"; -import useDatasetsBy from "../../../queries/use-datasets-by"; import { MlModel } from "../../../types"; import { MlModelList } from "../ml-model-list"; +import { ProjectDataExplorer } from "../project-data-explorer/project-data-explorer"; import { ModelPopUpProps } from "./ml-model-selection-popup.props"; const SectionLabel = styled(Text)` @@ -32,34 +36,13 @@ const ModelSelectionPopupContainer = styled(PopUp)` height: 70vh; `; -const FileExplorer = styled(FlexRow)` - width: 100%; - height: 50%; -`; - -const StyledList = styled(List)` - overflow-y: auto; -`; - -const StyledIcon = styled(Icon)` - width: 2rem; - height: 2rem; - padding-right: 0.8rem; -`; - -const VerticalLine = styled.div` - border-left: 1px solid ${color("sheetBorder")}; - margin: 0 1vw; +const DropDownContainer = styled(FlexRow)` + width: 50vw; + hight: 30%; `; export const ModelSelectionPopup = observer( ({ isOpen, onClose, activeImageSelection, projectId }) => { - const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = - useDatasetsBy(projectId); - - // const { dataset, datasetError, isErrorDataset, isLoadingDataset } = - // useDataset(datasetId); - const { mlModels, mlModelsError, isErrorMlModels, isLoadingMlModels } = useMlModels(); @@ -77,6 +60,38 @@ export const ModelSelectionPopup = observer( onClose && onClose(); } }; + const [selectedModelName, setSelectedModelName] = useState( + (mlModels && mlModels[0].name) || "", + ); + + const mlModelNameOptions: IEnumParameterOption[] = useMemo( + () => + mlModels + ? mlModels.map((model) => ({ label: model.name, value: model.name })) + : [], + [mlModels], + ); + + const availableModelVersions = useMemo( + () => + mlModels + ? mlModels.filter((model) => model.name === selectedModelName) + : [], + [mlModels, selectedModelName], + ); + + const [selectedModel, setSelectedModel] = useState( + availableModelVersions[0], + ); + + const mlModelVersionOptions: IEnumParameterOption[] = useMemo( + () => + availableModelVersions.map((model) => ({ + label: model.version, + value: model, + })), + [availableModelVersions], + ); const { t } = useTranslation(); @@ -94,29 +109,19 @@ export const ModelSelectionPopup = observer( mlModelsError?.response?.statusText } (${mlModelsError?.response?.status})`} )} - - {datasets && ( - - {datasets.map((dataset) => ( - - - {dataset.name} - - ))} - - )} - - {datasets && ( - - {datasets.map((dataset) => ( - - - {dataset.name} - - ))} - - )} - + + setSelectedModelName(newValue)} + /> + setSelectedModel(newValue)} + /> + + ); }, diff --git a/apps/editor/src/components/menu/project-data-explorer/index.ts b/apps/editor/src/components/menu/project-data-explorer/index.ts new file mode 100644 index 000000000..a8d6a185f --- /dev/null +++ b/apps/editor/src/components/menu/project-data-explorer/index.ts @@ -0,0 +1 @@ +export * from "./project-data-explorer"; diff --git a/apps/editor/src/components/menu/project-data-explorer/project-data-explorer.tsx b/apps/editor/src/components/menu/project-data-explorer/project-data-explorer.tsx new file mode 100644 index 000000000..3fa4395cd --- /dev/null +++ b/apps/editor/src/components/menu/project-data-explorer/project-data-explorer.tsx @@ -0,0 +1,112 @@ +import { color, FlexRow, Icon, List, ListItem, Text } from "@visian/ui-shared"; +import styled from "styled-components"; +import { Image } from "../../../types"; + +import useDatasetsBy from "apps/editor/src/queries/use-datasets-by"; +import { useDataset, useImagesBy } from "apps/editor/src/queries"; +import { useCallback, useEffect, useMemo, useState } from "react"; + +const FileExplorer = styled(FlexRow)` + width: 100%; + height: 50%; +`; + +const StyledList = styled(List)` + overflow-y: auto; +`; + +const StyledIcon = styled(Icon)` + width: 2rem; + height: 2rem; + padding-right: 0.8rem; +`; + +const VerticalLine = styled.div` + border-left: 1px solid ${color("sheetBorder")}; + margin: 0 1vw; +`; + +export const ProjectDataExplorer = ({ projectId }: { projectId: string }) => { + const { datasets, datasetsError, isErrorDatasets, isLoadingDatasets } = + useDatasetsBy(projectId); + + const [selectedDataset, setSelectedDataset] = useState( + (datasets && datasets[0].id) || "", + ); + + const { images, imagesError, isErrorImages, isLoadingImages, refetchImages } = + useImagesBy(selectedDataset); + + const selectDataset = useCallback( + (datasetId) => { + // saveImageSelection(selectedDataset); + setSelectedDataset(datasetId); + }, + [selectedDataset], + ); + + const [selectedImages, setSelectedImages] = useState< + Map> + >(new Map()); + + const setImageSelection = useCallback( + (datasetId: string, imageId: string, selection: boolean) => { + setSelectedImages((prevSelectedImages) => { + if (selection) { + prevSelectedImages.get(datasetId)?.set(imageId, selection); + } else { + prevSelectedImages.get(datasetId)?.delete(imageId); + } + prevSelectedImages.set( + datasetId, + prevSelectedImages.get(datasetId) || new Map(), + ); + return new Map(prevSelectedImages); + }); + }, + [], + ); + + return ( + + {datasets && ( + + {datasets.map((dataset) => ( + selectDataset(dataset.id)} + > + + {dataset.name} + + ))} + + )} + + {datasets && ( + + {images && + images.map((image) => ( + + setImageSelection( + image.dataset, + image.id, + !selectedImages.get(image.dataset)?.get(image.id), + ) + } + > + + {image.dataUri} + + ))} + + )} + + ); +}; diff --git a/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx b/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx index 023127525..695bdfd0f 100644 --- a/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx +++ b/libs/ui-shared/src/lib/components/drop-down/drop-down-options.tsx @@ -21,8 +21,7 @@ export const Option = styled.div<{ box-sizing: border-box; cursor: pointer; display: flex; - height: ${(props) => - props.size == "medium" ? getSize("listElementHeight") : "24px"}; + height: ${(props) => (props.size == "medium" ? "48px" : "24px")}; overflow: hidden; user-select: none; @@ -47,7 +46,7 @@ const ExpandedSelector = styled(Option)` export const OptionText = styled(Text)<{ size?: "small" | "medium" }>` flex: 1 0; font-size: ${(props) => - props.size == "medium" ? fontSize("default") : fontSize("small")}; + props.size ? fontSize("default") : fontSize("small")}; margin: 0 14px; overflow: hidden; text-overflow: ellipsis; @@ -120,6 +119,7 @@ export const DropDownOptions: React.FC = ({ {options.map((option, index) => (