Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions govtool/backend/sql/list-dreps.sql
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ LatestVoteEpoch AS (
JOIN tx ON tx.id = lvp.tx_id
JOIN block ON block.id = tx.block_id
),
VotesLastYear AS (
SELECT
vp.drep_voter AS drep_id,
COUNT(DISTINCT vp.gov_action_proposal_id) AS votes_last_year
FROM voting_procedure vp
JOIN tx ON tx.id = vp.tx_id
JOIN block ON block.id = tx.block_id
WHERE block.time >= now() - INTERVAL '1 year'
GROUP BY vp.drep_voter
),
RankedDRepRegistration AS (
SELECT DISTINCT ON (dr.drep_hash_id)
dr.id,
Expand Down Expand Up @@ -127,6 +137,7 @@ DRepData AS (
off_chain_vote_drep_data.qualifications,
off_chain_vote_drep_data.image_url,
off_chain_vote_drep_data.image_hash,
COALESCE(vly.votes_last_year, 0) AS votes_last_year,
COALESCE(
(
SELECT jsonb_agg(
Expand Down Expand Up @@ -239,6 +250,7 @@ DRepData AS (
LEFT JOIN tx AS tx_first_register ON tx_first_register.id = dr_first_register.tx_id
LEFT JOIN block AS block_first_register ON block_first_register.id = tx_first_register.block_id
LEFT JOIN LatestVoteEpoch lve ON lve.drep_id = dh.id
LEFT JOIN VotesLastYear vly ON vly.drep_id = dh.id
CROSS JOIN DRepActivity
GROUP BY
dh.raw,
Expand All @@ -265,6 +277,7 @@ DRepData AS (
off_chain_vote_drep_data.qualifications,
off_chain_vote_drep_data.image_url,
off_chain_vote_drep_data.image_hash,
vly.votes_last_year,
(
SELECT jsonb_agg(
jsonb_build_object(
Expand Down
3 changes: 3 additions & 0 deletions govtool/backend/src/VVA/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ drepRegistrationToDrep Types.DRepRegistration {..} =
dRepQualifications = dRepRegistrationQualifications,
dRepImageUrl = dRepRegistrationImageUrl,
dRepImageHash = HexText <$> dRepRegistrationImageHash,
dRepVotesLastYear = dRepRegistrationVotesLastYear,
dRepIdentityReferences = DRepReferences <$> dRepRegistrationIdentityReferences,
dRepLinkReferences = DRepReferences <$> dRepRegistrationLinkReferences
}
Expand Down Expand Up @@ -205,6 +206,8 @@ drepList mSearchQuery statuses mSortMode mPage mPageSize = do
Just Random -> fmap snd . sortOn fst . Prelude.zip randomizedOrderList
Just VotingPower -> sortOn $ \Types.DRepRegistration {..} ->
Down dRepRegistrationVotingPower
Just Activity -> sortOn $ \Types.DRepRegistration {..} ->
Down dRepRegistrationVotesLastYear
Just RegistrationDate -> sortOn $ \Types.DRepRegistration {..} ->
Down dRepRegistrationLatestRegistrationDate
Just Status -> sortOn $ \Types.DRepRegistration {..} ->
Expand Down
4 changes: 3 additions & 1 deletion govtool/backend/src/VVA/API/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ instance ToParamSchema GovernanceActionType where
& enum_ ?~ map toJSON (enumFromTo minBound maxBound :: [GovernanceActionType])


data DRepSortMode = Random | VotingPower | RegistrationDate | Status deriving
data DRepSortMode = Random | VotingPower | Activity | RegistrationDate | Status deriving
( Bounded
, Enum
, Eq
Expand Down Expand Up @@ -917,6 +917,7 @@ data DRep
, dRepQualifications :: Maybe Text
, dRepImageUrl :: Maybe Text
, dRepImageHash :: Maybe HexText
, dRepVotesLastYear :: Maybe Integer
, dRepIdentityReferences :: Maybe DRepReferences
, dRepLinkReferences :: Maybe DRepReferences
}
Expand Down Expand Up @@ -944,6 +945,7 @@ exampleDrep =
<> "\"qualifications\": \"Some Qualifications\","
<> "\"qualifications\": \"Some Qualifications\","
<> "\"imageUrl\": \"https://image.url\","
<> "\"votesLastYear\": 15,"
<> "\"imageHash\": \"9198b1b204273ba5c67a13310b5a806034160f6a063768297e161d9b759cad61\"}"

-- ToSchema instance for DRep
Expand Down
4 changes: 3 additions & 1 deletion govtool/backend/src/VVA/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ data DRepQueryResult
, queryQualifications :: Maybe Text
, queryImageUrl :: Maybe Text
, queryImageHash :: Maybe Text
, queryVotesLastYear :: Maybe Integer
, queryIdentityReferences :: Maybe Value
, queryLinkReferences :: Maybe Value
}
Expand All @@ -69,7 +70,7 @@ instance FromRow DRepQueryResult where
<$> field <*> field <*> field <*> field <*> field <*> field
<*> field <*> field <*> field <*> field <*> field <*> field
<*> field <*> field <*> field <*> field <*> field <*> field
<*> field <*> field <*> field <*> field
<*> field <*> field <*> field <*> field <*> field

sqlFrom :: ByteString -> SQL.Query
sqlFrom bs = fromString $ unpack $ Text.decodeUtf8 bs
Expand Down Expand Up @@ -113,6 +114,7 @@ listDReps mSearchQuery = withPool $ \conn -> do
(queryQualifications result)
(queryImageUrl result)
(queryImageHash result)
(queryVotesLastYear result)
(queryIdentityReferences result)
(queryLinkReferences result)
| result <- results
Expand Down
2 changes: 2 additions & 0 deletions govtool/backend/src/VVA/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ data DRepRegistration
, dRepRegistrationQualifications :: Maybe Text
, dRepRegistrationImageUrl :: Maybe Text
, dRepRegistrationImageHash :: Maybe Text
, dRepRegistrationVotesLastYear :: Maybe Integer
, dRepRegistrationIdentityReferences :: Maybe Value
, dRepRegistrationLinkReferences :: Maybe Value
}
Expand Down Expand Up @@ -187,6 +188,7 @@ instance FromRow DRepRegistration where
<*> field -- dRepRegistrationQualifications
<*> field -- dRepRegistrationImageUrl
<*> field -- dRepRegistrationImageHash
<*> field -- dRepRegistrationVotesLastYear
<*> field -- dRepRegistrationIdentityReferences
<*> field -- dRepRegistrationLinkReferences

Expand Down
137 changes: 137 additions & 0 deletions govtool/frontend/src/components/molecules/PaginationFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import {
Box,
Typography,
IconButton,
Select,
MenuItem,
SelectChangeEvent,
} from "@mui/material";
import KeyboardArrowLeft from "@mui/icons-material/KeyboardArrowLeft";
import KeyboardArrowRight from "@mui/icons-material/KeyboardArrowRight";
import { FC } from "react";

type Props = {
page: number;
total: number;
pageSize: number;
onPageChange: (nextPage: number) => void;
onPageSizeChange: (nextRpp: number) => void;
pageSizeOptions?: number[];
};

export const PaginationFooter: FC<Props> = ({
page,
total,
pageSize,
onPageChange,
onPageSizeChange,
pageSizeOptions = [5, 10, 25, 50],
}) => {
const pageCount = Math.max(1, Math.ceil((total || 0) / (pageSize || 1)));
const clampedPage = Math.min(Math.max(page, 1), pageCount);

const start = total === 0 ? 0 : (clampedPage - 1) * pageSize + 1;
const end = total === 0 ? 0 : Math.min(clampedPage * pageSize, total);

const handlePrev = () => onPageChange(Math.max(clampedPage - 1, 1));
const handleNext = () => onPageChange(Math.min(clampedPage + 1, pageCount));

const handlePageSizeChange = (e: SelectChangeEvent<number>) => {
const next = Number(e.target.value);
onPageSizeChange(next);

const nextPageCount = Math.max(1, Math.ceil((total || 0) / next));
if (clampedPage > nextPageCount) {
onPageChange(nextPageCount);
}
};

return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: 2,
px: 2,
py: 1,
color: "#3B485C",
}}
>
<Box sx={{ display: "inline-flex", alignItems: "baseline", gap: 1.25 }}>
<Typography variant="body2" sx={{ fontWeight: 500, color: "#3B485C" }}>
Rows&nbsp;per&nbsp;page:
</Typography>

<Select
value={pageSize}
onChange={handlePageSizeChange}
variant="standard"
disableUnderline
sx={{
verticalAlign: "baseline",
"& .MuiSelect-select": {
py: 0,
lineHeight: 1.5,
display: "inline-flex",
alignItems: "center",
},
"& .MuiSelect-icon": {
top: "calc(50% - 12px)",
},
}}
renderValue={(val) => (
<Box
component="span"
sx={{ fontWeight: 500, color: "#3B485C", fontSize: 15 }}
>
{val as number}
</Box>
)}
MenuProps={{
PaperProps: { sx: { mt: 1, borderRadius: 1.5 } },
MenuListProps: { dense: true },
}}
>
{pageSizeOptions.map((n) => (
<MenuItem key={n} value={n}>
{n}
</MenuItem>
))}
</Select>
</Box>

<Typography variant="body2" sx={{ minWidth: 110, textAlign: "center" }}>
{start}-{end} of {total}
</Typography>

<Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
<IconButton
size="small"
onClick={handlePrev}
disabled={clampedPage <= 1 || total === 0}
aria-label="Previous page"
>
<KeyboardArrowLeft />
</IconButton>

<Typography
variant="body2"
sx={{ width: 24, textAlign: "center" }}
aria-label="Current page"
>
{clampedPage}
</Typography>

<IconButton
size="small"
onClick={handleNext}
disabled={clampedPage >= pageCount || total === 0}
aria-label="Next page"
>
<KeyboardArrowRight />
</IconButton>
</Box>
</Box>
);
};
4 changes: 2 additions & 2 deletions govtool/frontend/src/consts/dRepDirectory/sorting.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const DREP_DIRECTORY_SORTING = [
{
key: "Random",
label: "Random",
key: "Activity",
label: "Activity",
},
{
key: "RegistrationDate",
Expand Down
13 changes: 8 additions & 5 deletions govtool/frontend/src/context/contextProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CardanoProvider, useCardano } from "./wallet";
import { ModalProvider, useModal } from "./modal";
import { SnackbarProvider, useSnackbar } from "./snackbar";
import { DataActionsBarProvider } from "./dataActionsBar";
import { PaginationProvider } from "./pagination";
import { FeatureFlagProvider } from "./featureFlag";
import { GovernanceActionProvider } from "./governanceAction";
import { AdaHandleProvider } from "./adaHandle";
Expand All @@ -23,11 +24,13 @@ const ContextProviders = ({ children }: Props) => (
<ModalProvider>
<SnackbarProvider>
<DataActionsBarProvider>
<CardanoProvider>
<MaintenanceEndingBannerProvider>
{children}
</MaintenanceEndingBannerProvider>
</CardanoProvider>
<PaginationProvider>
<CardanoProvider>
<MaintenanceEndingBannerProvider>
{children}
</MaintenanceEndingBannerProvider>
</CardanoProvider>
</PaginationProvider>
</DataActionsBarProvider>
</SnackbarProvider>
</ModalProvider>
Expand Down
2 changes: 2 additions & 0 deletions govtool/frontend/src/context/dataActionsBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface DataActionsBarContextType {
debouncedSearchText: string;
filtersOpen: boolean;
searchText: string;
lastPath: string;
setChosenFilters: Dispatch<SetStateAction<string[]>>;
setChosenSorting: Dispatch<SetStateAction<string>>;
setFiltersOpen: Dispatch<SetStateAction<boolean>>;
Expand Down Expand Up @@ -120,6 +121,7 @@ const DataActionsBarProvider: FC<ProviderProps> = ({ children }) => {
debouncedSearchText,
filtersOpen,
searchText,
lastPath,
setChosenFilters,
setChosenSorting,
setFiltersOpen,
Expand Down
1 change: 1 addition & 0 deletions govtool/frontend/src/context/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./appContext";
export * from "./contextProviders";
export * from "./dataActionsBar";
export * from "./pagination";
export * from "./modal";
export * from "./pendingTransaction";
export * from "./snackbar";
Expand Down
56 changes: 56 additions & 0 deletions govtool/frontend/src/context/pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, {
createContext,
useContext,
Dispatch,
SetStateAction,
FC,
useState,
useMemo,
} from "react";

type PaginationContextType = {
page: number;
pageSize: number;
setPage: Dispatch<SetStateAction<number>>;
setPageSize: Dispatch<SetStateAction<number>>;
};

const PaginationContext = createContext<
PaginationContextType | undefined>(
undefined);
PaginationContext.displayName = "PaginationContext";

type PaginationProviderProps = {
children: React.ReactNode;
};

const PaginationProvider: FC<PaginationProviderProps> = ({ children }) => {
const [page, setPage] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(5);

const contextValue = useMemo(
() => ({
page,
pageSize,
setPage,
setPageSize,
}),
[page, pageSize],
);

return (
<PaginationContext.Provider value={contextValue}>
{children}
</PaginationContext.Provider>
);
};

function usePagination() {
const ctx = useContext(PaginationContext);
if (!ctx) {
throw new Error("usePagination must be used within a PaginationProvider");
}
return ctx;
}

export { PaginationProvider, usePagination };
Loading
Loading