feat: SolaceNet gateway rails, IRU marketplace hardening, and docs

- Gateway adapter registry, rails routes, optional SOLACENET_GATEWAY_RAILS_ENFORCE; HTTP integration tests.
- IRU marketplace: rate limits, public routes, notifications/SMTP env docs; marketplace UI constants and flows.
- Quantum proxy legacy protocol types; debank/tezos/GSDS touch-ups; .env.example operator notes.
- SolaceNet doc set (gaps, runbooks, telecom schema example).

Tests: npm run test:iru-marketplace, npm run test:gateway (pass).
Note: full-repo tsc still reports unrelated legacy errors outside this change set.
Made-with: Cursor
This commit is contained in:
defiQUG
2026-04-07 23:21:55 -07:00
parent 1190476b0a
commit 6ebf71dda8
75 changed files with 4104 additions and 338 deletions

View File

@@ -0,0 +1,30 @@
/**
* Sankofa Marketplace — product and vendor labels.
* SolaceNet is the commercial name for the IRU offering line; other products may be added over time.
*/
import { OfferMetadata } from '@/constants/offerTaxonomy';
export {
BILLING_MODE_LABELS,
COMMERCIAL_MODEL_LABELS,
FULFILLMENT_MODE_LABELS,
OFFER_STATUS_LABELS,
OFFER_TYPE_LABELS,
SUPPORT_OWNER_LABELS,
} from '@/constants/offerTaxonomy';
export const MARKETPLACE_NAME = 'Sankofa Phoenix Marketplace';
export const SOLACENET_PRODUCT_NAME = 'SolaceNet';
export const SOLACENET_VENDOR_NAME = 'Solace Bank Group PLC';
/** Legal / technical term shown alongside the product name */
export const SOLACENET_IRU_LABEL = 'Irrevocable Right of Use (IRU)';
export const SOLACENET_OFFER_METADATA: OfferMetadata = {
offerType: 'partner',
commercialModel: 'IRU',
supportOwner: 'partner',
fulfillmentMode: 'request_only',
billingMode: 'contract',
status: 'request_only',
};

View File

@@ -0,0 +1,57 @@
export type OfferType = 'native' | 'partner';
export type CommercialModel =
| 'IRU'
| 'SaaS'
| 'managed_service'
| 'reserved_capacity'
| 'custom';
export type SupportOwner = 'sankofa' | 'partner' | 'shared';
export type FulfillmentMode = 'self_service' | 'request_only' | 'operator_provisioned';
export type BillingMode = 'subscription' | 'contract' | 'quote';
export type OfferStatus = 'active' | 'preview' | 'request_only';
export interface OfferMetadata {
offerType: OfferType;
commercialModel: CommercialModel;
supportOwner: SupportOwner;
fulfillmentMode: FulfillmentMode;
billingMode: BillingMode;
status: OfferStatus;
}
export const OFFER_TYPE_LABELS: Record<OfferType, string> = {
native: 'Native offer',
partner: 'Partner offer',
};
export const COMMERCIAL_MODEL_LABELS: Record<CommercialModel, string> = {
IRU: 'IRU',
SaaS: 'SaaS',
managed_service: 'Managed service',
reserved_capacity: 'Reserved capacity',
custom: 'Custom commercial model',
};
export const SUPPORT_OWNER_LABELS: Record<SupportOwner, string> = {
sankofa: 'Sankofa support',
partner: 'Partner support',
shared: 'Shared support',
};
export const FULFILLMENT_MODE_LABELS: Record<FulfillmentMode, string> = {
self_service: 'Self-service',
request_only: 'Request only',
operator_provisioned: 'Operator provisioned',
};
export const BILLING_MODE_LABELS: Record<BillingMode, string> = {
subscription: 'Subscription billing',
contract: 'Contract billing',
quote: 'Quote-based billing',
};
export const OFFER_STATUS_LABELS: Record<OfferStatus, string> = {
active: 'Active',
preview: 'Preview',
request_only: 'Request only',
};

View File

