diff --git a/src/app/service-providers/data/column-definition.tsx b/src/app/service-providers/data/column-definition.tsx index 921e6fcd..185e2c5e 100644 --- a/src/app/service-providers/data/column-definition.tsx +++ b/src/app/service-providers/data/column-definition.tsx @@ -1,9 +1,9 @@ import { ID } from '@filecoin-foundation/ui-filecoin/Table/ID' import { YesNoStatus } from '@filecoin-foundation/ui-filecoin/Table/YesNoStatus' -// import { ExternalTextLink } from '@filecoin-foundation/ui-filecoin/TextLink/ExternalTextLink' import { createColumnHelper } from '@tanstack/react-table' import { CompactPeerID } from '@/components/CompactPeerID' +import { PdpScanLink } from '@/components/PdpScanLink' import { ProviderOverview } from '@/components/ProviderOverview' import { SoftwareVersion } from '@/components/SoftwareVersion' @@ -52,15 +52,16 @@ export const columns = [ sortingFn: sortSoftwareVersion, sortUndefined: 'last', }), - // TODO: Add check activity link - // columnHelper.accessor('checkActivityUrl', { - // header: 'Check Activity', - // cell: (info) => ( - // - // View on PDP Scan - // - // ), - // }), + columnHelper.accessor('checkActivityUrl', { + header: 'Check Activity', + cell: (info) => ( + + ), + enableSorting: false, + }), // TODO: Add Service Offered Column columnHelper.accessor('serviceStatus', { header: 'Status', diff --git a/src/app/warm-storage-service/data/column-definition.tsx b/src/app/warm-storage-service/data/column-definition.tsx index 5bb0a2be..073deec8 100644 --- a/src/app/warm-storage-service/data/column-definition.tsx +++ b/src/app/warm-storage-service/data/column-definition.tsx @@ -1,8 +1,8 @@ import { ID } from '@filecoin-foundation/ui-filecoin/Table/ID' -// import { ExternalTextLink } from '@filecoin-foundation/ui-filecoin/TextLink/ExternalTextLink' import { createColumnHelper } from '@tanstack/react-table' import { CompactPeerID } from '@/components/CompactPeerID' +import { PdpScanLink } from '@/components/PdpScanLink' import { ProviderOverview } from '@/components/ProviderOverview' import { SoftwareVersion } from '@/components/SoftwareVersion' @@ -45,15 +45,16 @@ export const columns = [ sortingFn: sortSoftwareVersion, sortUndefined: 'last', }), - // TODO: Add check activity link - // columnHelper.accessor('checkActivityUrl', { - // header: 'Check Activity', - // cell: (info) => ( - // - // View on PDP Scan - // - // ), - // }), + columnHelper.accessor('checkActivityUrl', { + header: 'Check Activity', + cell: (info) => ( + + ), + enableSorting: false, + }), columnHelper.accessor('location', { header: 'Location', cell: (info) => info.getValue(), diff --git a/src/components/PdpScanLink.tsx b/src/components/PdpScanLink.tsx new file mode 100644 index 00000000..f1fb3332 --- /dev/null +++ b/src/components/PdpScanLink.tsx @@ -0,0 +1,17 @@ +import { ExternalTextLink } from '@filecoin-foundation/ui-filecoin/TextLink/ExternalTextLink' + +type PdpScanLinkProps = { + pdpScanUrl: string + providerName: string +} + +export function PdpScanLink({ pdpScanUrl, providerName }: PdpScanLinkProps) { + return ( + + View on PDP Scan + + ) +} diff --git a/src/schemas/provider-schema.ts b/src/schemas/provider-schema.ts index 151e07d1..25b3e075 100644 --- a/src/schemas/provider-schema.ts +++ b/src/schemas/provider-schema.ts @@ -9,7 +9,7 @@ const ethereumAddressSchema = z.custom
( export const providerSchema = z.object({ capabilityKeys: z.array(z.string()), capacityTb: z.bigint().optional(), - // checkActivityUrl: z.url().optional(), // TODO: Remove optional + checkActivityUrl: z.url(), description: z.string(), id: z.number(), ipniIpfs: z.boolean(), diff --git a/src/services/providers/contract.ts b/src/services/providers/contract.ts index 6686e824..21c34374 100644 --- a/src/services/providers/contract.ts +++ b/src/services/providers/contract.ts @@ -4,7 +4,7 @@ import type { ServiceRegistryABI, WarmStorageViewABI } from '@/config/abis' import { BATCH_SIZE, PDP_PRODUCT_TYPE, PROVIDER_FETCH_LIMIT } from './constants' import { processProviderData } from './processor' -import type { ProviderWithoutSoftwareVersion } from './types' +import type { BaseProviderData } from './types' /** * Fetch approved provider IDs from WarmStorageView contract with pagination @@ -50,8 +50,8 @@ export async function fetchProvidersBulk( >, onlyActive: boolean, approvedProviderIds?: Set, -): Promise { - const providers: ProviderWithoutSoftwareVersion[] = [] +): Promise { + const providers: BaseProviderData[] = [] let offset = 0 let hasMore = true @@ -117,7 +117,7 @@ export async function fetchProviderById( PublicClient >, isApproved = false, -): Promise { +): Promise { const providerData = await serviceRegistryContract.read.getProviderWithProduct([ providerId, diff --git a/src/services/providers/index.ts b/src/services/providers/index.ts index 2cfcc9b8..44bded9a 100644 --- a/src/services/providers/index.ts +++ b/src/services/providers/index.ts @@ -12,6 +12,7 @@ import { type ServiceProvider, } from '@/schemas/provider-schema' import type { FetchProvidersOptions, ProviderFilter } from '@/types/providers' +import { getCheckActivityUrl } from '@/utils/provider-urls' import { VERSION_FETCH_CONCURRENCY } from './constants' import { @@ -19,7 +20,7 @@ import { fetchProviderById, fetchProvidersBulk, } from './contract' -import type { ProviderWithoutSoftwareVersion } from './types' +import type { BaseProviderData } from './types' import { fetchSoftwareVersion } from './version' /** @@ -34,7 +35,7 @@ async function fetchProvidersByFilter( PublicClient > }, -): Promise { +): Promise { const { storageView, serviceRegistry } = contracts // Fetch approved provider IDs for marking providers @@ -70,8 +71,7 @@ async function fetchProvidersByFilter( const providers = await Promise.all(providerPromises) return providers.filter( - (provider): provider is ProviderWithoutSoftwareVersion => - provider !== null, + (provider): provider is BaseProviderData => provider !== null, ) } @@ -79,14 +79,13 @@ async function fetchProvidersByFilter( } /** - * Enrich providers with software version information + * Enrich providers with additional information (software version and check activity URL) */ -async function enrichProvidersWithVersions( - providers: ProviderWithoutSoftwareVersion[], +async function enrichProviders( + providers: BaseProviderData[], + network: Network, ): Promise { - const providersWithVersions: Array< - ProviderWithoutSoftwareVersion & { softwareVersion?: string } - > = [] + const providersWithVersions: ServiceProvider[] = [] // Process providers in batches for (let i = 0; i < providers.length; i += VERSION_FETCH_CONCURRENCY) { @@ -94,7 +93,11 @@ async function enrichProvidersWithVersions( const batchResults = await Promise.all( batch.map(async (provider) => { const softwareVersion = await fetchSoftwareVersion(provider.serviceUrl) - return { ...provider, softwareVersion } + const checkActivityUrl = getCheckActivityUrl( + network, + provider.payeeAddress, + ) + return { ...provider, softwareVersion, checkActivityUrl } }), ) providersWithVersions.push(...batchResults) @@ -178,6 +181,6 @@ export async function fetchProviders( serviceRegistry, }) - // Enrich with software versions - return enrichProvidersWithVersions(fetchedProviders) + // Enrich with additional information + return enrichProviders(fetchedProviders, network) } diff --git a/src/services/providers/processor.ts b/src/services/providers/processor.ts index 83968523..ea6d1cb6 100644 --- a/src/services/providers/processor.ts +++ b/src/services/providers/processor.ts @@ -5,7 +5,7 @@ import type { ProviderData } from '@/types/contract-types' import { decodePDPCapabilities } from '@/utils/decode-pdp-capabilities' import { parseLocation } from '@/utils/parse-location' -import type { ProviderWithoutSoftwareVersion } from './types' +import type { BaseProviderData } from './types' const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' @@ -40,7 +40,7 @@ function buildCapabilities( export function processProviderData( providerData: ProviderData, isApproved = false, -): ProviderWithoutSoftwareVersion | undefined { +): BaseProviderData | undefined { const { providerId, providerInfo, product, productCapabilityValues } = providerData diff --git a/src/services/providers/types.ts b/src/services/providers/types.ts index ddbe67ed..45f8c700 100644 --- a/src/services/providers/types.ts +++ b/src/services/providers/types.ts @@ -1,9 +1,9 @@ import type { ServiceProvider } from '@/schemas/provider-schema' /** - * Provider without software version (before version fetch) + * Base provider data from contract (before enrichment with software version and check activity URL) */ -export type ProviderWithoutSoftwareVersion = Omit< +export type BaseProviderData = Omit< ServiceProvider, - 'softwareVersion' + 'softwareVersion' | 'checkActivityUrl' > diff --git a/src/utils/provider-urls.ts b/src/utils/provider-urls.ts new file mode 100644 index 00000000..1851d110 --- /dev/null +++ b/src/utils/provider-urls.ts @@ -0,0 +1,18 @@ +import type { Address } from 'viem' + +import type { Network } from '@/config/chains' +import { EXPLORERS } from '@/constants/external-services' + +/** + * Constructs the check activity URL for a provider on PDP Scan + * + * @param network - The network (mainnet or calibration) + * @param payeeAddress - The provider's payee address + * @returns The full URL to view provider activity on PDP Scan + */ +export function getCheckActivityUrl( + network: Network, + payeeAddress: Address, +): string { + return `${EXPLORERS.PDP_SCAN[network]}${payeeAddress}` +}