diff --git a/apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/page.tsx b/apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/page.tsx
index 7300fd6..06c1cf2 100644
--- a/apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/page.tsx
+++ b/apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/page.tsx
@@ -1,170 +1 @@
-import NotFoundPage from "@/app/not-found";
-import { ContentCard } from "@/components/ContentCard";
-import { CopyButton } from "@/components/CopyButton";
-import DEPRECATED_Typography from "@/components/core/Typography";
-import PageContent from "@/components/layout/PageContent";
-import { PageLoader } from "@/components/layout/PageLoader";
-import { TableRowItem } from "@/components/Table";
-import { TableCard } from "@/components/TableCard";
-import { completeDate } from "@/helpers/date";
-import { toTitleCase } from "@/helpers/string";
-import { useClientSDK } from "@/providers/ClientProvider";
-import { Icon, Tag, Typography, useModal } from "@keetanetwork/web-ui";
-import { useQuery$ } from "@preact-signals/query";
-import { Fragment } from "preact/jsx-runtime";
-import { twMerge } from "tailwind-merge";
-import { CertificatePemContent } from "./CertificatePemContent";
-import { ChainCertificateModal } from "./ChainCertificateModal";
-import { IconBoolean } from "./IconBoolean";
-
-export default function CertificatePage({ params: { accountPublicKey, certificateHash } }: { params: { accountPublicKey: string, certificateHash: string }}) {
- const sdk = useClientSDK();
- const { data, isError, isLoading } = useQuery$(() => ({
- queryKey: ['account', accountPublicKey, 'certificate', certificateHash],
- queryFn: () => sdk.account.certificate(accountPublicKey, certificateHash),
- }))
-
- const { openModal } = useModal();
-
- if (isError) {
- return();
- }
-
- if (!data || isLoading) {
- return();
- }
-
- const { certificate } = data;
-
- return (
-
-
-
-
-
-
- Certificate
-
-
-
-
-
-
- {certificate.hash}
-
-
-
-
-
-
- {certificate.issuerName}
-
-
-
-
-
-
-
{completeDate(certificate.expiresAt)}, type: "jsx" }
- ),
- { label: "Serial", value: `${certificate.serial.toString(16).toUpperCase()} (${certificate.serial.toString()})` },
- { label: "Trusted", value: , type: "jsx" },
- ]}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.value,
- }))}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.value,
- }))}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.sensitive ? "********" : item.value,
- }))}
- />
-
- {/* Chain of Trust */}
- (
-
-
-
- {/*
- {middleSubstring(row.hash, 4)}
- */}
- {/* */}
-
-
- {row.isSelfSigned ? (
-
- {row.issuerName}
-
-
- ) : (
- <>
-
- {row.issuerName}
-
-
- {row.subjectName}
-
- >
- )}
-
- openModal()} className={"text-functional-focused"}>View
-
-
- )}
- />
-
- {/* Certificate PEM Content */}
-
-
-
- )
-}
+export { default } from "@/components/Certificate/page"
diff --git a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/CertificatePemContent.tsx b/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/CertificatePemContent.tsx
deleted file mode 100644
index 1c8c652..0000000
--- a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/CertificatePemContent.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { twMerge } from 'tailwind-merge';
-
-import Icon from '@/components/core/Icon';
-import Typography from '@/components/core/Typography';
-
-export function CertificatePemContent({ content }: { content: string }) {
- return(
-
Certificate PEM
-
-
- {content}
-
-
-
);
-}
diff --git a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/ChainCertificateModal.tsx b/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/ChainCertificateModal.tsx
deleted file mode 100644
index 744b087..0000000
--- a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/ChainCertificateModal.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { ContentCard } from "@/components/ContentCard";
-import { completeDate } from "@/helpers/date";
-import { Button, Modal, ModalContent, ModalFooter, ModalHeader, useModal } from "@keetanetwork/web-ui";
-import { IconBoolean } from "./IconBoolean";
-import { toTitleCase } from "@/helpers/string";
-import { ExplorerClientSDK } from "@/libs/explorer-sdk";
-
-type ChainCertificateModalProps = {
- certificate: Awaited>['certificate']['chain'][number];
-};
-
-export function ChainCertificateModal({ certificate }: ChainCertificateModalProps) {
- const { closeModal } = useModal();
- return (
-
-
-
-
- , type: "jsx" },
- ]}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.value,
- }))}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.value,
- }))}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.sensitive ? "********" : item.value,
- }))}
- />
-
-
- closeModal()}>Close}
- />
-
- )
-}
diff --git a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/IconBoolean.tsx b/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/IconBoolean.tsx
deleted file mode 100644
index ce50d06..0000000
--- a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/IconBoolean.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Icon } from "@keetanetwork/web-ui";
-import { twMerge } from "tailwind-merge";
-
-export function IconBoolean({ value }: { value: boolean }) {
- return (
-
- );
-}
diff --git a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/page.tsx b/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/page.tsx
index 7300fd6..06c1cf2 100644
--- a/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/page.tsx
+++ b/apps/web/src/app/storage/[accountPublicKey]/certificate/[certificateHash]/page.tsx
@@ -1,170 +1 @@
-import NotFoundPage from "@/app/not-found";
-import { ContentCard } from "@/components/ContentCard";
-import { CopyButton } from "@/components/CopyButton";
-import DEPRECATED_Typography from "@/components/core/Typography";
-import PageContent from "@/components/layout/PageContent";
-import { PageLoader } from "@/components/layout/PageLoader";
-import { TableRowItem } from "@/components/Table";
-import { TableCard } from "@/components/TableCard";
-import { completeDate } from "@/helpers/date";
-import { toTitleCase } from "@/helpers/string";
-import { useClientSDK } from "@/providers/ClientProvider";
-import { Icon, Tag, Typography, useModal } from "@keetanetwork/web-ui";
-import { useQuery$ } from "@preact-signals/query";
-import { Fragment } from "preact/jsx-runtime";
-import { twMerge } from "tailwind-merge";
-import { CertificatePemContent } from "./CertificatePemContent";
-import { ChainCertificateModal } from "./ChainCertificateModal";
-import { IconBoolean } from "./IconBoolean";
-
-export default function CertificatePage({ params: { accountPublicKey, certificateHash } }: { params: { accountPublicKey: string, certificateHash: string }}) {
- const sdk = useClientSDK();
- const { data, isError, isLoading } = useQuery$(() => ({
- queryKey: ['account', accountPublicKey, 'certificate', certificateHash],
- queryFn: () => sdk.account.certificate(accountPublicKey, certificateHash),
- }))
-
- const { openModal } = useModal();
-
- if (isError) {
- return();
- }
-
- if (!data || isLoading) {
- return();
- }
-
- const { certificate } = data;
-
- return (
-
-
-
-
-
-
- Certificate
-
-
-
-
-
-
- {certificate.hash}
-
-
-
-
-
-
- {certificate.issuerName}
-
-
-
-
-
-
-
{completeDate(certificate.expiresAt)}, type: "jsx" }
- ),
- { label: "Serial", value: `${certificate.serial.toString(16).toUpperCase()} (${certificate.serial.toString()})` },
- { label: "Trusted", value: , type: "jsx" },
- ]}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.value,
- }))}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.value,
- }))}
- />
-
- ({
- label: toTitleCase(item.name),
- value: item.sensitive ? "********" : item.value,
- }))}
- />
-
- {/* Chain of Trust */}
- (
-
-
-
- {/*
- {middleSubstring(row.hash, 4)}
- */}
- {/* */}
-
-
- {row.isSelfSigned ? (
-
- {row.issuerName}
-
-
- ) : (
- <>
-
- {row.issuerName}
-
-
- {row.subjectName}
-
- >
- )}
-
- openModal()} className={"text-functional-focused"}>View
-
-
- )}
- />
-
- {/* Certificate PEM Content */}
-
-
-
- )
-}
+export { default } from "@/components/Certificate/page"
diff --git a/apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/CertificatePemContent.tsx b/apps/web/src/components/Certificate/CertificatePemContent.tsx
similarity index 100%
rename from apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/CertificatePemContent.tsx
rename to apps/web/src/components/Certificate/CertificatePemContent.tsx
diff --git a/apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/ChainCertificateModal.tsx b/apps/web/src/components/Certificate/ChainCertificateModal.tsx
similarity index 83%
rename from apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/ChainCertificateModal.tsx
rename to apps/web/src/components/Certificate/ChainCertificateModal.tsx
index 744b087..6241e75 100644
--- a/apps/web/src/app/account/[accountPublicKey]/certificate/[certificateHash]/ChainCertificateModal.tsx
+++ b/apps/web/src/components/Certificate/ChainCertificateModal.tsx
@@ -3,7 +3,8 @@ import { completeDate } from "@/helpers/date";
import { Button, Modal, ModalContent, ModalFooter, ModalHeader, useModal } from "@keetanetwork/web-ui";
import { IconBoolean } from "./IconBoolean";
import { toTitleCase } from "@/helpers/string";
-import { ExplorerClientSDK } from "@/libs/explorer-sdk";
+import type { ExplorerClientSDK } from "@/libs/explorer-sdk";
+import { CardContainer } from "@/components/CardContainer";
type ChainCertificateModalProps = {
certificate: Awaited>['certificate']['chain'][number];
@@ -23,7 +24,7 @@ export function ChainCertificateModal({ certificate }: ChainCertificateModalProp
content={[
{ label: "Issuer", value: certificate.issuerName },
{ label: "Subject", value: certificate.subjectName },
- { label: "Is Self-Signed", value: certificate.isSelfSigned ? "Yes" : "No" },
+ { label: "Is Root", value: certificate.isSelfSigned ? : undefined, type: "jsx" },
{ label: "Issued At", value: completeDate(certificate.issuedAt) },
{ label: certificate.valid ? "Valid Until" : "Expired On", value: completeDate(certificate.expiresAt) },
{ label: "Serial", value: `${certificate.serial.toString(16).toUpperCase()} (${certificate.serial.toString()})` },
@@ -57,6 +58,10 @@ export function ChainCertificateModal({ certificate }: ChainCertificateModalProp
value: item.sensitive ? "********" : item.value,
}))}
/>
+
+
+ {certificate.pem}
+
({
+ queryKey: ['account', accountPublicKey, 'certificate', certificateHash],
+ queryFn: () => sdk.account.certificate(accountPublicKey, certificateHash),
+ }))
+
+ const { openModal } = useModal();
+
+ if (isError) {
+ return();
+ }
+
+ if (!data || isLoading) {
+ return();
+ }
+
+ const { certificate } = data;
+
+ return (
+
+
+
+
+
+
+ Certificate
+
+
+
+
+
+
+ {certificate.hash}
+
+
+
+
+
+
+ {certificate.issuerName}
+
+
+
+
+
+
+
{completeDate(certificate.expiresAt)}, type: "jsx" }
+ ),
+ { label: "Serial", value: `${certificate.serial.toString(16).toUpperCase()} (${certificate.serial.toString()})` },
+ { label: "Trusted", value: , type: "jsx" },
+ ]}
+ />
+
+ ({
+ label: toTitleCase(item.name),
+ value: item.value,
+ }))}
+ />
+
+ ({
+ label: toTitleCase(item.name),
+ value: item.value,
+ }))}
+ />
+
+ ({
+ label: toTitleCase(item.name),
+ value: item.sensitive ? "********" : item.value,
+ }))}
+ />
+
+ {/* Chain of Trust */}
+ (
+
+
+
+ {/*
+ {middleSubstring(row.hash, 4)}
+ */}
+ {/* */}
+
+
+ {row.isSelfSigned ? (
+
+ {row.issuerName}
+
+
+ ) : (
+ <>
+
+ {row.issuerName}
+
+
+ {row.subjectName}
+
+ >
+ )}
+
+ openModal()} className={"text-functional-focused"}>View
+
+
+ )}
+ />
+
+ {/* Certificate PEM Content */}
+
+
+
+ )
+}
diff --git a/apps/web/src/components/ContentCard.tsx b/apps/web/src/components/ContentCard.tsx
index 63e8ddc..2880796 100644
--- a/apps/web/src/components/ContentCard.tsx
+++ b/apps/web/src/components/ContentCard.tsx
@@ -1,11 +1,11 @@
import { Typography } from "@keetanetwork/web-ui";
import { TextAccountLink } from "./TextAccountLink";
-import { NonNullableProps } from "@/utils/types";
-import { CardContainer, CardContainerProps } from "./CardContainer";
+import type { NonNullableProps } from "@/utils/types";
+import { CardContainer, type CardContainerProps } from "./CardContainer";
import { twMerge } from "tailwind-merge";
import { TextBlockLink } from "./TextBlockLink";
import { completeDate, completeDay } from "@/helpers/date";
-import { VNode } from "preact";
+import type { VNode } from "preact";
type ContentItemType = "text" | "account" | "block" | "datetime" | "date" | "jsx";
@@ -37,14 +37,14 @@ interface ContentItemDatetime extends ContentItemBase {
interface ContentItemJSX extends ContentItemBase {
type: Extract;
- value: VNode;
+ value: VNode | undefined;
}
export type ContentItem = ContentItemText | ContentItemAccount | ContentItemBlock | ContentItemDatetime | ContentItemJSX;
interface ContentCardProps extends CardContainerProps {
content: ContentItem[]
-};
+}
/**
* Utility function to check if a value is valid
diff --git a/apps/web/src/libs/explorer-sdk.ts b/apps/web/src/libs/explorer-sdk.ts
index 8bfcd2e..e4ea31a 100644
--- a/apps/web/src/libs/explorer-sdk.ts
+++ b/apps/web/src/libs/explorer-sdk.ts
@@ -74,6 +74,7 @@ function certificateToAPIResponse(certificate: Anchor.lib.Certificates.Certifica
issuerDN: certificate.issuerDN,
subjectDN: certificate.subjectDN,
attributes,
+ pem: certificate.toPEM(),
}) as const;
}
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index a440be6..6de2410 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -1,6 +1,6 @@
{
"name": "explorer",
- "version": "0.1.0",
+ "version": "0.1.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
@@ -16,7 +16,7 @@
"node": ">=20.18.0",
"npm": ">=9.8.1"
},
- "version": "0.1.0"
+ "version": "0.1.1"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.4",
diff --git a/package.json b/package.json
index 8b348fb..f353874 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "explorer",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "",
"author": "Keeta Token Genesis LLC",
"private": true,