diff --git a/CHANGELOG.md b/CHANGELOG.md index 401998728..58efefbbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ changes. - Fix duplicate testIds for reference errors and hints in DRep metadata form [Issue 1965](https://github.com/IntersectMBO/govtool/issues/1965) - Eliminate duplicate DReps in the DRep Directory [Issue 2171](https://github.com/IntersectMBO/govtool/issues/2171) - Handle script based DReps [Issue 1951](https://github.com/IntersectMBO/govtool/issues/1951) +- Fix displaying protocol parameter cost models [Issue 2191](https://github.com/IntersectMBO/govtool/issues/2191) ### Changed diff --git a/govtool/backend/sql/get-current-epoch-params.sql b/govtool/backend/sql/get-current-epoch-params.sql index 9fd642903..2b591a0b4 100644 --- a/govtool/backend/sql/get-current-epoch-params.sql +++ b/govtool/backend/sql/get-current-epoch-params.sql @@ -1 +1,18 @@ -select ROW_TO_JSON(epoch_param) from epoch_param order by epoch_no desc limit 1; +SELECT + jsonb_set( + ROW_TO_JSON(epoch_param)::jsonb, + '{cost_model}', + CASE + WHEN cost_model.id IS NOT NULL THEN + ROW_TO_JSON(cost_model)::jsonb + ELSE + 'null'::jsonb + END + ) AS epoch_param +FROM + epoch_param +LEFT JOIN + cost_model ON epoch_param.cost_model_id = cost_model.id +ORDER BY + epoch_no DESC +LIMIT 1; diff --git a/govtool/backend/sql/list-proposals.sql b/govtool/backend/sql/list-proposals.sql index a0c333bcf..bbe19be2c 100644 --- a/govtool/backend/sql/list-proposals.sql +++ b/govtool/backend/sql/list-proposals.sql @@ -64,7 +64,16 @@ SELECT creator_block.epoch_no, voting_anchor.url, encode(voting_anchor.data_hash, 'hex'), - ROW_TO_JSON(proposal_params), + jsonb_set( + ROW_TO_JSON(proposal_params)::jsonb, + '{cost_model}', + CASE + WHEN cost_model.id IS NOT NULL THEN + ROW_TO_JSON(cost_model)::jsonb + ELSE + 'null'::jsonb + END + ) AS proposal_params, off_chain_vote_gov_action_data.title, off_chain_vote_gov_action_data.abstract, off_chain_vote_gov_action_data.motivation, @@ -104,6 +113,7 @@ FROM JOIN block AS creator_block ON creator_block.id = creator_tx.block_id LEFT JOIN voting_anchor ON voting_anchor.id = gov_action_proposal.voting_anchor_id LEFT JOIN param_proposal as proposal_params ON gov_action_proposal.param_proposal = proposal_params.id + LEFT JOIN cost_model AS cost_model ON proposal_params.cost_model_id = cost_model.id LEFT JOIN off_chain_vote_data ON off_chain_vote_data.voting_anchor_id = voting_anchor.id LEFT JOIN off_chain_vote_gov_action_data ON off_chain_vote_gov_action_data.off_chain_vote_data_id = off_chain_vote_data.id LEFT JOIN voting_procedure ON voting_procedure.gov_action_proposal_id = gov_action_proposal.id diff --git a/govtool/frontend/src/components/atoms/VotePill.tsx b/govtool/frontend/src/components/atoms/VotePill.tsx index 53ad58ece..cd37c9e02 100644 --- a/govtool/frontend/src/components/atoms/VotePill.tsx +++ b/govtool/frontend/src/components/atoms/VotePill.tsx @@ -32,13 +32,15 @@ export const VotePill = ({ minWidth="50px" maxWidth={maxWidth ? `${maxWidth}px` : "auto"} width={width ? `${width}px` : "auto"} - maxHeight="14px" > {t( `votes.${ diff --git a/govtool/frontend/src/components/molecules/DataMissingHeader.tsx b/govtool/frontend/src/components/molecules/DataMissingHeader.tsx index 7e19844e9..a07f7277d 100644 --- a/govtool/frontend/src/components/molecules/DataMissingHeader.tsx +++ b/govtool/frontend/src/components/molecules/DataMissingHeader.tsx @@ -36,9 +36,7 @@ export const DataMissingHeader = ({ > - filterUpdatableProtocolParams(epochParams, protocolParams, [ - "id", - "registered_tx_id", - "key", + mapArrayToObjectByKeys(protocolParams, [ + "PlutusV1", + "PlutusV2", + "PlutusV3", ]), - [epochParams, protocolParams], + [protocolParams], + ); + + const updatableProtocolParams = useMemo( + () => + filterUpdatableProtocolParams( + epochParams, + mappedArraysToObjectsProtocolParams, + ["id", "registered_tx_id", "key"], + ), + [epochParams, mappedArraysToObjectsProtocolParams], ); const nonNullProtocolParams = useMemo( () => - filterOutNullParams(protocolParams, ["id", "registered_tx_id", "key"]), - [updatableProtocolParams, protocolParams], + filterOutNullParams(mappedArraysToObjectsProtocolParams, [ + "id", + "registered_tx_id", + "key", + ]), + [updatableProtocolParams, mappedArraysToObjectsProtocolParams], ); const isModifiedPadding = diff --git a/govtool/frontend/src/main.tsx b/govtool/frontend/src/main.tsx index 76f6ff419..60187d40e 100644 --- a/govtool/frontend/src/main.tsx +++ b/govtool/frontend/src/main.tsx @@ -5,6 +5,7 @@ import { QueryClient, QueryClientProvider } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; import TagManager from "react-gtm-module"; import { ThemeProvider } from "@emotion/react"; +import { CssBaseline } from "@mui/material"; import * as Sentry from "@sentry/react"; import { ContextProviders, UsersnapProvider } from "@context"; @@ -51,6 +52,7 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + diff --git a/govtool/frontend/src/theme.ts b/govtool/frontend/src/theme.ts index 910447fab..2d0831b23 100644 --- a/govtool/frontend/src/theme.ts +++ b/govtool/frontend/src/theme.ts @@ -1,6 +1,11 @@ import { createTheme } from "@mui/material/styles"; import { - cyan, errorRed, orange, primaryBlue, progressYellow, successGreen, + cyan, + errorRed, + orange, + primaryBlue, + progressYellow, + successGreen, } from "./consts"; export type Theme = typeof theme; @@ -17,12 +22,19 @@ export const theme = createTheme({ }, }, components: { + MuiCssBaseline: { + styleOverrides: { + ":root": { + fonfFamily: "Poppins, Arial", + }, + }, + }, MuiAccordion: { styleOverrides: { root: { borderRadius: `12px !important`, - } - } + }, + }, }, MuiInputBase: { styleOverrides: { @@ -52,7 +64,7 @@ export const theme = createTheme({ { props: { color: "default", variant: "filled" }, style: { - backgroundColor: primaryBlue.c50 + backgroundColor: primaryBlue.c50, }, }, { @@ -110,7 +122,7 @@ export const theme = createTheme({ MuiPopover: { defaultProps: { elevation: 2, - } + }, }, }, typography: { diff --git a/govtool/frontend/src/utils/filterOutNullParams.ts b/govtool/frontend/src/utils/filterOutNullParams.ts index dd7b14af8..6e15ee239 100644 --- a/govtool/frontend/src/utils/filterOutNullParams.ts +++ b/govtool/frontend/src/utils/filterOutNullParams.ts @@ -8,7 +8,7 @@ export const filterOutNullParams = ( originalObject?: Record | undefined | null, filterOutKeys?: string[], -) => { +): Record | null => { if (!originalObject) { return null; } @@ -20,12 +20,27 @@ export const filterOutNullParams = ( value !== undefined && !filterOutKeys?.includes(key) ) { - acc[key] = value; + if ( + typeof value === "object" && + !Array.isArray(value) && + value !== null + ) { + // Recursively filter the nested object + const nestedFiltered = filterOutNullParams( + value as Record, + filterOutKeys, + ); + if (nestedFiltered && Object.keys(nestedFiltered).length > 0) { + acc[key] = nestedFiltered; + } + } else { + acc[key] = value; + } } return acc; }, {}, ); - return finalObject; + return Object.keys(finalObject).length > 0 ? finalObject : null; }; diff --git a/govtool/frontend/src/utils/filterUpdatableProtocolParams.ts b/govtool/frontend/src/utils/filterUpdatableProtocolParams.ts index 92377db29..ab5f7c5fd 100644 --- a/govtool/frontend/src/utils/filterUpdatableProtocolParams.ts +++ b/govtool/frontend/src/utils/filterUpdatableProtocolParams.ts @@ -19,16 +19,36 @@ export const filterUpdatableProtocolParams = ( const finalObject = Object.entries(referenceObject).reduce< Record >((acc, [key, referenceValue]) => { + const originalValue = originalObject[key]; + const isValid = !filterOutKeys?.includes(key) && originalObject.hasOwnProperty(key) && referenceValue !== undefined && referenceValue !== null; - if (isValid) acc[key] = originalObject[key]; + if (isValid) { + if ( + typeof originalValue === "object" && + originalValue !== null && + typeof referenceValue === "object" && + referenceValue !== null + ) { + const nestedFiltered = filterUpdatableProtocolParams( + originalValue as Record, + referenceValue as Record, + filterOutKeys, + ); + if (nestedFiltered && Object.keys(nestedFiltered).length > 0) { + acc[key] = nestedFiltered; + } + } else { + acc[key] = originalValue; + } + } return acc; }, {}); - return finalObject; + return Object.keys(finalObject).length > 0 ? finalObject : null; }; diff --git a/govtool/frontend/src/utils/index.ts b/govtool/frontend/src/utils/index.ts index 3ee9719fc..aa8a2e049 100644 --- a/govtool/frontend/src/utils/index.ts +++ b/govtool/frontend/src/utils/index.ts @@ -22,6 +22,7 @@ export * from "./getProposalTypeLabel"; export * from "./isValidFormat"; export * from "./jsonUtils"; export * from "./localStorage"; +export * from "./mapArrayToObjectByKeys"; export * from "./mapDtoToDrep"; export * from "./mapDtoToProposal"; export * from "./numberValidation"; diff --git a/govtool/frontend/src/utils/mapArrayToObjectByKeys.ts b/govtool/frontend/src/utils/mapArrayToObjectByKeys.ts new file mode 100644 index 000000000..10c6099a3 --- /dev/null +++ b/govtool/frontend/src/utils/mapArrayToObjectByKeys.ts @@ -0,0 +1,40 @@ +/** + * Maps an object to a new object by including only the desired keys + * and converting arrays to objects. + * @param obj - The object to map. + * @param desiredKeys - An array of keys to include in the mapped object. + * @returns The mapped object. + */ +export const mapArrayToObjectByKeys = ( + obj?: Record | null, + desiredKeys?: string[], +): Record | null => { + if (!obj || !desiredKeys) { + return null; + } + + return Object.entries(obj).reduce((acc, [key, value]) => { + if (desiredKeys.includes(key) && Array.isArray(value)) { + const arrayToObject = value.reduce>( + (arrayAcc, arrayValue, index) => { + arrayAcc[index] = arrayValue; + return arrayAcc; + }, + {}, + ); + acc[key] = arrayToObject; + } else if ( + typeof value === "object" && + value !== null && + !Array.isArray(value) + ) { + acc[key] = mapArrayToObjectByKeys( + value as Record, + desiredKeys, + ); + } else { + acc[key] = value; + } + return acc; + }, {} as Record); +};