From 6b68a981db398da03a10268b21982e94851803cf Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:37:11 +0300 Subject: [PATCH 01/14] MDE/PKFE-31 bug-fix --- .../src/features/editor/components/editorView/editorView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/front-end/src/features/editor/components/editorView/editorView.tsx b/app/front-end/src/features/editor/components/editorView/editorView.tsx index edcd860..368ad4e 100644 --- a/app/front-end/src/features/editor/components/editorView/editorView.tsx +++ b/app/front-end/src/features/editor/components/editorView/editorView.tsx @@ -165,7 +165,7 @@ export const EditorView: React.FC = () => { const { totalRows, header, rows } = fileContentResponse; if (!header) { - fileStateUpdate(undefined, { columns: [], rows: [], aggregations: {} }, undefined); + fileStateUpdate(undefined, { columns: [], rows: [], aggregations: fileContent.aggregations }, undefined); return; } From be9dc76940bfb6300dc9420e67f1879f4bb75f68 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:38:45 +0300 Subject: [PATCH 02/14] MDE/PKFE-31 updated workspace provider for file tree array --- .../stores/workspaceContextProvider.tsx | 10 +++++-- .../src/features/editor/utils/helpers.tsx | 26 ++++++++++++++++++- .../src/features/editor/utils/index.ts | 2 +- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/front-end/src/features/editor/stores/workspaceContextProvider.tsx b/app/front-end/src/features/editor/stores/workspaceContextProvider.tsx index 7a87392..16de5d8 100644 --- a/app/front-end/src/features/editor/stores/workspaceContextProvider.tsx +++ b/app/front-end/src/features/editor/stores/workspaceContextProvider.tsx @@ -1,4 +1,5 @@ import { FileContentModel, FileModel, FilePaginationModel, FileTypes } from '@/features/editor/types'; +import { getWorkspaceArray } from '@/features/editor/utils'; import { useSessionContext, useStatusContext } from '@/hooks'; import { axios, socket } from '@/lib'; import { Endpoints, Events } from '@/types'; @@ -20,6 +21,7 @@ export interface WorkspaceContextProps { // File tree state properties fileTreeIsLoading: boolean; fileTree: TreeViewBaseItem[]; + fileTreeArray: FileModel[]; } export const WorkspaceContext = createContext({ @@ -37,6 +39,7 @@ export const WorkspaceContext = createContext({ // File tree state defaults fileTreeIsLoading: true, fileTree: [], + fileTreeArray: [], }); interface Props { @@ -148,6 +151,7 @@ export const WorkspaceContextProvider: React.FC = ({ children }) => { // File tree state const [fileTreeIsLoading, setFileTreeIsLoading] = useState(true); const [fileTree, setFileTree] = useState[]>([]); + const [fileTreeArray, setFileTreeArray] = useState([]); const { blockedStateUpdate } = useStatusContext(); @@ -157,6 +161,7 @@ export const WorkspaceContextProvider: React.FC = ({ children }) => { try { const response = await axios.get(Endpoints.WORKSPACE); setFileTree(response.data); + setFileTreeArray(getWorkspaceArray(response.data)); } catch (error) { console.error('Failed to fetch workspace data:', error); } finally { @@ -192,8 +197,9 @@ export const WorkspaceContextProvider: React.FC = ({ children }) => { filesHistory, filesHistoryStateUpdate, - fileTreeIsLoading: fileTreeIsLoading, - fileTree: fileTree, + fileTreeIsLoading, + fileTree, + fileTreeArray, }; return {children}; diff --git a/app/front-end/src/features/editor/utils/helpers.tsx b/app/front-end/src/features/editor/utils/helpers.tsx index 6b4db62..c0263bb 100644 --- a/app/front-end/src/features/editor/utils/helpers.tsx +++ b/app/front-end/src/features/editor/utils/helpers.tsx @@ -1,6 +1,6 @@ +import { FileModel, FileTreeViewItemProps, FileTypes } from '@/features/editor/types'; import { Article as ArticleIcon, FolderRounded, InsertDriveFile as InsertDriveFileIcon } from '@mui/icons-material'; import { TreeViewBaseItem } from '@mui/x-tree-view'; -import { FileTreeViewItemProps, FileTypes } from '../types'; export const isExpandable = (reactChildren: React.ReactNode) => { if (Array.isArray(reactChildren)) { @@ -66,3 +66,27 @@ export const getFileExtension = (filename: string): string => { const dotIndex = filename.lastIndexOf('.'); return dotIndex !== -1 ? filename.substring(dotIndex + 1).toLowerCase() : ''; }; + +export const getWorkspaceArray = (fileTreeView: TreeViewBaseItem[]): FileModel[] => { + const workspaceArray: FileModel[] = []; + fileTreeView.sort((a, b) => { + if (a.fileType === FileTypes.FOLDER || b.fileType === FileTypes.FOLDER) { + if (a.fileType === b.fileType) { + return a.id.localeCompare(b.id); + } + + return a.fileType === FileTypes.FOLDER ? 1 : -1; + } + + return a.id.localeCompare(b.id); + }); + + for (const item of fileTreeView) { + workspaceArray.push({ id: item.id, label: item.label, type: item.fileType }); + if (item.children && item.children.length !== 0) { + workspaceArray.push(...getWorkspaceArray(item.children)); + } + } + + return workspaceArray; +}; diff --git a/app/front-end/src/features/editor/utils/index.ts b/app/front-end/src/features/editor/utils/index.ts index 76178c3..acd3fe7 100644 --- a/app/front-end/src/features/editor/utils/index.ts +++ b/app/front-end/src/features/editor/utils/index.ts @@ -1 +1 @@ -export { doesFileExist, findUniqueFileName, getFileExtension, getIconFromFileType, isExpandable } from './helpers'; +export { doesFileExist, findUniqueFileName, getFileExtension, getIconFromFileType, isExpandable, getWorkspaceArray } from './helpers'; From fc8d0679b335044b417add2acff616299aed1cf6 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:40:05 +0300 Subject: [PATCH 03/14] MDE/PKFE-31 toolbar resizing --- .../editor/components/toolbarView/toolbarGroupsSelector.tsx | 2 +- app/front-end/src/features/editor/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupsSelector.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupsSelector.tsx index 15cc20f..dcea7bc 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupsSelector.tsx +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupsSelector.tsx @@ -26,5 +26,5 @@ export interface ToolbarGroupsSelectorProps { * @returns {JSX.Element} The rendered List component. */ export const ToolbarGroupsSelector: React.FC = ({ children }) => { - return {children}; + return {children}; }; diff --git a/app/front-end/src/features/editor/index.tsx b/app/front-end/src/features/editor/index.tsx index fc54172..de4c293 100644 --- a/app/front-end/src/features/editor/index.tsx +++ b/app/front-end/src/features/editor/index.tsx @@ -54,7 +54,7 @@ export const Editor = () => { { Date: Sun, 8 Sep 2024 00:41:14 +0300 Subject: [PATCH 04/14] MDE/PKFE-31 implemented toolbar context provider --- .../src/features/editor/hooks/index.ts | 1 + .../editor/hooks/useToolbarContext.ts | 4 + .../src/features/editor/stores/index.ts | 2 + .../editor/stores/toolbarContextProvider.tsx | 197 ++++++++++++++++++ .../features/editor/types/enums/genesEnum.tsx | 3 + .../src/features/editor/types/index.ts | 1 + 6 files changed, 208 insertions(+) create mode 100644 app/front-end/src/features/editor/hooks/useToolbarContext.ts create mode 100644 app/front-end/src/features/editor/stores/toolbarContextProvider.tsx create mode 100644 app/front-end/src/features/editor/types/enums/genesEnum.tsx diff --git a/app/front-end/src/features/editor/hooks/index.ts b/app/front-end/src/features/editor/hooks/index.ts index 5ff1d0d..2cbaa08 100644 --- a/app/front-end/src/features/editor/hooks/index.ts +++ b/app/front-end/src/features/editor/hooks/index.ts @@ -1 +1,2 @@ +export { useToolbarContext } from './useToolbarContext'; export { useWorkspaceContext } from './useWorkspaceContext'; diff --git a/app/front-end/src/features/editor/hooks/useToolbarContext.ts b/app/front-end/src/features/editor/hooks/useToolbarContext.ts new file mode 100644 index 0000000..7a25802 --- /dev/null +++ b/app/front-end/src/features/editor/hooks/useToolbarContext.ts @@ -0,0 +1,4 @@ +import { ToolbarContext } from '@/features/editor/stores'; +import { useContext } from 'react'; + +export const useToolbarContext = () => useContext(ToolbarContext); diff --git a/app/front-end/src/features/editor/stores/index.ts b/app/front-end/src/features/editor/stores/index.ts index 790e4c1..45278ee 100644 --- a/app/front-end/src/features/editor/stores/index.ts +++ b/app/front-end/src/features/editor/stores/index.ts @@ -1,2 +1,4 @@ +export { ToolbarContext, ToolbarContextProvider } from './toolbarContextProvider'; +export type { ToolbarContextProps } from './toolbarContextProvider'; export { WorkspaceContext, WorkspaceContextProvider } from './workspaceContextProvider'; export type { WorkspaceContextProps } from './workspaceContextProvider'; diff --git a/app/front-end/src/features/editor/stores/toolbarContextProvider.tsx b/app/front-end/src/features/editor/stores/toolbarContextProvider.tsx new file mode 100644 index 0000000..5e4339d --- /dev/null +++ b/app/front-end/src/features/editor/stores/toolbarContextProvider.tsx @@ -0,0 +1,197 @@ +import { GenesEnum } from '@/features/editor/types'; +import React, { createContext, useState } from 'react'; + +export interface ToolbarContextProps { + // + // Universal state properties + // + saveTo: string; + override: boolean; + saveToStateUpdate: (saveTo: string, override?: boolean) => void; + + // + // Download state properties + // + gene: GenesEnum; + geneStateUpdate: (gene: GenesEnum) => void; + + // + // Merge state properties + // + lovdFile: string; + clinvarFile: string; + gnomadFile: string; + mergeStateUpdate: (lovdFile: string, clinvarFile: string, gnomadFile: string) => void; + + lovdError: string; + lovdErrorStateUpdate: (lovdFileError: string) => void; + clinvarError: string; + clinvarErrorStateUpdate: (clinvarFileError: string) => void; + gnomadError: string; + gnomadErrorStateUpdate: (gnomadFileError: string) => void; + + // + // Apply state properties + // + applyTo: string; + applyToStateUpdate: (applyTo: string) => void; + + applyError: string; + applyErrorStateUpdate: (applyError: string) => void; +} + +export const ToolbarContext = createContext({ + // + // Universal state defaults + // + saveTo: '/', // Root directory (new file) + override: false, + saveToStateUpdate: () => {}, + + // + // Download state defaults + // + gene: GenesEnum.EYS, + geneStateUpdate: () => {}, + + // + // Merge state defaults + // + lovdFile: '', + clinvarFile: '', + gnomadFile: '', + mergeStateUpdate: () => {}, + + lovdError: '', + lovdErrorStateUpdate: () => {}, + clinvarError: '', + clinvarErrorStateUpdate: () => {}, + gnomadError: '', + gnomadErrorStateUpdate: () => {}, + + // + // Apply state defaults + // + applyTo: '', + applyToStateUpdate: () => {}, + + applyError: '', + applyErrorStateUpdate: () => {}, +}); + +interface Props { + children?: React.ReactNode; +} + +export const ToolbarContextProvider: React.FC = ({ children }) => { + /*************** + State management + ***************/ + + // + // Universal state + // + const [saveTo, setSaveTo] = useState('/'); + const [override, setOverride] = useState(false); + + const saveToStateUpdate = (saveTo: string, override?: boolean) => { + setSaveTo(saveTo); + if (override !== undefined) setOverride(override); + }; + + // + // Download state + // + const [gene, setGene] = useState(GenesEnum.EYS); + + const geneStateUpdate = (gene: GenesEnum) => { + setGene(gene); + }; + + // + // Merge state + // + const [lovdFile, setLovdFile] = useState(''); + const [clinvarFile, setClinvarFile] = useState(''); + const [gnomadFile, setGnomadFile] = useState(''); + + const mergeStateUpdate = (lovdFile: string, clinvarFile: string, gnomadFile: string) => { + setLovdFile(lovdFile); + setClinvarFile(clinvarFile); + setGnomadFile(gnomadFile); + }; + + const [lovdError, setLovdError] = useState(''); + + const lovdErrorStateUpdate = (lovdFileError: string) => { + setLovdError(lovdFileError); + }; + + const [clinvarError, setClinvarError] = useState(''); + + const clinvarErrorStateUpdate = (clinvarFileError: string) => { + setClinvarError(clinvarFileError); + }; + + const [gnomadError, setGnomadError] = useState(''); + + const gnomadErrorStateUpdate = (gnomadFileError: string) => { + setGnomadError(gnomadFileError); + }; + + // + // Apply state + // + const [applyTo, setApplyTo] = useState(''); + + const applyToStateUpdate = (applyTo: string) => { + setApplyTo(applyTo); + }; + + const [applyError, setApplyError] = useState(''); + + const applyErrorStateUpdate = (applyError: string) => { + setApplyError(applyError); + }; + + const ToolbarContextValue: ToolbarContextProps = { + // + // Universal state + // + saveTo, + override, + saveToStateUpdate, + + // + // Download state + // + gene, + geneStateUpdate, + + // + // Merge state + // + lovdFile, + clinvarFile, + gnomadFile, + mergeStateUpdate, + + lovdError, + lovdErrorStateUpdate, + clinvarError, + clinvarErrorStateUpdate, + gnomadError, + gnomadErrorStateUpdate, + + // + // Apply state + // + applyTo, + applyToStateUpdate, + + applyError, + applyErrorStateUpdate, + }; + + return {children}; +}; diff --git a/app/front-end/src/features/editor/types/enums/genesEnum.tsx b/app/front-end/src/features/editor/types/enums/genesEnum.tsx new file mode 100644 index 0000000..c73fac4 --- /dev/null +++ b/app/front-end/src/features/editor/types/enums/genesEnum.tsx @@ -0,0 +1,3 @@ +export enum GenesEnum { + EYS = 'eys', +} diff --git a/app/front-end/src/features/editor/types/index.ts b/app/front-end/src/features/editor/types/index.ts index 8abb6be..d23c13e 100644 --- a/app/front-end/src/features/editor/types/index.ts +++ b/app/front-end/src/features/editor/types/index.ts @@ -1,5 +1,6 @@ export { ConsoleFeedbackTypes } from './consoleFeedback'; export type { ConsoleFeedback } from './consoleFeedback'; +export { GenesEnum } from './enums/genesEnum'; export type { FileDataRequestDTO, FileDataResponseDTO } from './fileData'; export { FileTreeItemContextMenuActions } from './fileTreeItemContextMenuActions'; export type { FileTreeViewItemProps } from './fileTreeViewItemProps'; From 456a4f721116dfe4b07cc7bf678063a516caa9b4 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:43:17 +0300 Subject: [PATCH 05/14] MDE/PKFE-31 overhauled toolbar buttons logic for hook support --- .../toolbarGroupButtons/applyGroupButtons.tsx | 106 +++++++++++--- .../downloadGroupButtons.tsx | 133 ++++++++++++++---- .../toolbarView/toolbarGroupButtons/index.ts | 3 + .../toolbarGroupButtons/mergeGroupButtons.tsx | 133 +++++++++++++++--- 4 files changed, 302 insertions(+), 73 deletions(-) diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/applyGroupButtons.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/applyGroupButtons.tsx index 3044051..c099c10 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/applyGroupButtons.tsx +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/applyGroupButtons.tsx @@ -1,26 +1,90 @@ +import { ToolbarGroupItem, ToolbarGroupItemProps } from '@/features/editor/components/toolbarView'; +import { useToolbarContext } from '@/features/editor/hooks'; +import { useStatusContext } from '@/hooks'; import { Deblur as DeblurIcon } from '@mui/icons-material'; +import { useCallback, useMemo } from 'react'; -import { ToolbarGroupItemProps } from '@/features/editor/components/toolbarView'; +export interface ApplyGroupButtonsProps {} -const applySpliceAiClick = () => { - console.log('Clicked Apply SpliceAI Button!'); -}; +export const ApplyGroupButtons: React.FC = () => { + const { blockedStateUpdate } = useStatusContext(); + const { saveTo, override, applyTo, applyErrorStateUpdate } = useToolbarContext(); -const applyCaddClick = () => { - console.log('Clicked Apply CADD Button!'); -}; + const applySpliceAiClick = useCallback(async () => { + if (!applyTo) { + applyErrorStateUpdate('Please select a file'); + return; + } + + blockedStateUpdate(true); + + try { + console.log( + 'Clicked Apply SpliceAI Button! Params:\n saveTo:', + saveTo, + '\n override:', + override, + '\n applyTo:', + applyTo + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // TODO: remove this line + } catch (error) { + console.error('Error applying SpliceAI:', error); + } finally { + blockedStateUpdate(false); + } + }, [saveTo, override, applyTo]); + + const applyCaddClick = useCallback(async () => { + if (!applyTo) { + applyErrorStateUpdate('Please select a file'); + return; + } -export const ApplyGroupButtons: ToolbarGroupItemProps[] = [ - { - group: 'apply', - icon: DeblurIcon, - label: 'Apply SpliceAI', - onClick: applySpliceAiClick, - }, - { - group: 'apply', - icon: DeblurIcon, - label: 'Apply CADD', - onClick: applyCaddClick, - }, -]; + blockedStateUpdate(true); + + try { + console.log( + 'Clicked Merge LOVD & ClinVar Button! Params:\n saveTo:', + saveTo, + '\n override:', + override, + '\n applyTo:', + applyTo + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // TODO: remove this line + } catch (error) { + console.error('Error applying CADD:', error); + } finally { + blockedStateUpdate(false); + } + }, [saveTo, override, applyTo]); + + const buttons: ToolbarGroupItemProps[] = useMemo( + () => [ + { + group: 'apply', + icon: DeblurIcon, + label: 'Apply SpliceAI', + onClick: applySpliceAiClick, + }, + { + group: 'apply', + icon: DeblurIcon, + label: 'Apply CADD', + onClick: applyCaddClick, + }, + ], + [applySpliceAiClick, applyCaddClick] + ); + + return ( + <> + {buttons.map((button, index) => ( + + ))} + + ); +}; diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/downloadGroupButtons.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/downloadGroupButtons.tsx index febfd84..39a4133 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/downloadGroupButtons.tsx +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/downloadGroupButtons.tsx @@ -1,36 +1,107 @@ -import { ToolbarGroupItemProps } from '@/features/editor/components/toolbarView'; - +import { ToolbarGroupItem, ToolbarGroupItemProps } from '@/features/editor/components/toolbarView'; +import { useToolbarContext } from '@/features/editor/hooks'; +import { useStatusContext } from '@/hooks'; import { Download as DownloadIcon } from '@mui/icons-material'; +import { useCallback, useMemo } from 'react'; -const handleDownloadLovdClick = () => { - console.log('Clicked Download Lovd Button!'); -}; +export interface DownloadGroupButtonsProps {} -const handleDownloadClinvarClick = () => { - console.log('Clicked Download Clinvar Button!'); -}; +export const DownloadGroupButtons: React.FC = () => { + const { blockedStateUpdate } = useStatusContext(); + const { saveTo, override, gene } = useToolbarContext(); -const handleDownloadGnomadClick = () => { - console.log('Clicked Download Gnomad Button!'); -}; + const handleDownloadLovdClick = useCallback(async () => { + blockedStateUpdate(true); + + try { + console.log( + 'Clicked Download Lovd Button! Params:\n saveTo:', + saveTo, + '\n override:', + override, + '\n gene:', + gene + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // TODO: remove this line + } catch (error) { + console.error('Error downloading LOVD file:', error); + } finally { + blockedStateUpdate(false); + } + }, [saveTo, override, gene]); + + const handleDownloadClinvarClick = useCallback(async () => { + blockedStateUpdate(true); -export const DownloadGroupButtons: ToolbarGroupItemProps[] = [ - { - group: 'download', - icon: DownloadIcon, - label: 'LOVD', - onClick: handleDownloadLovdClick, - }, - { - group: 'download', - icon: DownloadIcon, - label: 'ClinVar', - onClick: handleDownloadClinvarClick, - }, - { - group: 'download', - icon: DownloadIcon, - label: 'gnomAD', - onClick: handleDownloadGnomadClick, - }, -]; + try { + console.log( + 'Clicked Download Clinvar Button! Params:\n saveTo:', + saveTo, + '\n override:', + override, + '\n gene:', + gene + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // TODO: remove this line + } catch (error) { + console.error('Error downloading ClinVar file:', error); + } finally { + blockedStateUpdate(false); + } + }, [saveTo, override, gene]); + + const handleDownloadGnomadClick = useCallback(async () => { + blockedStateUpdate(true); + + try { + console.log( + 'Clicked Download Gnomad Button! Params:\n saveTo:', + saveTo, + '\n override:', + override, + '\n gene:', + gene + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // TODO: remove this line + } catch (error) { + console.error('Error downloading gnomAD file:', error); + } finally { + blockedStateUpdate(false); + } + }, [saveTo, override, gene]); + + const buttons: ToolbarGroupItemProps[] = useMemo( + () => [ + { + group: 'download', + icon: DownloadIcon, + label: 'LOVD', + onClick: handleDownloadLovdClick, + }, + { + group: 'download', + icon: DownloadIcon, + label: 'ClinVar', + onClick: handleDownloadClinvarClick, + }, + { + group: 'download', + icon: DownloadIcon, + label: 'gnomAD', + onClick: handleDownloadGnomadClick, + }, + ], + [handleDownloadLovdClick, handleDownloadClinvarClick, handleDownloadGnomadClick] + ); + + return ( + <> + {buttons.map((button, index) => ( + + ))} + + ); +}; diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/index.ts b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/index.ts index 243e3af..5e8bc3b 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/index.ts +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/index.ts @@ -1,3 +1,6 @@ export { ApplyGroupButtons } from './applyGroupButtons'; +export type { ApplyGroupButtonsProps } from './applyGroupButtons'; export { DownloadGroupButtons } from './downloadGroupButtons'; +export type { DownloadGroupButtonsProps } from './downloadGroupButtons'; export { MergeGroupButtons } from './mergeGroupButtons'; +export type { MergeGroupButtonsProps } from './mergeGroupButtons'; diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/mergeGroupButtons.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/mergeGroupButtons.tsx index 73d2271..6e1a22c 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/mergeGroupButtons.tsx +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupButtons/mergeGroupButtons.tsx @@ -1,26 +1,117 @@ +import { ToolbarGroupItem, ToolbarGroupItemProps } from '@/features/editor/components/toolbarView'; +import { useToolbarContext } from '@/features/editor/hooks'; +import { useStatusContext } from '@/hooks'; import { MergeType as MergeTypeIcon } from '@mui/icons-material'; +import { useCallback, useMemo } from 'react'; -import { ToolbarGroupItemProps } from '@/features/editor/components/toolbarView'; +export interface MergeGroupButtonsProps {} -const mergeLovdAndGnomadClick = () => { - console.log('Clicked Merge LOVD & gnomAD Button!'); -}; +export const MergeGroupButtons: React.FC = () => { + const { blockedStateUpdate } = useStatusContext(); + const { + saveTo, + override, + lovdFile, + clinvarFile, + gnomadFile, + lovdErrorStateUpdate, + clinvarErrorStateUpdate, + gnomadErrorStateUpdate, + } = useToolbarContext(); -const mergeLovdAndClinvarClick = () => { - console.log('Clicked Merge LOVD & ClinVar Button!'); -}; + const mergeLovdAndGnomadClick = useCallback(async () => { + clinvarErrorStateUpdate(''); + + if (!lovdFile) { + lovdErrorStateUpdate('Please select a LOVD file'); + return; + } + + if (!gnomadFile) { + gnomadErrorStateUpdate('Please select a gnomAD file'); + return; + } + + blockedStateUpdate(true); + + try { + console.log( + 'Clicked Merge LOVD & gnomAD Button! Params:\n saveTo:', + saveTo, + '\n override:', + override, + '\n lovd:', + lovdFile, + '\n gnomad:', + gnomadFile + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // TODO: remove this line + } catch (error) { + console.error('Error merging LOVD & gnomAD files:', error); + } finally { + blockedStateUpdate(false); + } + }, [saveTo, override, lovdFile, gnomadFile]); + + const mergeLovdAndClinvarClick = useCallback(async () => { + gnomadErrorStateUpdate(''); -export const MergeGroupButtons: ToolbarGroupItemProps[] = [ - { - group: 'merge', - icon: MergeTypeIcon, - label: 'Merge LOVD & gnomAD', - onClick: mergeLovdAndGnomadClick, - }, - { - group: 'merge', - icon: MergeTypeIcon, - label: 'Merge LOVD & ClinVar', - onClick: mergeLovdAndClinvarClick, - }, -]; + if (!lovdFile) { + lovdErrorStateUpdate('Please select a LOVD file'); + return; + } + + if (!clinvarFile) { + clinvarErrorStateUpdate('Please select a ClinVar file'); + return; + } + + blockedStateUpdate(true); + + try { + console.log( + 'Clicked Merge LOVD & ClinVar Button! Params:\n saveTo:', + saveTo, + '\n override:', + override, + '\n lovd:', + lovdFile, + '\n clinvar:', + clinvarFile + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); // TODO: remove this line + } catch (error) { + console.error('Error merging LOVD & ClinVar files:', error); + } finally { + blockedStateUpdate(false); + } + }, [saveTo, override, lovdFile, clinvarFile]); + + const buttons: ToolbarGroupItemProps[] = useMemo( + () => [ + { + group: 'merge', + icon: MergeTypeIcon, + label: 'Merge LOVD & gnomAD', + onClick: mergeLovdAndGnomadClick, + }, + { + group: 'merge', + icon: MergeTypeIcon, + label: 'Merge LOVD & ClinVar', + onClick: mergeLovdAndClinvarClick, + }, + ], + [mergeLovdAndGnomadClick, mergeLovdAndClinvarClick] + ); + + return ( + <> + {buttons.map((button, index) => ( + + ))} + + ); +}; From 26f842dd106d33adfaa7c3b839488d62997ceb3d Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:43:46 +0300 Subject: [PATCH 06/14] MDE/PKFE-31 styled core components for param fields --- .../styledGroupParamsComponents.tsx | 37 +++++++++++++++++++ .../src/stores/themeContextProvider.tsx | 2 + 2 files changed, 39 insertions(+) create mode 100644 app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/styledGroupParamsComponents.tsx diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/styledGroupParamsComponents.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/styledGroupParamsComponents.tsx new file mode 100644 index 0000000..6722d9c --- /dev/null +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/styledGroupParamsComponents.tsx @@ -0,0 +1,37 @@ +import { InputLabel, ListSubheader, MenuItem, Select, Typography, styled } from '@mui/material'; + +export const StyledGroupParamsTypography = styled(Typography)({ + fontSize: '0.875rem', + transition: 'color 0.3s ease', +}); + +export const StyledGroupParamsInputLabel = styled(InputLabel)({ + transition: 'color 0.3s ease', +}); + +export const StyledGroupParamsSelect = styled(Select)({ + height: '3rem', + transition: 'color 0.3s ease, border-color 0.3s ease', +}); + +export const StyledGroupParamsMenuItem = styled(MenuItem)({ + whiteSpace: 'normal', +}); + +export const StyledGroupParamsListSubheader = styled(ListSubheader)(({ theme }) => ({ + backgroundColor: theme.palette.background.default, + whiteSpace: 'normal', +})); + +export const StyledGroupParamsMenuItemTypography = styled(Typography)({ + width: '100%', + fontSize: '1rem', + textAlign: 'right', + overflowWrap: 'break-word', +}); + +export const StyledGroupParamsMenuItemTypographyBold = styled(Typography)({ + fontSize: '0.875rem', + fontWeight: 'bold', + overflowWrap: 'break-word', +}); diff --git a/app/front-end/src/stores/themeContextProvider.tsx b/app/front-end/src/stores/themeContextProvider.tsx index dc7770b..33295b3 100644 --- a/app/front-end/src/stores/themeContextProvider.tsx +++ b/app/front-end/src/stores/themeContextProvider.tsx @@ -99,6 +99,8 @@ export const ThemeContextProvider: React.FC = ({ children }) => { fontSize: '20px', color: mode === 'light' ? Colors.textPrimaryLight : Colors.textPrimaryDark, fontWeight: '400', + overflow: 'hidden', + textOverflow: 'ellipsis', }, }, }, From 36af934f7641e156908ff606ca36c08421ae35a5 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:44:45 +0300 Subject: [PATCH 07/14] MDE/PKFE-31 implemented different groups params fields --- .../toolbarGroupParams/applyGroupParams.tsx | 190 +++++++++++ .../downloadGroupParams.tsx | 263 +++++++++++++++ .../toolbarView/toolbarGroupParams/index.ts | 15 + .../toolbarGroupParams/mergeGroupParams.tsx | 316 ++++++++++++++++++ 4 files changed, 784 insertions(+) create mode 100644 app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/applyGroupParams.tsx create mode 100644 app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/downloadGroupParams.tsx create mode 100644 app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/index.ts create mode 100644 app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/mergeGroupParams.tsx diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/applyGroupParams.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/applyGroupParams.tsx new file mode 100644 index 0000000..cf80bf9 --- /dev/null +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/applyGroupParams.tsx @@ -0,0 +1,190 @@ +import { + StyledGroupParamsInputLabel, + StyledGroupParamsListSubheader, + StyledGroupParamsMenuItem, + StyledGroupParamsMenuItemTypography, + StyledGroupParamsMenuItemTypographyBold, + StyledGroupParamsSelect, + StyledGroupParamsTypography, +} from '@/features/editor/components/toolbarView/toolbarGroupParams'; +import { useToolbarContext, useWorkspaceContext } from '@/features/editor/hooks'; +import { FileModel, FileTypes } from '@/features/editor/types'; +import { getWorkspaceArray } from '@/features/editor/utils'; +import { useStatusContext } from '@/hooks'; +import { Box, Checkbox, FormControl, FormControlLabel, SelectChangeEvent, useTheme } from '@mui/material'; +import { useEffect, useState } from 'react'; + +export interface ApplyGroupParamsProps {} + +export const ApplyGroupParams: React.FC = () => { + const { blocked } = useStatusContext(); + const Theme = useTheme(); + const { fileTree, fileTreeArray } = useWorkspaceContext(); + const { saveTo, saveToStateUpdate, applyTo, applyToStateUpdate, applyError, applyErrorStateUpdate } = + useToolbarContext(); + + // + // Apply state + // + const [applyToValue, setApplyToValue] = useState(applyTo); + + const handleApplyToChange = (event: SelectChangeEvent) => { + setApplyToValue(event.target.value); + applyToStateUpdate(event.target.value); + applyErrorStateUpdate(''); + }; + + // + // Save To state + // + const [fileArray, setFileArray] = useState(fileTreeArray); + const [saveToValue, setSaveToValue] = useState(saveTo); + const [overrideValue, setOverrideValue] = useState(false); + + const handleSaveToChange = (event: SelectChangeEvent) => { + setSaveToValue(event.target.value); + saveToStateUpdate(event.target.value, false); + setOverrideValue(false); + }; + + const handleOverrideChange = (event: React.ChangeEvent) => { + setOverrideValue(event.target.checked); + saveToStateUpdate(saveToValue, event.target.checked); + }; + + // + // Effects + // + useEffect(() => { + setFileArray(getWorkspaceArray(fileTree)); + }, [fileTree]); + + return ( + + + + + {applyError ? applyError : 'Apply To'} + + + + root: + + {fileArray.map((file) => { + if (file.type === FileTypes.FOLDER) { + return ( + + {file.id}: + + ); + } + + return ( + + {file.label} + + ); + })} + + + + + + + Save To + + + + New file... + + + root: + + {fileArray.map((file) => { + if (file.type === FileTypes.FOLDER) { + return ( + + {file.id}: + + ); + } + + return ( + + {file.label} + + ); + })} + + + + } + label={ + + Override + + } + labelPlacement='start' + /> + + + ); +}; diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/downloadGroupParams.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/downloadGroupParams.tsx new file mode 100644 index 0000000..cce8d65 --- /dev/null +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/downloadGroupParams.tsx @@ -0,0 +1,263 @@ +import { + StyledGroupParamsInputLabel, + StyledGroupParamsListSubheader, + StyledGroupParamsMenuItem, + StyledGroupParamsMenuItemTypography, + StyledGroupParamsMenuItemTypographyBold, + StyledGroupParamsSelect, + StyledGroupParamsTypography, +} from '@/features/editor/components/toolbarView/toolbarGroupParams'; +import { useToolbarContext, useWorkspaceContext } from '@/features/editor/hooks'; +import { FileModel, FileTypes, GenesEnum } from '@/features/editor/types'; +import { getWorkspaceArray } from '@/features/editor/utils'; +import { useStatusContext } from '@/hooks'; +import { Box, Checkbox, FormControl, FormControlLabel, SelectChangeEvent, useTheme } from '@mui/material'; +import { useEffect, useState } from 'react'; + +export interface DownloadGroupParamsProps {} + +export const DownloadGroupParams: React.FC = () => { + const { blocked } = useStatusContext(); + const Theme = useTheme(); + const { fileTree, fileTreeArray } = useWorkspaceContext(); + const { saveTo, saveToStateUpdate, gene, geneStateUpdate } = useToolbarContext(); + + // + // Gene state + // + const [geneValue, setGeneValue] = useState(gene); + + const handleGeneChange = (event: SelectChangeEvent) => { + setGeneValue(event.target.value); + geneStateUpdate(event.target.value); + }; + + // + // Save To state + // + const [fileArray, setFileArray] = useState(fileTreeArray); + const [saveToValue, setSaveToValue] = useState(saveTo); + const [overrideValue, setOverrideValue] = useState(false); + + const handleSaveToChange = (event: SelectChangeEvent) => { + setSaveToValue(event.target.value); + saveToStateUpdate(event.target.value, false); + setOverrideValue(false); + }; + + const handleOverrideChange = (event: React.ChangeEvent) => { + setOverrideValue(event.target.checked); + saveToStateUpdate(saveToValue, event.target.checked); + }; + + // + // Effects + // + useEffect(() => { + setFileArray(getWorkspaceArray(fileTree)); + }, [fileTree]); + + return ( + + + + + Gene + + + + {GenesEnum.EYS.toUpperCase()} + + + + + + + + Save To + + + + New file... + + + root: + + {fileArray.map((file) => { + if (file.type === FileTypes.FOLDER) { + return ( + + {file.id}: + + ); + } + + return ( + + {file.label} + + ); + })} + + + + } + label={ + + Override + + } + labelPlacement='start' + /> + + + ); +}; + +{ + /* Gene + */ +} + +{ + /* Save To + */ +} diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/index.ts b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/index.ts new file mode 100644 index 0000000..e432661 --- /dev/null +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/index.ts @@ -0,0 +1,15 @@ +export { ApplyGroupParams } from './applyGroupParams'; +export type { ApplyGroupParamsProps } from './applyGroupParams'; +export { DownloadGroupParams } from './downloadGroupParams'; +export type { DownloadGroupParamsProps } from './downloadGroupParams'; +export { MergeGroupParams } from './mergeGroupParams'; +export type { MergeGroupParamsProps } from './mergeGroupParams'; +export { + StyledGroupParamsInputLabel, + StyledGroupParamsListSubheader, + StyledGroupParamsMenuItem, + StyledGroupParamsMenuItemTypography, + StyledGroupParamsMenuItemTypographyBold, + StyledGroupParamsSelect, + StyledGroupParamsTypography, +} from './styledGroupParamsComponents'; diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/mergeGroupParams.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/mergeGroupParams.tsx new file mode 100644 index 0000000..ff26f50 --- /dev/null +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupParams/mergeGroupParams.tsx @@ -0,0 +1,316 @@ +import { + StyledGroupParamsInputLabel, + StyledGroupParamsListSubheader, + StyledGroupParamsMenuItem, + StyledGroupParamsMenuItemTypography, + StyledGroupParamsMenuItemTypographyBold, + StyledGroupParamsSelect, + StyledGroupParamsTypography, +} from '@/features/editor/components/toolbarView/toolbarGroupParams'; +import { useToolbarContext, useWorkspaceContext } from '@/features/editor/hooks'; +import { FileModel, FileTypes } from '@/features/editor/types'; +import { getWorkspaceArray } from '@/features/editor/utils'; +import { useStatusContext } from '@/hooks'; +import { Box, Checkbox, FormControl, FormControlLabel, SelectChangeEvent, useTheme } from '@mui/material'; +import { useEffect, useState } from 'react'; + +export interface MergeGroupParamsProps {} + +export const MergeGroupParams: React.FC = () => { + const { blocked } = useStatusContext(); + const Theme = useTheme(); + const { fileTree, fileTreeArray } = useWorkspaceContext(); + const { + saveTo, + saveToStateUpdate, + lovdFile, + clinvarFile, + gnomadFile, + mergeStateUpdate, + lovdError, + clinvarError, + gnomadError, + lovdErrorStateUpdate, + clinvarErrorStateUpdate, + gnomadErrorStateUpdate, + } = useToolbarContext(); + + // + // Merge state + // + const [lovdFileValue, setLovdFileValue] = useState(lovdFile); + const [clinvarFileValue, setClinvarFileValue] = useState(clinvarFile); + const [gnomadFileValue, setGnomadFileValue] = useState(gnomadFile); + + const handleLovdFileChange = (event: SelectChangeEvent) => { + setLovdFileValue(event.target.value); + mergeStateUpdate(event.target.value, clinvarFileValue, gnomadFileValue); + lovdErrorStateUpdate(''); + }; + + const handleClinvarFileChange = (event: SelectChangeEvent) => { + setClinvarFileValue(event.target.value); + mergeStateUpdate(lovdFileValue, event.target.value, gnomadFileValue); + clinvarErrorStateUpdate(''); + }; + + const handleGnomadFileChange = (event: SelectChangeEvent) => { + setGnomadFileValue(event.target.value); + mergeStateUpdate(lovdFileValue, clinvarFileValue, event.target.value); + gnomadErrorStateUpdate(''); + }; + + // + // Save To state + // + const [fileArray, setFileArray] = useState(fileTreeArray); + const [saveToValue, setSaveToValue] = useState(saveTo); + const [overrideValue, setOverrideValue] = useState(false); + + const handleSaveToChange = (event: SelectChangeEvent) => { + setSaveToValue(event.target.value); + saveToStateUpdate(event.target.value, false); + setOverrideValue(false); + }; + + const handleOverrideChange = (event: React.ChangeEvent) => { + setOverrideValue(event.target.checked); + saveToStateUpdate(saveToValue, event.target.checked); + }; + + // + // Effects + // + useEffect(() => { + setFileArray(getWorkspaceArray(fileTree)); + }, [fileTree]); + + return ( + + + + + {lovdError ? lovdError : 'Lovd File'} + + + + root: + + {fileArray.map((file) => { + if (file.type === FileTypes.FOLDER) { + return ( + + {file.id}: + + ); + } + + return ( + + {file.label} + + ); + })} + + + + + {clinvarError ? clinvarError : 'Clinvar File'} + + + + root: + + {fileArray.map((file) => { + if (file.type === FileTypes.FOLDER) { + return ( + + {file.id}: + + ); + } + + return ( + + {file.label} + + ); + })} + + + + + {gnomadError ? gnomadError : 'Gnomad File'} + + + + root: + + {fileArray.map((file) => { + if (file.type === FileTypes.FOLDER) { + return ( + + {file.id}: + + ); + } + + return ( + + {file.label} + + ); + })} + + + + + + + Save To + + + + New file... + + + root: + + {fileArray.map((file) => { + if (file.type === FileTypes.FOLDER) { + return ( + + {file.id}: + + ); + } + + return ( + + {file.label} + + ); + })} + + + + } + label={ + + Override + + } + labelPlacement='start' + /> + + + ); +}; From 523657e534c4e7b46bfb97ae6201e7a1a54722f2 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:45:07 +0300 Subject: [PATCH 08/14] MDE/PKFE-31 updated toolbar view --- .../components/toolbarView/toolbarGroup.tsx | 38 ++++++++++++------- .../components/toolbarView/toolbarView.tsx | 36 ++++++++++++------ 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroup.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroup.tsx index 031a267..1d62db3 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarGroup.tsx +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroup.tsx @@ -1,7 +1,8 @@ -import { List, useTheme } from '@mui/material'; +import { Box, List, useTheme } from '@mui/material'; export interface ToolbarGroupProps { - children: React.ReactNode; + params: React.ReactNode; + buttons: React.ReactNode; } /** @@ -25,24 +26,33 @@ export interface ToolbarGroupProps { * @param {React.ReactNode} children - The child elements to be displayed inside the list. * @returns {JSX.Element} The rendered List component. */ -export const ToolbarGroup: React.FC = ({ children }) => { +export const ToolbarGroup: React.FC = ({ params, buttons }) => { const Theme = useTheme(); return ( - - {children} - + {params} + + {buttons} + + ); }; diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarView.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarView.tsx index 835a492..6768e78 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarView.tsx +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarView.tsx @@ -1,7 +1,5 @@ import { ToolbarGroup, - ToolbarGroupItem, - ToolbarGroupItemProps, ToolbarGroupsSelector, ToolbarGroupsSelectorItem, ToolbarGroupsSelectorItemProps, @@ -11,7 +9,13 @@ import { DownloadGroupButtons, MergeGroupButtons, } from '@/features/editor/components/toolbarView/toolbarGroupButtons'; -import { useMemo, useState } from 'react'; +import { + ApplyGroupParams, + DownloadGroupParams, + MergeGroupParams, +} from '@/features/editor/components/toolbarView/toolbarGroupParams'; +import { ToolbarContextProvider } from '@/features/editor/stores'; +import React, { useMemo, useState } from 'react'; /** * ToolbarView component manages and displays a set of toolbar groups and items. @@ -54,26 +58,34 @@ export const ToolbarView: React.FC = () => { }, ]; + // Combine the params groups into a dictionary for easy access + const ToolbarGroupsParams: Record = useMemo( + () => ({ + download: , + merge: , + apply: , + }), + [] + ); + // Combine the button groups into a dictionary for easy access - const ToolbarGroupsButtons: Record = useMemo( + const ToolbarGroupsButtons: Record = useMemo( () => ({ - download: DownloadGroupButtons, - merge: MergeGroupButtons, - apply: ApplyGroupButtons, + download: , + merge: , + apply: , }), [DownloadGroupButtons, MergeGroupButtons, ApplyGroupButtons] ); return ( - <> + {ToolbarGroups.map((group, index) => ( ))} - - {ToolbarGroupsButtons[selectedGroup]?.map((button, index) => )} - - + + ); }; From 33d500cdb4174a747520e46e2ac82234477cd7c4 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Sun, 8 Sep 2024 00:54:43 +0300 Subject: [PATCH 09/14] MDE/PKFE-31 update button icon color on disabled --- .../features/editor/components/toolbarView/toolbarGroupItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupItem.tsx b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupItem.tsx index 8a43dd8..1a24c20 100644 --- a/app/front-end/src/features/editor/components/toolbarView/toolbarGroupItem.tsx +++ b/app/front-end/src/features/editor/components/toolbarView/toolbarGroupItem.tsx @@ -42,7 +42,7 @@ export const ToolbarGroupItem: React.FC = ({ icon: Icon, return (