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:
30
frontend/src/constants/marketplace.ts
Normal file
30
frontend/src/constants/marketplace.ts
Normal 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',
|
||||
};
|
||||
57
frontend/src/constants/offerTaxonomy.ts
Normal file
57
frontend/src/constants/offerTaxonomy.ts
Normal 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',
|
||||
};
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user