@@ -1,8 +1,8 @@
// Agreement Viewer Component
// Preview and e-signature for IRU Agreement
// SolaceNet (IRU) agreement preview / e-signature
import React, { useState, useEffect } from 'react';
import { apiClient } from '@/services/api/client';
import { SOLACENET_PRODUCT_NAME, SOLACENET_VENDOR_NAME } from '@/constants/marketplace';
interface AgreementViewerProps {
agreementId?: string;
@@ -27,7 +27,7 @@ export const AgreementViewer: React.FC<AgreementViewerProps> = ({
// For now, use placeholder
setLoading(false);
setAgreement({
content: 'IRU Participation Agreement content will be loaded here...',
content: `${SOLACENET_PRODUCT_NAME} participation agreement content will be loaded here...`,
status: 'draft',
});
}, [agreementId, subscriptionId]);
@@ -74,8 +74,9 @@ export const AgreementViewer: React.FC<AgreementViewerProps> = ({
<div className="min-h-screen bg-gray-50 py-8">
<div className="container mx-auto px-4 max-w-4xl">
<div className="bg-white rounded-lg shadow-lg p-8">
<p className="text-sm text-gray-500 mb-2">{SOLACENET_VENDOR_NAME}</p>
<h2 className="text-3xl font-bold mb-6 text-gray-800">
IRU Participation Agreement
{SOLACENET_PRODUCT_NAME} participation agreement
</h2>
{agreement?.status && (

View File

@@ -1,8 +1,8 @@
// Checkout Flow Component
// Subscription and payment flow for IRU
// Checkout — SolaceNet (IRU) subscription flow
import React, { useState } from 'react';
import { apiClient } from '@/services/api/client';
import { SOLACENET_PRODUCT_NAME, SOLACENET_VENDOR_NAME } from '@/constants/marketplace';
interface CheckoutFlowProps {
subscriptionId?: string;
@@ -86,7 +86,8 @@ export const CheckoutFlow: React.FC<CheckoutFlowProps> = ({
<div className="bg-gray-50 p-4 rounded">
<p className="text-gray-600">Offering ID: {offeringId}</p>
<p className="text-gray-600 mt-2">
Please review the IRU Participation Agreement before proceeding.
Please review the {SOLACENET_PRODUCT_NAME} participation agreement (
{SOLACENET_VENDOR_NAME}) before proceeding.
</p>
</div>
<button

View File

@@ -1,9 +1,19 @@
// IRU Offerings Page
// Catalog view with filtering
// SolaceNet (IRU) catalog — Sankofa Marketplace; other products may be listed elsewhere
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { apiClient } from '@/services/api/client';
import {
BILLING_MODE_LABELS,
COMMERCIAL_MODEL_LABELS,
FULFILLMENT_MODE_LABELS,
OFFER_TYPE_LABELS,
SOLACENET_OFFER_METADATA,
SOLACENET_PRODUCT_NAME,
SOLACENET_VENDOR_NAME,
SOLACENET_IRU_LABEL,
SUPPORT_OWNER_LABELS,
} from '@/constants/marketplace';
interface MarketplaceOffering {
id: string;
@@ -100,7 +110,37 @@ export const IRUOfferings: React.FC = () => {
<div className="min-h-screen bg-gray-50 py-8">
<div className="container mx-auto px-4">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-4 text-gray-800">IRU Offerings</h1>
<p className="text-sm font-medium text-gray-500 uppercase tracking-wide mb-1">
{SOLACENET_VENDOR_NAME}
</p>
<h1 className="text-3xl font-bold mb-2 text-gray-800">
{SOLACENET_PRODUCT_NAME}{' '}
<span className="text-xl font-normal text-gray-600">({SOLACENET_IRU_LABEL})</span>
</h1>
<p className="text-gray-600 mb-4">
One product family on Sankofa Marketplace additional offerings from other vendors may be
available separately.
</p>
<div className="mb-4 flex flex-wrap gap-2">
<span className="rounded-full bg-blue-50 px-3 py-1 text-sm text-blue-700">
{OFFER_TYPE_LABELS[SOLACENET_OFFER_METADATA.offerType]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{COMMERCIAL_MODEL_LABELS[SOLACENET_OFFER_METADATA.commercialModel]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{FULFILLMENT_MODE_LABELS[SOLACENET_OFFER_METADATA.fulfillmentMode]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{SUPPORT_OWNER_LABELS[SOLACENET_OFFER_METADATA.supportOwner]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{BILLING_MODE_LABELS[SOLACENET_OFFER_METADATA.billingMode]}
</span>
</div>
<p className="text-sm text-gray-600">
IRU remains the commercial model for this partner offering line. It does not define a separate marketplace category.
</p>
{/* Filters */}
<div className="bg-white rounded-lg shadow p-4 mb-6">

View File

@@ -1,8 +1,14 @@
// Inquiry Form Component
// Form for submitting initial IRU inquiry
// Inquiry form for SolaceNet (IRU) — Solace Bank Group PLC on Sankofa Marketplace
import React, { useState } from 'react';
import { apiClient } from '@/services/api/client';
import {
COMMERCIAL_MODEL_LABELS,
FULFILLMENT_MODE_LABELS,
SOLACENET_OFFER_METADATA,
SOLACENET_PRODUCT_NAME,
SOLACENET_VENDOR_NAME,
} from '@/constants/marketplace';
interface InquiryFormProps {
offeringId: string;
@@ -79,12 +85,13 @@ export const InquiryForm: React.FC<InquiryFormProps> = ({
return (
<div className="text-center py-8">
<div className="text-green-600 text-5xl mb-4"></div>
<h3 className="text-2xl font-semibold mb-2 text-gray-800">Inquiry Submitted Successfully</h3>
<h3 className="text-2xl font-semibold mb-2 text-gray-800">Inquiry submitted</h3>
<p className="text-gray-600 mb-4">
You will receive an acknowledgment within 24 hours.
Thank you for your interest in {SOLACENET_PRODUCT_NAME}. {SOLACENET_VENDOR_NAME} will follow up
within 24 hours.
</p>
<p className="text-sm text-gray-500">
We'll review your inquiry and contact you with next steps.
We will review your inquiry and contact you with next steps.
</p>
</div>
);
@@ -92,6 +99,14 @@ export const InquiryForm: React.FC<InquiryFormProps> = ({
return (
<form onSubmit={handleSubmit} className="space-y-4">
<p className="text-sm text-gray-500">
This inquiry is for <strong>{SOLACENET_PRODUCT_NAME}</strong>, offered by {SOLACENET_VENDOR_NAME}{' '}
on Sankofa Marketplace.
</p>
<p className="text-sm text-gray-500">
Commercial model: <strong>{COMMERCIAL_MODEL_LABELS[SOLACENET_OFFER_METADATA.commercialModel]}</strong>.
Fulfillment: <strong>{FULFILLMENT_MODE_LABELS[SOLACENET_OFFER_METADATA.fulfillmentMode]}</strong>.
</p>
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
{error}

View File

@@ -4,6 +4,18 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { apiClient } from '@/services/api/client';
import {
BILLING_MODE_LABELS,
COMMERCIAL_MODEL_LABELS,
FULFILLMENT_MODE_LABELS,
MARKETPLACE_NAME,
OFFER_TYPE_LABELS,
SOLACENET_PRODUCT_NAME,
SOLACENET_OFFER_METADATA,
SOLACENET_VENDOR_NAME,
SOLACENET_IRU_LABEL,
SUPPORT_OWNER_LABELS,
} from '@/constants/marketplace';
interface MarketplaceOffering {
id: string;
@@ -84,22 +96,65 @@ export const MarketplaceHome: React.FC = () => {
{/* Hero Section */}
<div className="bg-gradient-to-r from-blue-600 to-indigo-700 text-white py-20">
<div className="container mx-auto px-4">
<h1 className="text-4xl md:text-5xl font-bold mb-4">
Sankofa Phoenix Marketplace
</h1>
<p className="text-xl md:text-2xl mb-8 text-blue-100">
Digital Bank of International Settlements - IRU Offerings
<h1 className="text-4xl md:text-5xl font-bold mb-4">{MARKETPLACE_NAME}</h1>
<p className="text-xl md:text-2xl mb-4 text-blue-100">
Multiple sovereign-grade offerings one trusted venue
</p>
<p className="text-lg text-blue-100 max-w-3xl">
Discover and subscribe to Irrevocable Right of Use (IRU) offerings for financial
infrastructure and SaaS services. Designed for Central Banks, Settlement Banks,
Commercial Banks, DFIs, and Special Entities.
<p className="text-lg text-blue-50 max-w-3xl mb-6">
Explore products from leading vendors. Below you can browse{' '}
<span className="font-semibold text-white">{SOLACENET_PRODUCT_NAME}</span> (
{SOLACENET_IRU_LABEL}) from <span className="font-semibold text-white">{SOLACENET_VENDOR_NAME}</span>
a partner program offered through Sankofa Marketplace for Central Banks, Settlement Banks, Commercial Banks, DFIs, and
Special Entities.
</p>
<div className="flex flex-wrap gap-2">
<span className="rounded-full bg-white/15 px-3 py-1 text-sm">
{OFFER_TYPE_LABELS[SOLACENET_OFFER_METADATA.offerType]}
</span>
<span className="rounded-full bg-white/15 px-3 py-1 text-sm">
{COMMERCIAL_MODEL_LABELS[SOLACENET_OFFER_METADATA.commercialModel]}
</span>
<span className="rounded-full bg-white/15 px-3 py-1 text-sm">
{FULFILLMENT_MODE_LABELS[SOLACENET_OFFER_METADATA.fulfillmentMode]}
</span>
<span className="rounded-full bg-white/15 px-3 py-1 text-sm">
{SUPPORT_OWNER_LABELS[SOLACENET_OFFER_METADATA.supportOwner]}
</span>
<span className="rounded-full bg-white/15 px-3 py-1 text-sm">
{BILLING_MODE_LABELS[SOLACENET_OFFER_METADATA.billingMode]}
</span>
</div>
</div>
</div>
{/* Offerings by Tier */}
{/* SolaceNet (IRU) offerings — additional marketplace products may appear here over time */}
<div className="container mx-auto px-4 py-12">
<div className="mb-10 pb-6 border-b border-gray-200">
<p className="text-sm font-medium text-gray-500 uppercase tracking-wide mb-1">
{SOLACENET_VENDOR_NAME}
</p>
<h2 className="text-2xl md:text-3xl font-bold text-gray-900">
{SOLACENET_PRODUCT_NAME}{' '}
<span className="text-lg md:text-xl font-normal text-gray-600">({SOLACENET_IRU_LABEL})</span>
</h2>
<div className="mt-3 flex flex-wrap gap-2">
<span className="rounded-full bg-blue-50 px-3 py-1 text-sm text-blue-700">
{OFFER_TYPE_LABELS[SOLACENET_OFFER_METADATA.offerType]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{COMMERCIAL_MODEL_LABELS[SOLACENET_OFFER_METADATA.commercialModel]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{FULFILLMENT_MODE_LABELS[SOLACENET_OFFER_METADATA.fulfillmentMode]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{BILLING_MODE_LABELS[SOLACENET_OFFER_METADATA.billingMode]}
</span>
</div>
<p className="mt-3 text-sm text-gray-600">
IRU is the commercial model for this partner offering line. It is not a separate marketplace type.
</p>
</div>
{Object.entries(offeringsByTier).map(([tier, tierOfferings]) => (
<div key={tier} className="mb-12">
<h2 className="text-3xl font-bold mb-6 text-gray-800">
@@ -148,7 +203,12 @@ export const MarketplaceHome: React.FC = () => {
{/* Features Section */}
<div className="bg-white py-12">
<div className="container mx-auto px-4">
<h2 className="text-3xl font-bold mb-8 text-center text-gray-800">Why Choose DBIS IRU?</h2>
<h2 className="text-3xl font-bold mb-8 text-center text-gray-800">
Why choose {SOLACENET_PRODUCT_NAME}?
</h2>
<p className="text-center text-gray-600 max-w-2xl mx-auto -mt-4 mb-8">
Offered by {SOLACENET_VENDOR_NAME} on {MARKETPLACE_NAME}.
</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="text-center">
<div className="text-4xl mb-4">🏛</div>

View File

@@ -1,10 +1,20 @@
// Offering Detail Page
// Detailed view of an IRU offering with specs and inquiry form
// Offering detail — SolaceNet (IRU) offering from Solace Bank Group PLC on Sankofa Marketplace
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { apiClient } from '@/services/api/client';
import { InquiryForm } from './InquiryForm';
import {
BILLING_MODE_LABELS,
COMMERCIAL_MODEL_LABELS,
FULFILLMENT_MODE_LABELS,
OFFER_TYPE_LABELS,
SOLACENET_OFFER_METADATA,
SOLACENET_PRODUCT_NAME,
SOLACENET_VENDOR_NAME,
SOLACENET_IRU_LABEL,
SUPPORT_OWNER_LABELS,
} from '@/constants/marketplace';
interface MarketplaceOffering {
id: string;
@@ -114,8 +124,25 @@ export const OfferingDetail: React.FC = () => {
>
Back to Marketplace
</button>
<p className="text-sm text-gray-500 mb-1">
{SOLACENET_VENDOR_NAME} · {SOLACENET_PRODUCT_NAME} ({SOLACENET_IRU_LABEL})
</p>
<h1 className="text-4xl font-bold mb-2 text-gray-800">{offering.name}</h1>
<div className="flex items-center gap-4">
<div className="mb-3 flex flex-wrap gap-2">
<span className="rounded-full bg-blue-50 px-3 py-1 text-sm text-blue-700">
{OFFER_TYPE_LABELS[SOLACENET_OFFER_METADATA.offerType]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{COMMERCIAL_MODEL_LABELS[SOLACENET_OFFER_METADATA.commercialModel]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{FULFILLMENT_MODE_LABELS[SOLACENET_OFFER_METADATA.fulfillmentMode]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{SUPPORT_OWNER_LABELS[SOLACENET_OFFER_METADATA.supportOwner]}
</span>
</div>
<div className="flex flex-wrap items-center gap-4">
<span className="bg-blue-100 text-blue-800 px-3 py-1 rounded-full text-sm font-semibold">
Tier {offering.capacityTier}: {TIER_NAMES[offering.capacityTier]}
</span>
@@ -214,11 +241,25 @@ export const OfferingDetail: React.FC = () => {
{/* Pricing Card */}
<div className="bg-white rounded-lg shadow p-6 sticky top-4">
<h2 className="text-2xl font-semibold mb-4 text-gray-800">Pricing</h2>
<p className="mb-4 text-sm text-gray-600">
This is a request-based partner program. Commercial terms are handled through qualification and agreement,
not as a separate marketplace type.
</p>
<div className="mb-4 flex flex-wrap gap-2">
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{BILLING_MODE_LABELS[SOLACENET_OFFER_METADATA.billingMode]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{FULFILLMENT_MODE_LABELS[SOLACENET_OFFER_METADATA.fulfillmentMode]}
</span>
</div>
{pricing ? (
<div className="space-y-4">
{pricing.basePrice && (
<div>
<div className="text-sm text-gray-500 mb-1">IRU Grant Fee</div>
<div className="text-sm text-gray-500 mb-1">
{SOLACENET_PRODUCT_NAME} grant fee ({SOLACENET_IRU_LABEL})
</div>
<div className="text-2xl font-bold text-blue-600">
{pricing.currency} {pricing.basePrice.toLocaleString()}
</div>
@@ -296,7 +337,9 @@ export const OfferingDetail: React.FC = () => {
<div className="bg-white rounded-lg max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="p-6">
<div className="flex justify-between items-center mb-4">
<h2 className="text-2xl font-semibold text-gray-800">Request Information</h2>
<h2 className="text-2xl font-semibold text-gray-800">
Request information {SOLACENET_PRODUCT_NAME}
</h2>
<button
onClick={() => setShowInquiryForm(false)}
className="text-gray-500 hover:text-gray-700"

View File

@@ -1,8 +1,8 @@
// IRU Management Page
// IRU lifecycle management
// SolaceNet (IRU) lifecycle — vendor: Solace Bank Group PLC
import React, { useState, useEffect } from 'react';
import { apiClient } from '@/services/api/client';
import { SOLACENET_PRODUCT_NAME, SOLACENET_VENDOR_NAME } from '@/constants/marketplace';
interface IRUManagementData {
subscriptionId: string;
@@ -30,7 +30,7 @@ export const IRUManagement: React.FC = () => {
setManagement(data.data);
}
} catch (err: any) {
setError(err.message || 'Failed to load IRU management data');
setError(err.message || `Failed to load ${SOLACENET_PRODUCT_NAME} management data`);
} finally {
setLoading(false);
}
@@ -44,7 +44,7 @@ export const IRUManagement: React.FC = () => {
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading IRU management...</p>
<p className="mt-4 text-gray-600">Loading {SOLACENET_PRODUCT_NAME}</p>
</div>
</div>
);
@@ -64,7 +64,10 @@ export const IRUManagement: React.FC = () => {
return (
<div className="min-h-screen bg-gray-50 py-8">
<div className="container mx-auto px-4">
<h1 className="text-3xl font-bold mb-8 text-gray-800">IRU Management</h1>
<p className="text-sm font-medium text-gray-500 uppercase tracking-wide mb-1">
{SOLACENET_VENDOR_NAME}
</p>
<h1 className="text-3xl font-bold mb-8 text-gray-800">{SOLACENET_PRODUCT_NAME} management</h1>
{management.length > 0 ? (
<div className="space-y-6">
@@ -142,7 +145,7 @@ export const IRUManagement: React.FC = () => {
</div>
) : (
<div className="bg-white rounded-lg shadow p-8 text-center">
<p className="text-gray-600 text-lg">No IRU subscriptions found.</p>
<p className="text-gray-600 text-lg">No {SOLACENET_PRODUCT_NAME} subscriptions found.</p>
</div>
)}
</div>

View File

@@ -1,9 +1,16 @@
// Participant Dashboard
// Main dashboard for IRU participants
// Participant dashboard — SolaceNet (IRU) subscription via Sankofa Marketplace
import React, { useState, useEffect } from 'react';
import { apiClient } from '@/services/api/client';
import { Link } from 'react-router-dom';
import {
COMMERCIAL_MODEL_LABELS,
FULFILLMENT_MODE_LABELS,
OFFER_TYPE_LABELS,
SOLACENET_OFFER_METADATA,
SOLACENET_PRODUCT_NAME,
SOLACENET_VENDOR_NAME,
} from '@/constants/marketplace';
interface DashboardData {
subscription: any;
@@ -68,7 +75,21 @@ export const ParticipantDashboard: React.FC = () => {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Subscription Card */}
<div className="lg:col-span-2 bg-white rounded-lg shadow p-6">
<h2 className="text-2xl font-semibold mb-4 text-gray-800">IRU Subscription</h2>
<h2 className="text-2xl font-semibold mb-4 text-gray-800">
{SOLACENET_PRODUCT_NAME} subscription
</h2>
<p className="text-sm text-gray-500 mb-4">{SOLACENET_VENDOR_NAME}</p>
<div className="mb-4 flex flex-wrap gap-2">
<span className="rounded-full bg-blue-50 px-3 py-1 text-sm text-blue-700">
{OFFER_TYPE_LABELS[SOLACENET_OFFER_METADATA.offerType]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{COMMERCIAL_MODEL_LABELS[SOLACENET_OFFER_METADATA.commercialModel]}
</span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700">
{FULFILLMENT_MODE_LABELS[SOLACENET_OFFER_METADATA.fulfillmentMode]}
</span>
</div>
<div className="space-y-3">
<div>
<div className="text-sm text-gray-500">Offering</div>
@@ -99,7 +120,7 @@ export const ParticipantDashboard: React.FC = () => {
to="/portal/iru-management"
className="mt-4 inline-block text-blue-600 hover:text-blue-700"
>
Manage IRU
Manage {SOLACENET_PRODUCT_NAME}
</Link>
</div>
@@ -153,7 +174,8 @@ export const ParticipantDashboard: React.FC = () => {
<div className="bg-white rounded-lg shadow p-8 text-center">
<h2 className="text-2xl font-semibold mb-4 text-gray-800">No Active Subscription</h2>
<p className="text-gray-600 mb-6">
You don't have an active IRU subscription. Browse the marketplace to get started.
You don't have an active {SOLACENET_PRODUCT_NAME} subscription. Browse the marketplace to
explore offerings from {SOLACENET_VENDOR_NAME} and others.
</p>
<Link
to="/marketplace"