Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

feat: add poollist and programlist #118

Merged
merged 2 commits into from
Jan 21, 2025
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
2 changes: 2 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export * from "./primitives/MarkdownEditor";
export * from "./components/Form";
export * from "./components/GenericProgressForm";
export * from "./components/MultipleSelect";
export * from "./features/pool/components/PoolList";
export * from "./features/program/components/ProgramList";
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { action } from "@storybook/addon-actions";
import type { Meta, StoryObj } from "@storybook/react";

import { GenericProgressForm } from "./GenericProgressForm";
import { roundSetupSteps } from "./mocks/RoundSetup";

const onSubmit = action("onSubmit");

const meta: Meta<typeof GenericProgressForm> = {
title: "Components/GenericProgressForm",
component: GenericProgressForm,
Expand All @@ -16,9 +19,7 @@ export const Default: Story = {
args: {
name: "Round setup",
steps: roundSetupSteps,
onSubmit: async (values: any) => {
console.log("Submitted final values:", values);
},
onSubmit: async (values: any) => onSubmit(values),
dbName: "formDB",
storeName: "formDrafts",
},
Expand Down
7 changes: 5 additions & 2 deletions src/features/pool/components/PoolCard/PoolCard.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { PoolStatus, PoolType } from "@/types";

import { PoolCard, PoolCardProps, PoolCardQueryProps } from "./PoolCard";

const onProgramClick = action("Pool Clicked!");
const onPoolClick = action("Pool Clicked!");

const simpleRound = {
roundName: "Grants Round Defi",
Expand All @@ -21,7 +21,8 @@ const simpleRound = {
operatorsCount: 10,
logoImg:
"https://cdn.prod.website-files.com/6433c5d029c6bb75f3f00bd5/66f47dd26d8ec8d0e48a22d0_gitcoin-profile.png",
onClick: () => onProgramClick,
onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
createdAtBlock: 123456,
};

export default {
Expand All @@ -39,6 +40,8 @@ export default {
votingEndDate: { control: "date" },
operatorsCount: { control: "number" },
queryResult: { table: { disable: true } }, // Hide queryResult from controls
createdAtBlock: { control: "number" },
onClick: { action: "onClick" },
},
} as Meta<typeof PoolCard>;

Expand Down
9 changes: 7 additions & 2 deletions src/features/pool/components/PoolCard/PoolDataCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ export function PoolDataCard({ data }: PoolDataCardProps) {
const { name, icon } = getChainInfo(data.chainId);
return (
<div
onClick={data.onClick}
className="inline-flex h-60 w-full items-center justify-between rounded-2xl border border-grey-100 p-6"
onClick={() =>
data.onClick?.({
chainId: data.chainId,
roundId: data.roundId,
})
}
className="inline-flex h-60 w-full cursor-pointer items-center justify-between rounded-2xl border border-grey-100 p-6"
>
<div className="flex items-center justify-start gap-6">
<img className="relative size-48 rounded-2xl" src={data.logoImg} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PoolStatus, PoolType } from "@/types";

import { PoolCardGroup } from "./PoolCardGroup";

const onProgramClick = action("Pool Clicked!");
const onPoolClick = action("Pool Clicked!");

const pools = [
{
Expand All @@ -19,9 +19,10 @@ const pools = [
votingStartDate: new Date("2024-12-09T19:22:56.413Z"),
votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
operatorsCount: 10,
createdAtBlock: 1234567890,
logoImg:
"https://cdn.prod.website-files.com/6433c5d029c6bb75f3f00bd5/66f47dd26d8ec8d0e48a22d0_gitcoin-profile.png",
onClick: () => onProgramClick,
onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
},
{
roundName: "Uniswap",
Expand All @@ -35,7 +36,8 @@ const pools = [
votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
operatorsCount: 5,
logoImg: "https://thegivingblock.com/wp-content/uploads/2021/07/Uniswap-Logo.png",
onClick: () => onProgramClick,
createdAtBlock: 1234567890,
onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
},
];

Expand Down
4 changes: 2 additions & 2 deletions src/features/pool/components/PoolCardGroup/PoolCardGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ const justifyVariants = tv({
export const PoolCardGroup = ({ pools, justify }: PoolCardGroupProps) => {
return (
<div className={justifyVariants({ justify })}>
{pools.map((stat, index) => (
<PoolCard key={index} {...stat} />
{pools.map((pool, index) => (
<PoolCard key={index} {...pool} />
))}
</div>
);
Expand Down
67 changes: 67 additions & 0 deletions src/features/pool/components/PoolList/PoolList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { action } from "@storybook/addon-actions";
import type { Meta, StoryObj } from "@storybook/react";

import { PoolType } from "@/types";
import { PoolStatus } from "@/types/pool";

import { PoolList } from "./PoolList";

const onPoolClick = action("Pool clicked!");

const meta: Meta<typeof PoolList> = {
title: "Features/Pool/PoolList",
component: PoolList,
};

export default meta;
type Story = StoryObj<typeof PoolList>;

const mockPools = [
{
roundName: "Grants Round Defi",
roundId: "90",
chainId: 10,
poolType: PoolType.QuadraticFunding,
poolStatus: PoolStatus.ApplicationsInProgress,
applicationStartDate: new Date("2024-12-08T19:22:56.413Z"),
applicationEndDate: new Date("2024-12-09T19:23:30.678Z"),
votingStartDate: new Date("2024-12-09T19:22:56.413Z"),
votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
operatorsCount: 10,
createdAtBlock: 100000,
logoImg:
"https://cdn.prod.website-files.com/6433c5d029c6bb75f3f00bd5/66f47dd26d8ec8d0e48a22d0_gitcoin-profile.png",
},
{
roundName: "Uniswap",
roundId: "91",
chainId: 8453,
poolType: PoolType.DirectGrants,
poolStatus: PoolStatus.FundingPending,
applicationStartDate: new Date("2024-12-08T19:22:56.413Z"),
applicationEndDate: new Date("2024-12-09T19:23:30.678Z"),
votingStartDate: new Date("2024-12-09T19:22:56.413Z"),
votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
operatorsCount: 5,
createdAtBlock: 1000,
logoImg: "https://thegivingblock.com/wp-content/uploads/2021/07/Uniswap-Logo.png",
},
];

export const Default: Story = {
args: {
pools: mockPools.map((pool) => ({
...pool,
onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
})),
title: "Available Pools",
noPoolsPlaceholder: "No pools found",
},
};

export const Empty: Story = {
args: {
...Default.args,
pools: [],
},
};
76 changes: 76 additions & 0 deletions src/features/pool/components/PoolList/PoolList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client";

import { useState } from "react";

import { MultipleSelect } from "@/components/MultipleSelect";

import { PoolCardProps } from "../PoolCard";
import { PoolCardGroup } from "../PoolCardGroup";
import { useFilteredAndOrderedPools } from "./hooks/useFilteredAndOrderedPools";
import { getSortFilterOptions } from "./utils";

export interface PoolListProps {
pools: PoolCardProps[];
title?: string;
noPoolsPlaceholder?: string;
}

export const PoolList = ({
pools,
title = "Pools",
noPoolsPlaceholder = "No pools found",
}: PoolListProps) => {
const [order, setOrder] = useState<Record<string, string[]>>({});
const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({});

const { orderOptions, filterOptions } = getSortFilterOptions(pools);

const handleFilterChange = (values: Record<string, string[]>) => {
setSelectedFilters(values);
};

const handleOrderChange = (values: Record<string, string[]>) => {
setOrder(values);
};

const filteredAndOrderedPools = useFilteredAndOrderedPools({ pools, order, selectedFilters });

return (
<div className="flex w-full flex-col gap-4">
<div className="flex w-full items-center justify-between">
<div className="flex-1 text-start font-ui-sans text-2xl font-medium">
{`${title} (${pools.length})`}
</div>
<div className="flex items-center gap-2">
<div className="flex items-center gap-2">
<div className="text-nowrap font-ui-sans text-body font-medium">Order by</div>
<MultipleSelect
options={orderOptions}
onChange={handleOrderChange}
defaultValue={{ "ORDER BY TIME": ["Recent"] }}
className="w-40"
variants={{ triggerTextColor: "green", itemsPosition: "end", headerPosition: "end" }}
/>
</div>
<div className="flex items-center gap-2">
<div className="text-nowrap font-ui-sans text-body font-medium">Filter by</div>
<MultipleSelect
options={filterOptions}
onChange={handleFilterChange}
defaultValue={{ ungrouped: ["All"] }} // so it starts with 'All' selected
className="w-64"
variants={{ triggerTextColor: "red" }}
/>
</div>
</div>
</div>
<div className="w-full">
{filteredAndOrderedPools.length > 0 ? (
<PoolCardGroup pools={filteredAndOrderedPools} />
) : (
<div className="font-ui-sans text-lg">{noPoolsPlaceholder}</div>
)}
</div>
</div>
);
};
1 change: 1 addition & 0 deletions src/features/pool/components/PoolList/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useFilteredAndOrderedPools";
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useMemo } from "react";

import { PoolData, PoolStatus } from "@/types/pool";

interface UseFilteredAndOrderedPoolsProps {
pools: PoolData[];
selectedFilters: Record<string, string[]>;
order: Record<string, string[]>;
}

export const useFilteredAndOrderedPools = ({
pools,
selectedFilters,
order,
}: UseFilteredAndOrderedPoolsProps) => {
return useMemo(() => {
let result = [...pools];

// 1) Check for "All" filter
const ungrouped = selectedFilters["ungrouped"];
const hasAllFilter = ungrouped?.includes("All");

// 2) Filter by network
const selectedNetworks = selectedFilters["Network"] || [];
if (!hasAllFilter && selectedNetworks.length > 0) {
result = result.filter((pool) => selectedNetworks.includes(pool.chainId.toString()));
}

// 3) Filter by status
const selectedStatuses = selectedFilters["Status"] || [];
if (!hasAllFilter && selectedStatuses.length > 0) {
result = result.filter((pool) => {
return selectedStatuses.some((status) => {
switch (status) {
case "active":
return (
pool.poolStatus === PoolStatus.RoundInProgress ||
pool.poolStatus === PoolStatus.ApplicationsInProgress
);
case "applications":
return pool.poolStatus === PoolStatus.ApplicationsInProgress;
case "finished":
return pool.poolStatus === PoolStatus.FundingPending;
default:
return false;
}
});
});
}

// 4) Apply ordering
const orderValue = order["ORDER BY TIME"]?.[0] || order["ORDER BY NAME"]?.[0] || "Recent";
switch (orderValue) {
case "Recent":
result.sort((a, b) => b.createdAtBlock - a.createdAtBlock);
break;
case "Oldest":
result.sort((a, b) => a.createdAtBlock - b.createdAtBlock);
break;
case "A-Z":
result.sort((a, b) => a.roundName.localeCompare(b.roundName));
break;
case "Z-A":
result.sort((a, b) => b.roundName.localeCompare(a.roundName));
break;
}

return result;
}, [pools, order, selectedFilters]);
};
3 changes: 3 additions & 0 deletions src/features/pool/components/PoolList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./PoolList";
export * from "./utils";
export * from "./hooks";
Loading
Loading