diff --git a/frontend-v2/src/app/backendApi.ts b/frontend-v2/src/app/backendApi.ts index efa95f7f..fb4156e8 100644 --- a/frontend-v2/src/app/backendApi.ts +++ b/frontend-v2/src/app/backendApi.ts @@ -767,6 +767,57 @@ const injectedRtkApi = api.injectEndpoints({ method: "DELETE", }), }), + resultsList: build.query({ + query: (queryArg) => ({ + url: `/api/results/`, + params: { project_id: queryArg.projectId }, + }), + }), + resultsCreate: build.mutation< + ResultsCreateApiResponse, + ResultsCreateApiArg + >({ + query: (queryArg) => ({ + url: `/api/results/`, + method: "POST", + body: queryArg.results, + }), + }), + resultsRetrieve: build.query< + ResultsRetrieveApiResponse, + ResultsRetrieveApiArg + >({ + query: (queryArg) => ({ url: `/api/results/${queryArg.id}/` }), + }), + resultsUpdate: build.mutation< + ResultsUpdateApiResponse, + ResultsUpdateApiArg + >({ + query: (queryArg) => ({ + url: `/api/results/${queryArg.id}/`, + method: "PUT", + body: queryArg.results, + }), + }), + resultsPartialUpdate: build.mutation< + ResultsPartialUpdateApiResponse, + ResultsPartialUpdateApiArg + >({ + query: (queryArg) => ({ + url: `/api/results/${queryArg.id}/`, + method: "PATCH", + body: queryArg.patchedResults, + }), + }), + resultsDestroy: build.mutation< + ResultsDestroyApiResponse, + ResultsDestroyApiArg + >({ + query: (queryArg) => ({ + url: `/api/results/${queryArg.id}/`, + method: "DELETE", + }), + }), sessionRetrieve: build.query< SessionRetrieveApiResponse, SessionRetrieveApiArg @@ -1569,6 +1620,37 @@ export type ProtocolDestroyApiArg = { /** A unique integer value identifying this protocol. */ id: number; }; +export type ResultsListApiResponse = /** status 200 */ ResultsRead[]; +export type ResultsListApiArg = { + /** Filter results by project ID */ + projectId?: number; +}; +export type ResultsCreateApiResponse = /** status 201 */ ResultsRead; +export type ResultsCreateApiArg = { + results: Results; +}; +export type ResultsRetrieveApiResponse = /** status 200 */ ResultsRead; +export type ResultsRetrieveApiArg = { + /** A unique integer value identifying this results. */ + id: number; +}; +export type ResultsUpdateApiResponse = /** status 200 */ ResultsRead; +export type ResultsUpdateApiArg = { + /** A unique integer value identifying this results. */ + id: number; + results: Results; +}; +export type ResultsPartialUpdateApiResponse = /** status 200 */ ResultsRead; +export type ResultsPartialUpdateApiArg = { + /** A unique integer value identifying this results. */ + id: number; + patchedResults: PatchedResults; +}; +export type ResultsDestroyApiResponse = unknown; +export type ResultsDestroyApiArg = { + /** A unique integer value identifying this results. */ + id: number; +}; export type SessionRetrieveApiResponse = unknown; export type SessionRetrieveApiArg = void; export type SimulationListApiResponse = /** status 200 */ SimulationRead[]; @@ -3087,6 +3169,90 @@ export type PatchedProtocolRead = { /** Group that uses this protocol */ group?: number | null; }; +export type RowsEnum = "parameters" | "variables" | "groups" | "intervals"; +export type ColumnsEnum = "parameters" | "variables" | "groups" | "intervals"; +export type Results = { + /** name of the table */ + name: string; + /** parameter to display as table rows + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + rows: RowsEnum; + /** parameter to display as table columns + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + columns: ColumnsEnum; + /** Project that this group belongs to. */ + project?: number | null; +}; +export type ResultsRead = { + id: number; + /** name of the table */ + name: string; + /** parameter to display as table rows + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + rows: RowsEnum; + /** parameter to display as table columns + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + columns: ColumnsEnum; + /** Project that this group belongs to. */ + project?: number | null; +}; +export type PatchedResults = { + /** name of the table */ + name?: string; + /** parameter to display as table rows + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + rows?: RowsEnum; + /** parameter to display as table columns + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + columns?: ColumnsEnum; + /** Project that this group belongs to. */ + project?: number | null; +}; +export type PatchedResultsRead = { + id?: number; + /** name of the table */ + name?: string; + /** parameter to display as table rows + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + rows?: RowsEnum; + /** parameter to display as table columns + + * `parameters` - Secondary parameters of the model. + * `variables` - Model variables. + * `groups` - Subject groups. + * `intervals` - Time intervals. */ + columns?: ColumnsEnum; + /** Project that this group belongs to. */ + project?: number | null; +}; export type SimulationSlider = { variable: number; }; @@ -3794,6 +3960,12 @@ export const { useProtocolUpdateMutation, useProtocolPartialUpdateMutation, useProtocolDestroyMutation, + useResultsListQuery, + useResultsCreateMutation, + useResultsRetrieveQuery, + useResultsUpdateMutation, + useResultsPartialUpdateMutation, + useResultsDestroyMutation, useSessionRetrieveQuery, useSimulationListQuery, useSimulationCreateMutation, diff --git a/frontend-v2/src/features/results/Results.tsx b/frontend-v2/src/features/results/Results.tsx index 13b727c6..50382570 100644 --- a/frontend-v2/src/features/results/Results.tsx +++ b/frontend-v2/src/features/results/Results.tsx @@ -4,38 +4,46 @@ import { SyntheticEvent, FC, useState } from "react"; import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; import ResultsTab from "./ResultsTab"; import { TableHeader } from "../../components/TableHeader"; - -type Table = { - name: string; - id: number; -}; +import { useResults } from "./useResults"; +import { ResultsRead } from "../../app/backendApi"; +import { useSelector } from "react-redux"; +import { RootState } from "../../app/store"; const Results: FC = () => { + const projectId = useSelector( + (state: RootState) => state.main.selectedProject, + ); const [tab, setTab] = useState(0); - const [tables, setTables] = useState([{ name: "Table 1", id: 0 }]); + const { results, createResults, deleteResults } = useResults(); const getHighestTableId = (): number => - tables + results ?.map(({ id }) => id) .sort((id1: number, id2: number) => (id1 > id2 ? 1 : 0)) .pop() || 0; const handleTabAdd = async () => { - const newTableId = getHighestTableId() + 1; const newTableName = `Table ${getHighestTableId() + 2}`; - const newTables = [...tables, { name: newTableName, id: newTableId }]; - setTables(newTables); - setTab(newTables?.length - 1); + const lastTable = results[results.length - 1]; + const newTable = { + ...lastTable, + name: newTableName, + project: projectId, + }; + newTable.columns ||= "parameters"; + newTable.rows ||= "variables"; + createResults({ results: newTable }); + setTab(results.length); }; const handleTabChange = async (event: SyntheticEvent, newValue: number) => { setTab(newValue); }; - const handleTabRemove = async (table: Table) => { + const handleTabRemove = async (table: ResultsRead) => { if (window.confirm("Are you sure you want to delete the current Table?")) { - const removedIndex = tables.map(({ id }) => id).indexOf(table.id); - setTables(tables.filter(({ id }) => id !== table.id)); + const removedIndex = results?.map(({ id }) => id).indexOf(table.id) || -1; + deleteResults({ id: table.id }); if (removedIndex === tab) { setTab(removedIndex - 1); @@ -71,7 +79,7 @@ const Results: FC = () => { allowScrollButtonsMobile sx={{ width: "fit-content" }} > - {tables?.map((table, index) => { + {results?.map((table, index) => { return ( { - {tables.map((table, index) => { + {results.map((table, index) => { return ( { hidden={tab !== index} sx={{ p: 3 }} > - + ); })} diff --git a/frontend-v2/src/features/results/ResultsTab.tsx b/frontend-v2/src/features/results/ResultsTab.tsx index a3e0b31f..475878c3 100644 --- a/frontend-v2/src/features/results/ResultsTab.tsx +++ b/frontend-v2/src/features/results/ResultsTab.tsx @@ -15,6 +15,7 @@ import { useConcentrationVariables } from "./useConcentrationVariables"; import { useParameters, Parameter } from "./useParameters"; import { ResultsTable } from "./ResultsTable"; import { useModelTimeIntervals } from "../../hooks/useModelTimeIntervals"; +import { Table } from "./Results"; const options = [ { name: "Parameters", value: "parameters" }, @@ -42,14 +43,14 @@ type RowFilter = { label: string; }; -const ResultsTab: FC = () => { +const ResultsTab: FC<{ table: Table }> = ({ table }) => { const { groups = [] } = useSubjectGroups(); const [intervals] = useModelTimeIntervals(); const concentrationVariables = useConcentrationVariables(); const parameters = useParameters(); - const [columns, setColumns] = useState("parameters"); - const [rows, setRows] = useState("variables"); + const [columns, setColumns] = useState(table.columns); + const [rows, setRows] = useState(table.rows); const [group, setGroup] = useState(0); const [variable, setVariable] = useState(0); const [interval, setInterval] = useState(0); @@ -93,8 +94,14 @@ const ResultsTab: FC = () => { ? "Interval" : "Group"; + function handleColumnsChange(event: SelectChangeEvent) { + setColumns(event.target.value); + table.columns = event.target.value; + } + function handleRowsChange(event: SelectChangeEvent) { setRows(event.target.value); + table.rows = event.target.value; } function handleGroupChange(event: SelectChangeEvent) { const newValue = parseInt(event.target.value); @@ -208,7 +215,7 @@ const ResultsTab: FC = () => { Columns