Skip to content

Commit

Permalink
fixed as per review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
satyajeetkolhapure committed Oct 25, 2024
1 parent 472c436 commit e2f0195
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 78 deletions.
2 changes: 2 additions & 0 deletions explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@tanstack/react-table": "^8.10.7",
"@verax-attestation-registry/verax-sdk": "2.1.1",
"@tippyjs/react": "^4.2.6",
"@wagmi/core": "^1.4.7",
"abitype": "^0.10.3",
"class-variance-authority": "^0.7.0",
Expand All @@ -45,6 +46,7 @@
"swr": "^2.2.4",
"tailwind-merge": "^2.0.0",
"tailwindcss-animate": "^1.0.7",
"tippy.js": "6",
"usehooks-ts": "^2.9.1",
"viem": "1.18.9",
"vite-tsconfig-paths": "^4.2.1",
Expand Down
18 changes: 14 additions & 4 deletions explorer/src/constants/columns/attestation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ import { Link } from "@/components/Link";
import { SortByDate } from "@/components/SortByDate";
import { ColumnsOptions } from "@/interfaces/components";
import { SWRCell } from "@/pages/Attestations/components/SWRCell";
import { toAttestationById, toPortalById, toSchemaById } from "@/routes/constants";
import { getBlockExplorerLink } from "@/utils";
import {
CHAIN_ID_ROUTE,
toAttestationById,
toAttestationsBySubject,
toPortalById,
toSchemaById,
} from "@/routes/constants";
import { displayAmountWithComma } from "@/utils/amountUtils";
import { cropString } from "@/utils/stringUtils";

Expand All @@ -22,9 +27,14 @@ import { EMPTY_0X_STRING, EMPTY_STRING, ITEMS_PER_PAGE_DEFAULT } from "../index"
interface ColumnsProps {
sortByDate: boolean;
chain: Chain;
network: string;
}

