diff --git a/.storybook/main.ts b/.storybook/main.ts index 4a904f0c..f3a89d89 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -3,7 +3,6 @@ import { StorybookConfig } from "@storybook/react-vite"; const config: StorybookConfig = { stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"], addons: [ - "@storybook/addon-onboarding", "@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions", diff --git a/package-lock.json b/package-lock.json index 9d4cdbe6..0d1d3d57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.5.2", "license": "Apache-2.0", "dependencies": { - "@covalenthq/client-sdk": "^1.0.0", + "@covalenthq/client-sdk": "^1.0.1", "@headlessui/react": "^1.7.18", "@headlessui/tailwindcss": "^0.2.0", "@radix-ui/react-avatar": "^1.0.4", @@ -31,7 +31,6 @@ "@storybook/addon-essentials": "^8.0.8", "@storybook/addon-interactions": "^8.0.8", "@storybook/addon-links": "^8.0.8", - "@storybook/addon-onboarding": "^8.0.8", "@storybook/blocks": "^8.0.8", "@storybook/manager-api": "^8.0.8", "@storybook/react": "^8.0.8", @@ -1964,8 +1963,9 @@ } }, "node_modules/@covalenthq/client-sdk": { - "version": "1.0.0", - "license": "MIT", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@covalenthq/client-sdk/-/client-sdk-1.0.1.tgz", + "integrity": "sha512-qIIqnq5jUXQY+o1v2uFSaygUCvckc+GgLJypJpnH5T4eB/YEjJcKHE0we7VdnbC5n6mFPbicARhauG7qL+kHQQ==", "dependencies": { "@rollup/plugin-commonjs": "^25.0.4", "@rollup/plugin-node-resolve": "^15.2.1", @@ -3744,15 +3744,6 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/addon-onboarding": { - "version": "8.0.8", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, "node_modules/@storybook/addon-outline": { "version": "8.0.8", "dev": true, diff --git a/package.json b/package.json index 75326677..df0ed498 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "@storybook/addon-essentials": "^8.0.8", "@storybook/addon-interactions": "^8.0.8", "@storybook/addon-links": "^8.0.8", - "@storybook/addon-onboarding": "^8.0.8", "@storybook/blocks": "^8.0.8", "@storybook/manager-api": "^8.0.8", "@storybook/react": "^8.0.8", @@ -83,7 +82,7 @@ "vite-plugin-dts": "^3.8.2" }, "dependencies": { - "@covalenthq/client-sdk": "^1.0.0", + "@covalenthq/client-sdk": "^1.0.1", "@headlessui/react": "^1.7.18", "@headlessui/tailwindcss": "^0.2.0", "@radix-ui/react-avatar": "^1.0.4", diff --git a/src/components/Atoms/Address/Address.stories.tsx b/src/components/Atoms/Address/Address.stories.tsx index a92546aa..2ef49c1c 100644 --- a/src/components/Atoms/Address/Address.stories.tsx +++ b/src/components/Atoms/Address/Address.stories.tsx @@ -13,5 +13,6 @@ type Story = StoryObj; export const Address: Story = { args: { address: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + show_copy_icon: true, }, }; diff --git a/src/components/Atoms/Address/Address.tsx b/src/components/Atoms/Address/Address.tsx index df6b40da..cf3c88e2 100644 --- a/src/components/Atoms/Address/Address.tsx +++ b/src/components/Atoms/Address/Address.tsx @@ -4,7 +4,10 @@ import { type AddressProps } from "@/utils/types/atoms.types"; import { useToast } from "@/utils/hooks"; import { useState } from "react"; -export const Address: React.FC = ({ address }) => { +export const Address: React.FC = ({ + address, + show_copy_icon = true, +}) => { const [showCopy, setShowCopy] = useState(false); const { toast } = useToast(); @@ -21,27 +24,31 @@ export const Address: React.FC = ({ address }) => { return (

{truncate(address)}

- + {show_copy_icon ? ( + + ) : ( + <> + )}
); }; diff --git a/src/components/Molecules/LatestBlocks/LatestBlocks.stories.tsx b/src/components/Molecules/LatestBlocks/LatestBlocks.stories.tsx new file mode 100644 index 00000000..99c1a091 --- /dev/null +++ b/src/components/Molecules/LatestBlocks/LatestBlocks.stories.tsx @@ -0,0 +1,18 @@ +import { type Meta, type StoryObj } from "@storybook/react"; +import { LatestBlocks as LatestBlocksComponent } from "./LatestBlocks"; + +type Story = StoryObj; + +const meta: Meta = { + title: "Molecules/Latest Blocks", + component: LatestBlocksComponent, +}; + +export default meta; + +export const LatestBlocks: Story = { + args: { + chain_name: "eth-mainnet", + height: 16643179, + }, +}; diff --git a/src/components/Molecules/LatestBlocks/LatestBlocks.tsx b/src/components/Molecules/LatestBlocks/LatestBlocks.tsx new file mode 100644 index 00000000..8cb08a7e --- /dev/null +++ b/src/components/Molecules/LatestBlocks/LatestBlocks.tsx @@ -0,0 +1,121 @@ +import { Address } from "@/components/Atoms"; +import { Button } from "@/components/ui/button"; +import { Card, CardDescription } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { GRK_SIZES } from "@/utils/constants/shared.constants"; +import { timestampParser } from "@/utils/functions"; +import { None, Some, type Option } from "@/utils/option"; +import { useGoldRush } from "@/utils/store"; +import { type LatestBlocksProps } from "@/utils/types/molecules.types"; +import { type Block } from "@covalenthq/client-sdk"; +import { ExternalLinkIcon } from "@radix-ui/react-icons"; +import { useEffect, useState } from "react"; + +export const LatestBlocks: React.FC = ({ + chain_name, + height, + limit = 5, + on_view_details, +}) => { + const { covalentClient } = useGoldRush(); + const [errorMessage, setErrorMessage] = useState(null); + const [maybeResult, setResult] = useState>(None); + + useEffect(() => { + (async () => { + setResult(None); + setErrorMessage(null); + try { + const { data, ...error } = + await covalentClient.BaseService.getBlockHeightsByPage( + chain_name, + timestampParser(Date(), "YYYY MM DD"), + "2100-01-01", + { + pageSize: limit, + } + ); + if (error.error) { + setErrorMessage(error.error_message); + throw error; + } + setResult(new Some(data.items)); + } catch (error) { + console.error(error); + } + })(); + }, [chain_name, height, limit]); + + return maybeResult.match({ + None: () => ( + <> + {new Array(limit).fill(null).map(() => ( + + ))} + + ), + Some: (blocks) => + errorMessage ? ( +

{errorMessage}

+ ) : ( + blocks.map((block) => ( + +
+ BLOCK HEIGHT + +

+ {block.height.toLocaleString()} +

+
+ +
+ SIGNED AT + +

+ {timestampParser(block.signed_at, "relative")} +

+
+ +
+ BLOCK HASH + +
+
+ +
+ GAS USED + {((block.gas_used / block.gas_limit) * 100).toFixed( + 2 + )} + % +
+ +
+ GAS LIMIT + + {block.gas_limit.toLocaleString()} +
+ + {on_view_details ? ( + + ) : ( + <> + )} +
+ )) + ), + }); +}; diff --git a/src/components/Molecules/index.ts b/src/components/Molecules/index.ts index c81caa0b..9fe853bd 100644 --- a/src/components/Molecules/index.ts +++ b/src/components/Molecules/index.ts @@ -4,6 +4,7 @@ export { ChainSelector } from "./ChainSelector/ChainSelector"; export { CollectionCard } from "./CollectionCard/CollectionCard"; export { DecodedTransaction } from "./DecodedTransaction/DecodedTransaction"; export { GasCard } from "./GasCard/GasCard"; +export { LatestBlocks } from "./LatestBlocks/LatestBlocks"; export { NFTFloorPrice } from "./NFTs/NFTFloorPrice/NFTFloorPrice"; export { NFTSalesCount } from "./NFTs/NFTSalesCount/NFTSalesCount"; export { NFTVolume } from "./NFTs/NFTVolume/NFTVolume"; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 708b698a..1cd44e22 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -12,7 +12,7 @@ const buttonVariants = cva( "bg-primary-light dark:bg-primary-dark text-foreground-light dark:text-foreground-dark shadow hover:bg-opacity-90", outline: "border border-input bg-background-light dark:bg-background-dark shadow-sm hover:bg-primary-light border-secondary-light dark:border-secondary-dark", - ghost: "hover:bg-primary-light hover:text-secondary-light dark:hover:bg-primary-dark dark:hover:text-secondary-dark", + ghost: "hover:bg-primary-light hover:text-foreground-light dark:hover:bg-primary-dark dark:hover:text-foreground-dark", link: "text-primary-light dark:text-primary-dark underline-offset-4 hover:underline", }, size: { diff --git a/src/utils/functions/timestamp-parser.ts b/src/utils/functions/timestamp-parser.ts index e3ffce04..9f566abb 100644 --- a/src/utils/functions/timestamp-parser.ts +++ b/src/utils/functions/timestamp-parser.ts @@ -15,7 +15,7 @@ const months: string[] = [ export const timestampParser = ( timestamp: string | Date, - type: "descriptive" | "DD MMM YY" | "relative" + type: "descriptive" | "DD MMM YY" | "relative" | "YYYY MM DD" ): string => { const _unix: Date = new Date(timestamp); @@ -78,8 +78,16 @@ export const timestampParser = ( } } + case "YYYY MM DD": { + const year = _unix.getFullYear(); + const month = String(_unix.getMonth() + 1).padStart(2, "0"); + const day = String(_unix.getDate()).padStart(2, "0"); + + return `${year}-${month}-${day}`; + } + default: { - return "error"; + return _unix.toISOString(); } } }; diff --git a/src/utils/types/atoms.types.ts b/src/utils/types/atoms.types.ts index fbc3d1f9..ad7d5840 100644 --- a/src/utils/types/atoms.types.ts +++ b/src/utils/types/atoms.types.ts @@ -2,6 +2,7 @@ import { type GRK_SIZES } from "../constants/shared.constants"; export interface AddressProps { address: string; + show_copy_icon?: boolean; } export interface AddressAvatarProps { diff --git a/src/utils/types/molecules.types.ts b/src/utils/types/molecules.types.ts index 993f23d8..da69e8ea 100644 --- a/src/utils/types/molecules.types.ts +++ b/src/utils/types/molecules.types.ts @@ -6,6 +6,7 @@ import { type ExchangeTransaction, type Transaction, type ChainItem, + type Block, } from "@covalenthq/client-sdk"; import { type DECODED_ACTION, @@ -24,6 +25,13 @@ export interface BlockDetailsProps { height: number; } +export interface LatestBlocksProps { + chain_name: Chain; + height: number; + limit?: number; + on_view_details?: (block: Block) => void; +} + export interface GasCardProps { chain_name: Chain; event_type: string;