From 3f0401fba3db0e403391808d039236f9fe234423 Mon Sep 17 00:00:00 2001 From: Mantvydas Deltuva Date: Tue, 3 Sep 2024 17:27:07 +0300 Subject: [PATCH] MDE/PKFE-16 grid column aggregation implementation into editor with back-end support --- .../components/editorView/editorView.tsx | 183 ++++++++++++------ .../src/types/constants/endpoints.ts | 1 + 2 files changed, 129 insertions(+), 55 deletions(-) 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 aa2d240..12616e9 100644 --- a/app/front-end/src/features/editor/components/editorView/editorView.tsx +++ b/app/front-end/src/features/editor/components/editorView/editorView.tsx @@ -1,11 +1,16 @@ -import { EditorColumnMenu, EditorToolbar } from '@/features/editor/components/editorView'; +import { EditorColumnMenu, EditorHeader, EditorToolbar } from '@/features/editor/components/editorView'; import { useWorkspaceContext } from '@/features/editor/hooks'; -import { FileDataRequestDTO, FileDataResponseDTO } from '@/features/editor/types'; +import { + ColumnAggregation, + EditorColumnMenuAggregationActions, + FileDataRequestDTO, + FileDataResponseDTO, +} from '@/features/editor/types'; import { useSessionContext } from '@/hooks'; import { axios } from '@/lib'; import { Endpoints } from '@/types'; import { DataGrid, GridColDef, GridRowsProp, useGridApiRef } from '@mui/x-data-grid'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; /** * EditorView component renders a DataGrid with dynamic columns and rows fetched from a workspace file. @@ -37,70 +42,87 @@ export const EditorView: React.FC = () => { const Workspace = useWorkspaceContext(); const [gridColumns, setgridColumns] = useState([]); + const [gridColumnsAggregation, setGridColumnsAggregation] = useState({}); const [gridRows, setgridRows] = useState([]); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(100); const [totalRows, setTotalRows] = useState(0); const [isLoading, setIsLoading] = useState(false); + const [response, setResponse] = useState({ totalRows: 0, header: [], rows: [], page: 0 }); + // Fetch workspace file data + const getWorkspaceFile = useCallback(async () => { + if (!Workspace.fileId) { + setgridColumns([]); + setgridRows([]); + return; + } + + setIsLoading(true); + + try { + const response = await axios.get(`${Endpoints.WORKSPACE_FILE}/${Workspace.fileId}`, { + params: { + page: page, + rowsPerPage: rowsPerPage, + }, + }); + + setResponse(response.data as FileDataResponseDTO); + } catch (error) { + console.error('Failed to fetch file content:', error); + } finally { + setIsLoading(false); + } + }, [Workspace.fileId, page, rowsPerPage]); + + // Data reset effect useEffect(() => { - const getWorkspaceFile = async () => { - if (!Workspace.fileId) { - setgridColumns([]); - setgridRows([]); - return; - } - - setIsLoading(true); - - try { - const response = await axios.get(`${Endpoints.WORKSPACE_FILE}/${Workspace.fileId}`, { - params: { - page: page, - rowsPerPage: rowsPerPage, - }, - }); + setGridColumnsAggregation({}); + }, [Workspace.fileId]); - const { totalRows, header, rows } = response.data as FileDataResponseDTO; + // Data fetching effect + useEffect(() => { + if (connected) getWorkspaceFile(); + }, [connected, getWorkspaceFile]); - if (!header) { - setgridColumns([]); - setgridRows([]); - return; - } + // Parse response effect + useEffect(() => { + const { totalRows, header, rows } = response; - const parsedColumns = header.map((value) => { - return { - field: value, - headerName: value, - width: 150, - editable: true, - }; - }); + if (!header) { + setgridColumns([]); + setgridRows([]); + setGridColumnsAggregation({}); + return; + } - const parsedRows = rows.map((row, index) => { - return { - id: index, - ...row.reduce((acc, value, index) => { - return { ...acc, [header[index]]: value }; - }, {}), - }; - }); + const parsedColumns = header.map((value) => { + return { + field: value, + headerName: value, + flex: 1, + minWidth: 150, + editable: true, + renderHeader: () => , + }; + }); - setgridColumns(parsedColumns || []); - setgridRows(parsedRows || []); - setTotalRows(totalRows); - } catch (error) { - console.error('Failed to fetch file content:', error); - } finally { - setIsLoading(false); - } - }; + const parsedRows = rows.map((row, index) => { + return { + id: index, + ...row.reduce((acc, value, index) => { + return { ...acc, [header[index]]: value }; + }, {}), + }; + }); - if (connected) getWorkspaceFile(); - }, [connected, Workspace.fileId, Workspace.fileType, page, rowsPerPage]); + setgridColumns(parsedColumns); + setgridRows(parsedRows); + setTotalRows(totalRows); + }, [response, gridColumnsAggregation]); - const handleSave = () => { + const handleSave = async () => { const data: FileDataRequestDTO = { page: page, rowsPerPage: rowsPerPage, @@ -112,7 +134,49 @@ export const EditorView: React.FC = () => { ), }; - axios.put(`${Endpoints.WORKSPACE}/${Workspace.fileId}`, data); + const responseSave = await axios.put(`${Endpoints.WORKSPACE_FILE}/${Workspace.fileId}`, data); + setResponse(responseSave.data as FileDataResponseDTO); + + const responseAggregate = await axios.get(`${Endpoints.WORKSPACE_AGGREGATE}/all/${Workspace.fileId}`, { + params: { + columnsAggregation: JSON.stringify(gridColumnsAggregation), + }, + }); + + const { columnsAggregation: responseColumnsAggregation } = responseAggregate.data; + setGridColumnsAggregation(responseColumnsAggregation); + }; + + const handleAggregation = async (field: string, action: EditorColumnMenuAggregationActions) => { + switch (action) { + case '': + setGridColumnsAggregation((prevAggregations) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { [field]: _, ...rest } = prevAggregations; + return rest; + }); + break; + default: + { + const response = await axios.get(`${Endpoints.WORKSPACE_AGGREGATE}/${Workspace.fileId}`, { + params: { + field: field, + action: action, + }, + }); + + const { field: responseField, action: responseAction, value: responseValue } = response.data; + + setGridColumnsAggregation((prevAggregations) => ({ + ...prevAggregations, + [responseField]: { + action: responseAction, + value: responseValue, + }, + })); + } + break; + } }; return ( @@ -137,7 +201,16 @@ export const EditorView: React.FC = () => { }} slots={{ toolbar: (props) => , - columnMenu: (props) => , + columnMenu: (props) => ( + + ), + }} + slotProps={{ + toolbar: {}, }} apiRef={gridApiRef} /> diff --git a/app/front-end/src/types/constants/endpoints.ts b/app/front-end/src/types/constants/endpoints.ts index e95c382..6805ff7 100644 --- a/app/front-end/src/types/constants/endpoints.ts +++ b/app/front-end/src/types/constants/endpoints.ts @@ -37,4 +37,5 @@ export const Endpoints = { WORKSPACE_CREATE: `/workspace/create`, WORKSPACE_RENAME: `/workspace/rename`, WORKSPACE_DELETE: `/workspace/delete`, + WORKSPACE_AGGREGATE: `/workspace/aggregate`, };