Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds proposal table, proposal status container, fixes theme toggle and dropdown issue #1525

Merged
merged 7 commits into from
Aug 11, 2023
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
4 changes: 2 additions & 2 deletions apps/stats-dapp/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const Header: FC<HeaderProps> = ({
</Typography>
</MenuItem>

<MenuItem className="px-4 py-2 hover:bg-mono-0 dark:hover:bg-mono-180">
<div className="px-4 py-2 hover:bg-mono-0 dark:hover:bg-mono-180">
<Typography variant="label" fw="bold">
Advanced
</Typography>
Expand All @@ -117,7 +117,7 @@ export const Header: FC<HeaderProps> = ({
setSelectedNetworkType={setSelectedNetworkType}
/>
</div>
</MenuItem>
</div>
</DropdownBody>
</Dropdown>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export const NetworkSelector: FC<NetworkSelectorProps> = ({
rightIcon={
savedEnpoints.customSubqueryEndpoint ? (
<DeleteBinWithBg
className="cursor-pointer"
onClick={() => {
setDefaultEndpointsAsUserSelected();
localStorage.removeItem('customSubqueryEndpoint');
Expand All @@ -181,6 +182,7 @@ export const NetworkSelector: FC<NetworkSelectorProps> = ({
/>
) : customSubqueryEndpoint ? (
<SaveWithBg
className="cursor-pointer"
onClick={async () => {
if (
await isValidSubqueryEndpoint(
Expand Down Expand Up @@ -224,6 +226,7 @@ export const NetworkSelector: FC<NetworkSelectorProps> = ({
rightIcon={
savedEnpoints.customPolkadotEndpoint ? (
<DeleteBinWithBg
className="cursor-pointer"
onClick={() => {
setDefaultEndpointsAsUserSelected();
localStorage.removeItem('customPolkadotEndpoint');
Expand All @@ -236,6 +239,7 @@ export const NetworkSelector: FC<NetworkSelectorProps> = ({
/>
) : customPolkadotEndpoint ? (
<SaveWithBg
className="cursor-pointer"
onClick={async () => {
if (
await isValidPolkadotEndpoint(
Expand Down
306 changes: 305 additions & 1 deletion apps/stats-dapp/src/containers/ProposalsTable/ProposalsTable.tsx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider relocating the utility functions and constants to a separate file to decrease the size of the current file. A suitable location could be apps/stats-dapp/src/containers/ProposalsTable/utils.ts.

Original file line number Diff line number Diff line change
@@ -1,3 +1,307 @@
import {
ColumnDef,
createColumnHelper,
getCoreRowModel,
getPaginationRowModel,
PaginationState,
Table as RTTable,
useReactTable,
} from '@tanstack/react-table';
import { ChainConfig, chainsConfig } from '@webb-tools/dapp-config';
import { getChipColorByProposalType, mapChainNameToLogo } from '../../utils';
import {
Accordion,
AccordionButton,
AccordionContent,
AccordionItem,
CardTable,
CheckBoxMenuGroup,
Chip,
Filter,
Table,
Divider,
ProposalsBadgeGroup,
} from '@webb-tools/webb-ui-components/components';
import { fuzzyFilter } from '@webb-tools/webb-ui-components/components/Filter/utils';
import { ChainIcon, Spinner } from '@webb-tools/icons';
import React, { useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import {
ProposalBatchStatus,
ProposalType,
ProposalBatchesOrderBy,
} from '../../generated/graphql';
import {
ProposalBatch,
BatchedProposalsQuery,
useBatchedProposals,
} from '../../provider/hooks';
import {
mapProposalStatusToChipColor,
PROPOSAL_STATUS,
PROPOSAL_TYPES,
} from './utils';

const columnHelper = createColumnHelper<ProposalBatch>();

const columns: ColumnDef<ProposalBatch, any>[] = [
columnHelper.accessor('status', {
header: 'Status',
cell: (props) => (
<StatusChip status={props.getValue<ProposalBatchStatus>()} />
),
}),

columnHelper.accessor('height', {
header: 'Height',
cell: (props) => props.getValue<string>(),
}),

columnHelper.accessor('proposals', {
header: 'Proposal(s)',
cell: (props) => {
const proposalTypes = props.getValue().map((proposal: any) => {
return proposal.type;
});
return proposalTypes.length > 0 ? (
<ProposalsBadgeGroup proposals={proposalTypes} />
) : (
'--'
);
},
}),

columnHelper.accessor('chain', {
header: 'Chain',
cell: (props) => {
const logoName = mapChainNameToLogo(props.getValue());
return <ChainIcon name={logoName} size="lg" />;
},
}),

// columnHelper.accessor('id', {
// header: '',
// cell: (props) => (
// <Button variant="link" size="sm">
// <Link to={`drawer/${props.getValue<string>()}`}>Details</Link>
// </Button>
// ),
// }),
];

export const ProposalsTable = () => {
return <div>Proposal Table</div>;
const [{ pageIndex, pageSize }, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});

const pagination = useMemo(
() => ({
pageIndex,
pageSize,
}),
[pageIndex, pageSize]
);

const chains = useMemo<Array<[string, ChainConfig]>>(
() =>
Object.keys(chainsConfig).map((key: any) => [
String(key),
chainsConfig[key],
]),
[]
);

const [globalFilter, setGlobalFilter] = useState('');
const [selectedProposalsStatuses, setSelectedProposalStatuses] = useState<
'all' | ProposalBatchStatus[]
>('all');
const [selectedProposalTypes, setSelectedProposalTypes] = useState<
'all' | ProposalType[]
>('all');
const [selectedChains, setSelectedChains] = useState<
'all' | [string, ChainConfig][]
>('all');

const pageQuery: BatchedProposalsQuery = useMemo(
() => ({
offset: pagination.pageIndex * pageSize,
perPage: pagination.pageSize,
orderBy: ProposalBatchesOrderBy.IdDesc,
filter: null,
}),
[
pageSize,
pagination.pageIndex,
pagination.pageSize,
selectedProposalTypes,
selectedProposalsStatuses,
selectedChains,
]
);

const batchedProposals = useBatchedProposals(pageQuery);

const totalItems = useMemo(() => {
if (batchedProposals.val) {
return batchedProposals.val.pageInfo.count;
}
return 0;
}, [batchedProposals]);

const pageCount = useMemo(
() => Math.ceil(totalItems / pageSize),
[pageSize, totalItems]
);

const data = useMemo(() => {
if (batchedProposals.val) {
return batchedProposals.val.items;
}

return [] as ProposalBatch[];
}, [batchedProposals]);

const table = useReactTable<ProposalBatch>({
columns,
data: data,
pageCount: pageCount,
getCoreRowModel: getCoreRowModel(),
state: {
pagination,
},
onPaginationChange: setPagination,
manualPagination: true,
getPaginationRowModel: getPaginationRowModel(),
filterFns: {
fuzzy: fuzzyFilter,
},
});

return (
<CardTable
titleProps={{
title: 'All Proposals',
info: 'All proposals',
variant: 'h5',
}}
leftTitle={
<Filter
searchPlaceholder={'Search proposals'}
searchText={globalFilter}
onSearchChange={(nextValue: string | number) => {
setGlobalFilter(nextValue.toString());
}}
clearAllFilters={() => {
table.setColumnFilters([]);
table.setGlobalFilter('');
setSelectedProposalTypes('all');
setSelectedProposalStatuses('all');
setSelectedChains('all');
}}
>
<Accordion type={'single'} collapsible>
<AccordionItem className={'p-0 py-0'} value={'proposal-type'}>
<AccordionButton>Type</AccordionButton>
<Divider className="bg-mono-40 dark:bg-mono-140" />
<AccordionContent className="p-0">
<div
className={
'max-w-[300px] max-h-[300px] overflow-x-hidden overflow-y-auto'
}
>
<CheckBoxMenuGroup
value={selectedProposalTypes}
options={PROPOSAL_TYPES}
onChange={(v) => {
setSelectedProposalTypes(v);
}}
labelGetter={(proposalType) => (
<span className={'text-xs'}>{proposalType}</span>
)}
keyGetter={(proposalType) =>
`Filter_proposals${proposalType}`
}
/>
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem className={'p-0 py-0'} value={'proposal-status'}>
<AccordionButton>Status</AccordionButton>
<Divider className="bg-mono-40 dark:bg-mono-140" />
<AccordionContent className="p-0">
<div
className={
'max-w-[300px] max-h-[300px] overflow-x-hidden overflow-y-auto'
}
>
<CheckBoxMenuGroup
value={selectedProposalsStatuses}
options={PROPOSAL_STATUS}
onChange={(v) => {
setSelectedProposalStatuses(v);
}}
labelGetter={(proposalStatus) => (
<Chip
color={mapProposalStatusToChipColor(proposalStatus)}
>
{proposalStatus}
</Chip>
)}
keyGetter={(proposalStatus) =>
`Filter_proposals${proposalStatus}`
}
/>
</div>
</AccordionContent>
</AccordionItem>

<AccordionItem className={'p-0'} value={'chain'}>
<AccordionButton>Chain</AccordionButton>
<AccordionContent className="p-2">
<div className="max-w-[300px] max-h-[300px] overflow-x-hidden overflow-y-auto">
<CheckBoxMenuGroup
value={selectedChains}
options={chains}
onChange={(v) => {
setSelectedChains(v);
}}
iconGetter={([_key, chainConfig]) => (
<div className="max-w-[20px] max-h-[20px] overflow-hidden ">
{/* {<chainConfig.logo />} */}
</div>
)}
labelGetter={([_, chain]) => chain.name}
keyGetter={([chainId]) => `Filter_proposals${chainId}`}
/>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</Filter>
}
className="h-[800px]"
>
{data.length > 0 ? (
<Table
tableProps={table as RTTable<unknown>}
isPaginated
totalRecords={totalItems}
title="Proposals"
/>
) : (
<div className="h-[800px] flex items-center flex-col justify-center">
<Spinner size="xl" />
</div>
)}
</CardTable>
);
};

interface StatusChipProps {
status: ProposalBatchStatus;
}

const StatusChip: React.FC<StatusChipProps> = ({ status }) => {
return <Chip color={getChipColorByProposalType(status)}>{status}</Chip>;
};
1 change: 1 addition & 0 deletions apps/stats-dapp/src/containers/ProposalsTable/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './ProposalsTable';
export * from './utils';
Loading
Loading