export const columns = ({ sortByDate = true, chain }: Partial<ColumnsProps> = {}): ColumnDef<Attestation>[] => [
export const columns = ({
sortByDate = true,
chain,
network,
}: Partial<ColumnsProps> = {}): ColumnDef<Attestation>[] => [
{
accessorKey: "id",
header: () => (
Expand Down Expand Up @@ -82,7 +92,7 @@ export const columns = ({ sortByDate = true, chain }: Partial<ColumnsProps> = {}

return (
<a
href={`${getBlockExplorerLink(chain)}/${subject}`}
href={toAttestationsBySubject(subject).replace(CHAIN_ID_ROUTE, network)}
onClick={(e) => e.stopPropagation()}
target="_blank"
className="hover:underline"
Expand Down
17 changes: 17 additions & 0 deletions explorer/src/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.tippy-box[data-theme~="light"] {
background-color: #0c0c11;
color: #fefefe;
}

.tippy-box[data-theme~="dark"] {
background-color: #fefefe;
color: #0c0c11;
}

.tippy-box[data-theme~="dark"] .tippy-arrow {
color: #fefefe;
}

.tippy-box[data-theme~="light"] .tippy-arrow {
color: #0c0c11;
}
1 change: 1 addition & 0 deletions explorer/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RouterProvider } from "react-router-dom";

import { router } from "./routes";
import "./index.css";
import "./custom.css";

createRoot(document.getElementById("root")!).render(
<StrictMode>
Expand Down
113 changes: 83 additions & 30 deletions explorer/src/pages/Attestation/components/AttestationCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,59 @@
import Tippy from "@tippyjs/react";
import "tippy.js/dist/tippy.css";
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 { issuersData } from "@/pages/Home/data";
import { IIssuer } from "@/pages/Home/interface";
import { useNetworkContext } from "@/providers/network-provider/context";
import { APP_ROUTES } from "@/routes/constants";
import { parseDateTime } from "@/utils/dateUtils";
import { timeElapsed } from "@/utils/dateUtils";

import { IAttestationCardProps } from "./interface";

export const AttestationCard: React.FC<IAttestationCardProps> = ({
id,
logo,
logoDark,
name,
description,
issuerName,
schemaId,
portalId,
issuanceDate,
expiryDate,
}) => {
const {
network: { network },
} = useNetworkContext();
const navigate = useNavigate();
const location = useLocation();
const { isDarkMode } = useTernaryDarkMode();
const isExpired = expiryDate ? new Date(expiryDate * 1000) < new Date() : false;

const issuerData = issuersData.find((issuer) =>
issuer.attestationDefinitions.some(
(definition) =>
definition.schema.toLowerCase() === schemaId.toLowerCase() &&
definition.portal.toLowerCase() === portalId.toLowerCase(),
),
) as IIssuer;
const attestationDefinitions = issuerData?.attestationDefinitions.find(
(definition) => definition.schema.toLowerCase() === schemaId.toLowerCase(),
);

if (!issuerData) {
console.log("Issuer not found for attestation", id, schemaId, portalId);
return null;
}

const logo = attestationDefinitions?.logo ?? issuerData?.logo;
const logoDark = attestationDefinitions?.logoDark ?? issuerData?.logoDark;
const name = attestationDefinitions?.name ?? issuerData?.name;
const description = attestationDefinitions?.description ?? "";
const issuerName = issuerData.name;

const maxDescriptionLength = 200;
const isDescriptionLong = description.length > maxDescriptionLength;
const truncatedDescription = isDescriptionLong ? `${description.slice(0, maxDescriptionLength)}...` : description;

const handleViewDetailsClick = (id: string) => {
navigate(generatePath(APP_ROUTES.ATTESTATION_BY_ID, { chainId: network, id }), {
Expand All @@ -40,33 +69,57 @@ export const AttestationCard: React.FC<IAttestationCardProps> = ({
return (
<div
key={`${id}`}
className="group flex flex-col gap-4 border border-border-card dark:border-border-cardDark rounded-xl p-4 md:p-6 hover:bg-surface-secondary dark:hover:bg-surface-secondaryDark transition md:min-h-[20rem]"
className="group flex flex-col justify-between gap-4 border border-border-card dark:border-border-cardDark rounded-xl p-4 md:p-6 hover:bg-surface-secondary dark:hover:bg-surface-secondaryDark transition md:min-h-[20rem]"
>
<div className="flex items-center gap-3 text-xl md:text-2xl font-semibold text-blackDefault dark:text-whiteDefault">
<div className="w-[2.5rem] h-[2.5rem] md:w-[3rem] md:h-[3rem] flex items-center justify-center">
{displayLogo()}
<div>
<div className="flex items-start gap-3 text-xl md:text-md font-semibold text-blackDefault dark:text-whiteDefault">
<div className="w-[2.5rem] h-[2.5rem] md:w-[3rem] md:h-[3rem] flex items-center mr-2 justify-center">
{displayLogo()}
</div>
<div className="flex flex-col">
<div>{name}</div>
<div className="text-sm font-normal text-blackDefault dark:text-whiteDefault">{issuerName}</div>
</div>
</div>
{name}
{description && description.trim() ? (
<div className="text-sm font-normal text-text-darkGrey dark:text-tertiary mt-4">
{isDescriptionLong ? (
<Tippy content={description} placement="bottom" theme={isDarkMode ? "dark" : "light"}>
<span>{truncatedDescription}</span>
</Tippy>
) : (
<span>{description}</span>
)}
</div>
) : null}
</div>
{description && description.trim() ? (
<div className="text-sm font-normal text-text-darkGrey dark:text-tertiary">{description}</div>
) : null}
<div className="text-sm font-normal text-text-darkGrey dark:text-tertiary">
<span className="font-bold">Issuer : </span>
{issuerName}
</div>
<div className="text-sm font-normal text-text-darkGrey dark:text-tertiary">
<span className="font-bold">Date : </span>
{parseDateTime(issuanceDate.toString(), true).stringUTC}
</div>
<div className="flex flex-1 flex-col lg:flex-row lg:items-end gap-4 justify-end lg:justify-start">
<Button
isSmall
name="View details"
handler={() => handleViewDetailsClick(id)}
buttonType={EButtonType.OUTLINED}
iconRight={<ChevronRight />}
/>
<div className="flex flex-col gap-2 mt-auto">
<div className="flex justify-between text-sm font-normal text-text-darkGrey dark:text-tertiary">
<span>Issued</span> <span>{timeElapsed(issuanceDate)}</span>
</div>
{!!expiryDate && isExpired && (
<div className="flex justify-between text-sm font-semibold text-text-darkGrey dark:text-tertiary">
<div className="flex items-center">
<span>Expired</span>
<svg className="w-4 h-4 ml-1 text-gray-400" fill="currentColor" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" fill="currentColor" />
<text x="12" y="17" textAnchor="middle" fontSize="14" fill="white" fontWeight="bold">
i
</text>
</svg>
</div>
<span>{timeElapsed(expiryDate)}</span>
</div>
)}
<div className="flex mt-4 lg:flex-row lg:items-end justify-end lg:justify-start">
<Button
isSmall
name="View details"
handler={() => handleViewDetailsClick(id)}
buttonType={EButtonType.OUTLINED}
iconRight={<ChevronRight />}
/>
</div>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
export interface IAttestationCardProps {
id: string;
logo: React.FC<React.SVGProps<SVGSVGElement>>;
logoDark?: React.FC<React.SVGProps<SVGSVGElement>>;
name: string;
description?: string;
issuerName: string;
schemaId: string;
portalId: string;
issuanceDate: number;
expiryDate?: number;
}
2 changes: 1 addition & 1 deletion explorer/src/pages/Attestations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const Attestations: React.FC = () => {

const data = isLoading
? { columns: columnsSkeletonRef.current, list: skeletonAttestations(itemsPerPage) }
: { columns: columns({ chain: network.chain }), list: attestationsList || [] };
: { columns: columns({ chain: network.chain, network: network.network }), list: attestationsList || [] };

const renderPagination = () => {
if (attestationsCount) {
Expand Down
21 changes: 3 additions & 18 deletions explorer/src/pages/MyAttestations/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ 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 {
Expand Down Expand Up @@ -94,27 +92,14 @@ export const MyAttestations: React.FC = () => {
<div className="flex flex-col gap-14 md:gap-[4.5rem] container mt-14 md:mt-12">
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{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 (
<AttestationCard
key={attestation.id}
id={attestation.id}
logo={attestationDefinitions?.logo ?? issuerData?.logo}
logoDark={attestationDefinitions?.logoDark ?? issuerData?.logoDark}
name={attestationDefinitions?.name ?? issuerData?.name}
description={attestationDefinitions?.description ?? ""}
issuerName={issuerData.name}
schemaId={attestation.schema.id}
portalId={attestation.portal.id}
issuanceDate={attestation.attestedDate}
expiryDate={attestation.expirationDate}
/>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { APP_ROUTES } from "@/routes/constants";
export const RecentAttestations: React.FC<{ schemaId: string }> = ({ schemaId }) => {
const {
sdk,
network: { chain },
network: { chain, network },
} = useNetworkContext();

const { data: attestations, isLoading } = useSWR(
Expand All @@ -27,7 +27,7 @@ export const RecentAttestations: React.FC<{ schemaId: string }> = ({ schemaId })
const data = isLoading
? { columns: columnsSkeletonRef.current, list: skeletonAttestations(5) }
: {
columns: columns({ sortByDate: false, chain }),
columns: columns({ sortByDate: false, chain, network }),
list: attestations || [],
};

Expand Down
21 changes: 3 additions & 18 deletions explorer/src/pages/Subject/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ 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();
Expand Down Expand Up @@ -36,27 +34,14 @@ export const Subject: React.FC = () => {
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{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 (
<AttestationCard
key={attestation.id}
id={attestation.id}
logo={attestationDefinitions?.logo ?? issuerData?.logo}
logoDark={attestationDefinitions?.logoDark ?? issuerData?.logoDark}
name={attestationDefinitions?.name ?? issuerData?.name}
description={attestationDefinitions?.description ?? ""}
issuerName={issuerData.name}
schemaId={attestation.schema.id}
portalId={attestation.portal.id}
issuanceDate={attestation.attestedDate}
expiryDate={attestation.expirationDate}
/>
);
})}
Expand Down
25 changes: 25 additions & 0 deletions explorer/src/utils/dateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,28 @@ export const parseDateTime = (inputDate: string, isSeconds = false): ParseDateTi
},
} as const;
};

export const timeElapsed = (seconds: number): string => {
const now = new Date();
const secondsElapsed = Math.floor(now.getTime() / 1000) - seconds;

const minutes = Math.floor(secondsElapsed / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const months = Math.floor(days / 30.44);
const years = Math.floor(days / 365.25);

if (years > 0) {
return years === 1 ? "1 year ago" : `${years} years ago`;
} else if (months > 0) {
return months === 1 ? "1 month ago" : `${months} months ago`;
} else if (days > 0) {
return days === 1 ? "1 day ago" : `${days} days ago`;
} else if (hours > 0) {
return hours === 1 ? "1 hour ago" : `${hours} hours ago`;
} else if (minutes > 0) {
return minutes === 1 ? "1 minute ago" : `${minutes} minutes ago`;
} else {
return secondsElapsed === 1 ? "1 second ago" : `${secondsElapsed} seconds ago`;
}
};
Loading

0 comments on commit e2f0195

Please sign in to comment.