From e7c85be73d2f4d8c893c7e8787851d94736ef057 Mon Sep 17 00:00:00 2001 From: Satyajeet Kolhapure Date: Thu, 24 Oct 2024 12:47:36 +0100 Subject: [PATCH] added attestation card view --- .../components/AttestationCard/index.tsx | 73 +++++++++++++++++++ .../components/AttestationCard/interface.ts | 9 +++ .../components/AttestationInfo/index.tsx | 6 +- explorer/src/pages/MyAttestations/index.tsx | 36 ++++++++- explorer/src/pages/Subject/index.tsx | 66 +++++++++++++++++ explorer/src/routes/constants.ts | 6 ++ explorer/src/routes/index.tsx | 2 + .../dataMapper/AttestationDataMapper.test.ts | 1 - 8 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 explorer/src/pages/Attestation/components/AttestationCard/index.tsx create mode 100644 explorer/src/pages/Attestation/components/AttestationCard/interface.ts create mode 100644 explorer/src/pages/Subject/index.tsx diff --git a/explorer/src/pages/Attestation/components/AttestationCard/index.tsx b/explorer/src/pages/Attestation/components/AttestationCard/index.tsx new file mode 100644 index 00000000..38f7ce2b --- /dev/null +++ b/explorer/src/pages/Attestation/components/AttestationCard/index.tsx @@ -0,0 +1,73 @@ +import { ChevronRight } from "lucide-react"; +import { generatePath, useLocation, useNavigate } from "react-router-dom"; +import { useTernaryDarkMode } from "usehooks-ts"; + +import { Button } from "@/components/Buttons"; +import { EButtonType } from "@/components/Buttons/enum"; +import { useNetworkContext } from "@/providers/network-provider/context"; +import { APP_ROUTES } from "@/routes/constants"; +import { parseDateTime } from "@/utils/dateUtils"; + +import { IAttestationCardProps } from "./interface"; + +export const AttestationCard: React.FC = ({ + id, + logo, + logoDark, + name, + description, + issuerName, + issuanceDate, +}) => { + const { + network: { network }, + } = useNetworkContext(); + const navigate = useNavigate(); + const location = useLocation(); + const { isDarkMode } = useTernaryDarkMode(); + + const handleViewDetailsClick = (id: string) => { + navigate(generatePath(APP_ROUTES.ATTESTATION_BY_ID, { chainId: network, id }), { + state: { from: location.pathname }, + }); + }; + + const displayLogo = () => { + const Logo: React.FC> = isDarkMode && logoDark ? logoDark : logo; + return ; + }; + + return ( +
+
+
+ {displayLogo()} +
+ {name} +
+ {description && description.trim() ? ( +
{description}
+ ) : null} +
+ Issuer : + {issuerName} +
+
+ Date : + {parseDateTime(issuanceDate.toString(), true).stringUTC} +
+
+
+
+ ); +}; diff --git a/explorer/src/pages/Attestation/components/AttestationCard/interface.ts b/explorer/src/pages/Attestation/components/AttestationCard/interface.ts new file mode 100644 index 00000000..f1902b89 --- /dev/null +++ b/explorer/src/pages/Attestation/components/AttestationCard/interface.ts @@ -0,0 +1,9 @@ +export interface IAttestationCardProps { + id: string; + logo: React.FC>; + logoDark?: React.FC>; + name: string; + description?: string; + issuerName: string; + issuanceDate: number; +} diff --git a/explorer/src/pages/Attestation/components/AttestationInfo/index.tsx b/explorer/src/pages/Attestation/components/AttestationInfo/index.tsx index 1a4bea35..811b4020 100644 --- a/explorer/src/pages/Attestation/components/AttestationInfo/index.tsx +++ b/explorer/src/pages/Attestation/components/AttestationInfo/index.tsx @@ -8,7 +8,7 @@ import { useEnsName } from "wagmi"; import { Link } from "@/components/Link"; import { useNetworkContext } from "@/providers/network-provider/context"; -import { toPortalById } from "@/routes/constants"; +import { CHAIN_ID_ROUTE, toAttestationsBySubject, toPortalById } from "@/routes/constants"; import { getBlockExplorerLink } from "@/utils"; import { displayAmountWithComma } from "@/utils/amountUtils"; import { cropString } from "@/utils/stringUtils"; @@ -17,7 +17,7 @@ import { createDateListItem } from "./utils"; export const AttestationInfo: React.FC = ({ ...attestation }) => { const { - network: { chain }, + network: { chain, network }, } = useNetworkContext(); const { data: attesterEnsAddress } = useEnsName({ @@ -73,7 +73,7 @@ export const AttestationInfo: React.FC = ({ ...attestation }) => { { title: t("attestation.info.subject"), value: displaySubjectEnsNameOrAddress(), - link: `${blockExplorerLink}/${subject}`, + link: toAttestationsBySubject(subject).replace(CHAIN_ID_ROUTE, network), }, ]; diff --git a/explorer/src/pages/MyAttestations/index.tsx b/explorer/src/pages/MyAttestations/index.tsx index 51e87a30..356612b3 100644 --- a/explorer/src/pages/MyAttestations/index.tsx +++ b/explorer/src/pages/MyAttestations/index.tsx @@ -9,18 +9,18 @@ import { useAccount } from "wagmi"; import { Button } from "@/components/Buttons"; import { EButtonType } from "@/components/Buttons/enum"; -import { DataTable } from "@/components/DataTable"; import { InfoBlock } from "@/components/InfoBlock"; import { THOUSAND } from "@/constants"; -import { columns } from "@/constants/columns/attestation"; import { EQueryParams } from "@/enums/queryParams"; import useWindowDimensions from "@/hooks/useWindowDimensions"; import { SWRKeys } from "@/interfaces/swr/enum"; import { useNetworkContext } from "@/providers/network-provider/context"; -import { APP_ROUTES } from "@/routes/constants"; import { cropString } from "@/utils/stringUtils"; +import { AttestationCard } from "../Attestation/components/AttestationCard"; import { TitleAndSwitcher } from "../Attestations/components/TitleAndSwitcher"; +import { issuersData } from "../Home/data"; +import { IIssuer } from "../Home/interface"; export const MyAttestations: React.FC = () => { const { @@ -91,7 +91,35 @@ export const MyAttestations: React.FC = () => { ) : !attestationsList || !attestationsList.length ? ( } message={t("attestation.messages.emptyList")} /> ) : ( - +
+
+ {attestationsList.map((attestation) => { + const issuerData = issuersData.find((issuer) => + issuer.attestationDefinitions.some( + (definition) => + definition.schema === attestation.schema.id && definition.portal === attestation.portal.id, + ), + ) as IIssuer; + const attestationDefinitions = issuerData?.attestationDefinitions.find( + (definition) => definition.schema === attestation.schema.id, + ); + + if (!issuerData) return null; + return ( + + ); + })} +
+
)} ); diff --git a/explorer/src/pages/Subject/index.tsx b/explorer/src/pages/Subject/index.tsx new file mode 100644 index 00000000..b8d14294 --- /dev/null +++ b/explorer/src/pages/Subject/index.tsx @@ -0,0 +1,66 @@ +import { OrderDirection } from "@verax-attestation-registry/verax-sdk/lib/types/.graphclient"; +import { useParams } from "react-router-dom"; +import useSWR from "swr"; + +import { EQueryParams } from "@/enums/queryParams"; +import { SWRKeys } from "@/interfaces/swr/enum"; +import { useNetworkContext } from "@/providers/network-provider/context"; + +import { AttestationCard } from "../Attestation/components/AttestationCard"; +import { issuersData } from "../Home/data"; +import { IIssuer } from "../Home/interface"; + +export const Subject: React.FC = () => { + const { subject } = useParams(); + const { + sdk, + network: { chain }, + } = useNetworkContext(); + const searchParams = new URLSearchParams(window.location.search); + const sortByDateDirection = searchParams.get(EQueryParams.SORT_BY_DATE); + + const { data: attestationsList } = useSWR( + `${SWRKeys.GET_ATTESTATION_LIST}/${subject}/${sortByDateDirection}/${chain.id}`, + () => + sdk.attestation.findBy( + undefined, + undefined, + { subject }, + "attestedDate", + (sortByDateDirection as OrderDirection) || "desc", + ), + ); + + return ( +
+
+ {attestationsList && + attestationsList.map((attestation) => { + const issuerData = issuersData.find((issuer) => + issuer.attestationDefinitions.some( + (definition) => + definition.schema === attestation.schema.id && definition.portal === attestation.portal.id, + ), + ) as IIssuer; + const attestationDefinitions = issuerData?.attestationDefinitions.find( + (definition) => definition.schema === attestation.schema.id, + ); + + if (!issuerData) return null; + return ( + + ); + })} +
+
+ ); +}; diff --git a/explorer/src/routes/constants.ts b/explorer/src/routes/constants.ts index a4166bf3..ec390988 100644 --- a/explorer/src/routes/constants.ts +++ b/explorer/src/routes/constants.ts @@ -1,4 +1,5 @@ const ID_ROUTE = ":id"; +const SUBJECT_ROUTE = ":subject"; export const CHAIN_ID_ROUTE = ":chainId"; export const APP_ROUTES = { @@ -15,6 +16,9 @@ export const APP_ROUTES = { get MY_ATTESTATIONS() { return this.ATTESTATIONS + "/my_attestations"; }, + get ATTESTATIONS_BY_SUBJECT() { + return this.HOME + "/subject/" + SUBJECT_ROUTE; + }, get ATTESTATION_BY_ID() { return this.ATTESTATIONS + `/${ID_ROUTE}`; }, @@ -40,6 +44,8 @@ export const APP_ROUTES = { } as const; export const toAttestationById = (id: string) => APP_ROUTES.ATTESTATION_BY_ID.replace(ID_ROUTE, id); +export const toAttestationsBySubject = (subject: string) => + APP_ROUTES.ATTESTATIONS_BY_SUBJECT.replace(SUBJECT_ROUTE, subject); export const toSchemaById = (id: string) => APP_ROUTES.SCHEMA_BY_ID.replace(ID_ROUTE, id); export const toModuleById = (id: string) => APP_ROUTES.MODULES_BY_ID.replace(ID_ROUTE, id); export const toPortalById = (id: string) => APP_ROUTES.PORTAL_BY_ID.replace(ID_ROUTE, id); diff --git a/explorer/src/routes/index.tsx b/explorer/src/routes/index.tsx index 50fa9944..292526c2 100644 --- a/explorer/src/routes/index.tsx +++ b/explorer/src/routes/index.tsx @@ -11,6 +11,7 @@ import { Portal } from "@/pages/Portal"; import { Schema } from "@/pages/Schema"; import { Schemas } from "@/pages/Schemas"; import { Search } from "@/pages/Search"; +import { Subject } from "@/pages/Subject"; import { Providers } from "@/providers"; import { loaderNetworkProvider } from "@/providers/network-provider/loader"; @@ -24,6 +25,7 @@ export const router = createBrowserRouter( } /> } /> } /> + } /> } /> } /> } /> diff --git a/sdk/src/dataMapper/AttestationDataMapper.test.ts b/sdk/src/dataMapper/AttestationDataMapper.test.ts index 1261b3e8..edba5493 100644 --- a/sdk/src/dataMapper/AttestationDataMapper.test.ts +++ b/sdk/src/dataMapper/AttestationDataMapper.test.ts @@ -7,7 +7,6 @@ import BaseDataMapper from "./BaseDataMapper"; import { lineaSepolia } from "viem/chains"; import { PublicClient, WalletClient } from "viem"; import { VeraxSdk } from "../VeraxSdk"; -import { off } from "process"; jest.mock("./BaseDataMapper"); jest.mock("../utils/abiCoder");