add standards registry authoring workflow

This commit is contained in:
defiQUG
2026-04-16 14:01:41 -07:00
parent 3f7cc0f854
commit 4d9a98ab03
49 changed files with 4684 additions and 127 deletions
-1
View File
@@ -5,4 +5,3 @@ const nextConfig = {
};
module.exports = nextConfig;
+8 -5
View File
@@ -7,7 +7,9 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"type-check": "tsc --noEmit"
"type-check": "tsc --noEmit",
"standards:generate": "tsx src/content/standards/generate-artifacts.ts",
"test": "vitest run"
},
"dependencies": {
"next": "^14.0.4",
@@ -26,20 +28,21 @@
"react-hook-form": "^7.49.2",
"@hookform/resolvers": "^3.3.4",
"zod": "^3.22.4",
"date-fns": "^3.0.6",
"zustand": "^4.4.7"
"date-fns": "^3.0.6"
},
"devDependencies": {
"@types/node": "^20.10.6",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"typescript": "^5.3.3",
"tsx": "^4.20.5",
"eslint": "^8.57.1",
"eslint-config-next": "^14.0.4",
"tailwindcss": "^3.4.1",
"postcss": "^8.4.33",
"autoprefixer": "^10.4.16",
"tailwindcss-animate": "^1.0.7"
"baseline-browser-mapping": "^2.10.19",
"tailwindcss-animate": "^1.0.7",
"vitest": "^1.6.1"
}
}
@@ -0,0 +1,699 @@
{
"standards": [
{
"slug": "erc-3643",
"name": "ERC-3643",
"shortName": "Compliant Tokenization",
"eyebrow": "Compliant Token Standard",
"icon": "shield",
"summary": "Issue, manage, and distribute regulated digital assets with identity-based compliance enforced directly at the token level.",
"category": "Compliance and tokenization",
"status": "Live",
"audience": "Financial institutions, issuers, tokenization platforms, and regulated RWA teams.",
"standards": [
"ERC-3643",
"ERC-20"
],
"publisher": "Tokeny",
"auditor": "Hacken",
"seo": {
"title": "ERC-3643 | The Order",
"description": "Compliant tokenization for real-world assets with identity-based compliance, lifecycle automation, and ERC-20 compatibility.",
"keywords": [
"ERC-3643",
"compliant tokenization",
"real-world assets",
"regulated digital assets"
]
},
"specification": {
"label": "EIP-3643 Specification",
"href": "https://eips.ethereum.org/EIPS/eip-3643",
"kind": "spec"
},
"docs": [
{
"label": "Tokeny ERC-3643 Overview",
"href": "https://www.erc3643.org/",
"kind": "docs"
}
],
"repos": [
{
"label": "ethereum/EIPs",
"href": "https://github.com/ethereum/EIPs",
"kind": "repo"
},
{
"label": "TokenySolutions/T-REX",
"href": "https://github.com/TokenySolutions/T-REX",
"kind": "repo"
}
],
"submissionPoints": [
{
"label": "Ethereum EIPs Repository",
"href": "https://github.com/ethereum/EIPs",
"kind": "submission"
},
{
"label": "EIP-3643 Pull Requests",
"href": "https://github.com/ethereum/EIPs/pulls",
"kind": "submission"
}
],
"compliance": {
"native": true,
"label": "Native compliance",
"summary": "Identity, eligibility, and transfer rules are enforced directly in token behavior rather than being bolted on externally.",
"enforcement": [
"Identity-aware transfer restrictions",
"Eligibility controls for holders and counterparties",
"Lifecycle rule updates during issuance and administration"
]
},
"governanceBody": "Ethereum Improvement Proposal process with ecosystem implementation led by Tokeny and related adopters.",
"maintainers": [
{
"name": "Ethereum EIPs editors",
"role": "Canonical specification governance",
"href": "https://github.com/ethereum/EIPs"
},
{
"name": "Tokeny",
"role": "Reference implementation and ecosystem steward",
"href": "https://github.com/TokenySolutions/T-REX"
}
],
"statusHistory": [
{
"date": "2021-11-09",
"title": "EIP-3643 created",
"summary": "The compliant tokenization standard was introduced through the Ethereum EIP process."
},
{
"date": "2022-01-26",
"title": "T-REX reference implementation publicized",
"summary": "Production-oriented implementation guidance and contracts became widely available through Tokeny."
},
{
"date": "2024-01-01",
"title": "Ecosystem adoption broadened",
"summary": "ERC-3643 continued to mature as a practical framework for regulated asset issuance and lifecycle management."
}
],
"implementationExamples": [
{
"label": "Tokeny T-REX reference contracts",
"summary": "Production-oriented smart contract suite implementing identity-aware compliance controls for ERC-3643 assets.",
"href": "https://github.com/TokenySolutions/T-REX"
},
{
"label": "EIP-3643 canonical specification",
"summary": "The normative source for interface behavior, goals, and compliance-oriented design expectations.",
"href": "https://eips.ethereum.org/EIPS/eip-3643"
}
],
"submissionChecklist": [
{
"label": "Canonical EIP specification reference",
"required": true,
"summary": "Point to the accepted EIP text and normative standard behavior."
},
{
"label": "Reference implementation repository",
"required": true,
"summary": "Include a public implementation repo that demonstrates how the standard is realized in production."
},
{
"label": "Compliance posture declaration",
"required": true,
"summary": "Document whether compliance is natively enforced or relies on external controls."
},
{
"label": "Submission points and governance path",
"required": true,
"summary": "Identify the repo, pull request surface, and governance venue for amendments or follow-on proposals."
}
],
"ecosystemSupport": [
{
"area": "Wallet compatibility",
"status": "Moderate",
"summary": "Leverages ERC-20 compatibility, though full compliance-aware workflows depend on supporting infrastructure."
},
{
"area": "Exchange and marketplace support",
"status": "Specialized",
"summary": "Best fit for venues prepared to handle regulated asset onboarding and rule-aware transfer flows."
},
{
"area": "Issuer administration tooling",
"status": "Strong",
"summary": "Well suited to issuer-facing workflows, lifecycle events, and identity-driven operating models."
},
{
"area": "Regulated asset operations",
"status": "Strong",
"summary": "Purpose-built for permissioned or regulated issuance where transfer and holder eligibility matter."
}
],
"comparisonTarget": "erc-20",
"featured": true,
"order": 10,
"href": "/standards/erc-3643",
"landingPage": {
"hero": {
"eyebrow": "Compliant Token Standard",
"headline": "Compliant Tokenization for Real-World Assets",
"subheadline": "ERC-3643 is the token standard for issuing, managing, and distributing regulated digital assets with identity-based compliance built into every transfer.",
"supportingCopy": "Designed for regulated markets, ERC-3643 combines token issuance, transfer control, lifecycle automation, and ecosystem compatibility in a single standard. It is built for teams bringing securities, funds, private debt, real estate, and other real-world assets onchain.",
"primaryCta": {
"label": "Explore ERC-3643",
"href": "/docs"
},
"secondaryCta": {
"label": "Compare Standards",
"href": "#comparison"
},
"tertiaryLink": {
"label": "View Documentation",
"href": "/docs"
}
},
"trustStrip": {
"label": "Standards and assurance",
"items": [
{
"label": "Standard",
"value": "ERC-3643, ERC-20 compatible"
},
{
"label": "Publisher",
"value": "Tokeny"
},
{
"label": "Auditor",
"value": "Hacken"
},
{
"label": "Category",
"value": "Compliant tokenization"
},
{
"label": "Fit",
"value": "Regulated digital assets and RWAs"
}
]
},
"valueProps": {
"headline": "Built for compliant digital asset issuance",
"intro": "ERC-3643 embeds identity, eligibility, and transfer controls directly into the token so compliance is enforced as part of the asset itself, not added as a separate layer.",
"cards": [
{
"title": "Built-In Compliance",
"copy": "Ensure only eligible participants can hold and transfer assets through identity-based controls enforced onchain."
},
{
"title": "Lifecycle Automation",
"copy": "Support issuance, restrictions, corporate actions, and ongoing administration with extensive built-in functionality."
},
{
"title": "Regulated Asset Ready",
"copy": "Well suited for securities, funds, private debt, commodities, real estate, and other tokenized real-world assets."
},
{
"title": "ERC-20 Compatibility",
"copy": "Integrates with wallets, marketplaces, exchanges, and dApps while maintaining compliance enforcement."
},
{
"title": "Configurable Rules",
"copy": "Adapt compliance policies over time to meet changing operational, legal, or market requirements."
},
{
"title": "Proven Model",
"copy": "Validated across more than 120 use cases for compliant issuance and asset management."
}
]
},
"useCases": {
"headline": "Designed for regulated and real-world asset use cases",
"intro": "ERC-3643 can be applied across a broad range of tokenized asset classes and identity-driven applications.",
"items": [
"Equities",
"Loans",
"Currencies",
"Funds and securities",
"Commodities",
"Private debt",
"Bonds",
"Real estate",
"Real-world assets",
"Identity",
"Loyalty"
],
"supportingLine": "From institutional issuance to programmable asset distribution, ERC-3643 provides a consistent compliance framework across asset classes."
},
"proofPoints": {
"headline": "Trusted for compliant tokenization",
"intro": "ERC-3643 is designed for teams that need more than simple token issuance. It provides the operational and compliance foundation required for regulated digital asset programs.",
"items": [
"More than 120 validated use cases across compliant digital asset issuance and management.",
"Over 100 built-in functions for lifecycle operations, automation, and corporate actions.",
"Identity-based compliance enforced directly at the token level.",
"Broad compatibility with exchanges, marketplaces, wallets, and dApps."
]
},
"comparison": {
"headline": "Why ERC-3643 instead of a basic token standard?",
"intro": "ERC-20 is foundational for fungible tokens, but it does not natively provide the compliance controls needed for regulated issuance. ERC-3643 extends ERC-20 compatibility with identity-aware transfer rules and operational controls built into the token.",
"columns": [
"Category",
"ERC-3643",
"ERC-20"
],
"rows": [
{
"category": "Primary purpose",
"left": "Compliant tokenization for regulated assets",
"right": "General-purpose fungible tokens"
},
{
"category": "Compliance",
"left": "Enforced onchain through identity and rules",
"right": "Typically external or manual"
},
{
"category": "Transfer restrictions",
"left": "Native",
"right": "Not native"
},
{
"category": "Eligibility controls",
"left": "Native",
"right": "Not native"
},
{
"category": "Lifecycle management",
"left": "Extensive built-in functions",
"right": "Minimal core functions"
},
{
"category": "Best fit",
"left": "Regulated assets and RWAs",
"right": "Simple token issuance"
}
],
"secondaryNote": "Compared with ERC-1400-style positioning, ERC-3643 is often easier to present as a practical, issuer-friendly standard for compliant tokenization with strong interoperability and lifecycle flexibility.",
"cta": {
"label": "View Full Comparison",
"href": "#comparison"
}
},
"faq": {
"headline": "Frequently asked questions",
"items": [
{
"question": "What is ERC-3643?",
"answer": "ERC-3643 is an Ethereum token standard for compliant tokenization. It combines ERC-20 compatibility with identity-based compliance controls that govern who can hold, transfer, and interact with tokenized assets."
},
{
"question": "How is ERC-3643 different from ERC-20?",
"answer": "ERC-20 defines the baseline behavior for fungible tokens, but it does not natively enforce compliance. ERC-3643 adds identity, eligibility, and transfer restrictions directly into token logic, making it better suited for regulated assets."
},
{
"question": "What types of assets can use ERC-3643?",
"answer": "ERC-3643 is well suited for equities, funds, securities, private debt, bonds, commodities, real estate, currencies, and other tokenized real-world assets."
},
{
"question": "Does ERC-3643 still work with the broader ecosystem?",
"answer": "Yes. ERC-3643 is designed to remain compatible with much of the broader Ethereum ecosystem, including wallets, dApps, marketplaces, and exchanges, while preserving compliance enforcement."
},
{
"question": "Who is ERC-3643 for?",
"answer": "It is particularly relevant for financial institutions, asset issuers, tokenization platforms, fund operators, and enterprises that need compliant digital asset issuance and lifecycle management."
}
]
},
"footerCta": {
"headline": "Bring regulated assets onchain with greater control",
"copy": "Use ERC-3643 to issue compliant digital assets with identity-based transfer enforcement, lifecycle automation, and compatibility across the broader digital asset ecosystem.",
"primaryCta": {
"label": "Get Started",
"href": "/contact"
},
"secondaryCta": {
"label": "Talk to Sales",
"href": "/contact"
}
}
}
},
{
"slug": "erc-20",
"name": "ERC-20",
"shortName": "Fungible Token Baseline",
"eyebrow": "Foundational Token Standard",
"icon": "coins",
"summary": "The core fungible token standard for broad wallet, exchange, and application compatibility across the Ethereum ecosystem.",
"category": "General-purpose tokenization",
"status": "Live",
"audience": "Protocol teams, issuers, exchanges, treasury teams, and builders focused on interoperability.",
"standards": [
"ERC-20"
],
"seo": {
"title": "ERC-20 | The Order",
"description": "The foundational fungible token standard for broad wallet, exchange, and application compatibility across Ethereum.",
"keywords": [
"ERC-20",
"fungible token",
"Ethereum token standard",
"token interoperability"
]
},
"specification": {
"label": "EIP-20 Specification",
"href": "https://eips.ethereum.org/EIPS/eip-20",
"kind": "spec"
},
"docs": [
{
"label": "OpenZeppelin ERC-20 Guide",
"href": "https://docs.openzeppelin.com/contracts/4.x/erc20",
"kind": "docs"
}
],
"repos": [
{
"label": "ethereum/EIPs",
"href": "https://github.com/ethereum/EIPs",
"kind": "repo"
},
{
"label": "OpenZeppelin/openzeppelin-contracts",
"href": "https://github.com/OpenZeppelin/openzeppelin-contracts",
"kind": "repo"
}
],
"submissionPoints": [
{
"label": "Ethereum EIPs Repository",
"href": "https://github.com/ethereum/EIPs",
"kind": "submission"
},
{
"label": "EIP-20 Pull Requests",
"href": "https://github.com/ethereum/EIPs/pulls",
"kind": "submission"
}
],
"compliance": {
"native": false,
"label": "External compliance required",
"summary": "ERC-20 does not natively enforce compliance. Regulated issuance typically depends on wrappers, policy layers, or additional contract systems.",
"enforcement": [
"External policy engines or offchain controls",
"Custom transfer restrictions when needed",
"Operational overlays for regulated counterparties"
]
},
"governanceBody": "Ethereum Improvement Proposal process with widespread ecosystem stewardship across libraries, wallets, and exchanges.",
"maintainers": [
{
"name": "Ethereum EIPs editors",
"role": "Canonical specification governance",
"href": "https://github.com/ethereum/EIPs"
},
{
"name": "OpenZeppelin",
"role": "Widely used implementation maintainer",
"href": "https://github.com/OpenZeppelin/openzeppelin-contracts"
}
],
"statusHistory": [
{
"date": "2015-11-19",
"title": "EIP-20 proposed",
"summary": "The fungible token interface was introduced as a standard for interoperable Ethereum assets."
},
{
"date": "2017-09-20",
"title": "EIP-20 finalized",
"summary": "ERC-20 became the canonical baseline for fungible token behavior across the ecosystem."
},
{
"date": "2024-01-01",
"title": "Baseline ecosystem standard maintained",
"summary": "ERC-20 remains the reference layer for fungible token interoperability and exchange integration."
}
],
"implementationExamples": [
{
"label": "OpenZeppelin ERC-20 implementation",
"summary": "Widely used and audited Solidity implementation for fungible token behavior.",
"href": "https://github.com/OpenZeppelin/openzeppelin-contracts"
},
{
"label": "EIP-20 canonical specification",
"summary": "Normative interface definition for fungible token balances, transfers, and approvals.",
"href": "https://eips.ethereum.org/EIPS/eip-20"
}
],
"submissionChecklist": [
{
"label": "Canonical EIP specification reference",
"required": true,
"summary": "Link the accepted EIP text as the normative basis for the registry entry."
},
{
"label": "Reference implementation repository",
"required": true,
"summary": "Provide at least one widely adopted implementation repo to anchor ecosystem usage."
},
{
"label": "Compliance dependency disclosure",
"required": true,
"summary": "State clearly that compliance is external if the token standard does not enforce it natively."
},
{
"label": "Submission and amendment path",
"required": true,
"summary": "Document the repository and PR path for future revisions to the standard."
}
],
"ecosystemSupport": [
{
"area": "Wallet compatibility",
"status": "Strong",
"summary": "Near-universal support across Ethereum wallets and custody providers."
},
{
"area": "Exchange and marketplace support",
"status": "Strong",
"summary": "Deep support across centralized and decentralized trading venues."
},
{
"area": "Issuer administration tooling",
"status": "Moderate",
"summary": "Strong for basic issuance, though advanced compliance workflows usually require add-on systems."
},
{
"area": "Regulated asset operations",
"status": "Specialized",
"summary": "Can be used as a base layer, but regulated issuance typically needs additional policy and control infrastructure."
}
],
"comparisonTarget": "erc-3643",
"featured": false,
"order": 20,
"href": "/standards/erc-20",
"landingPage": {
"hero": {
"eyebrow": "Foundational Token Standard",
"headline": "The Baseline Standard for Fungible Tokens",
"subheadline": "ERC-20 defines the core interface for fungible tokens on Ethereum and remains the foundation for interoperable token issuance across wallets, exchanges, and applications.",
"supportingCopy": "For teams that need broad ecosystem support and straightforward fungible token behavior, ERC-20 provides the common baseline. It is best suited to general-purpose token issuance and often serves as the interoperability layer beneath more specialized token frameworks.",
"primaryCta": {
"label": "Explore ERC-20",
"href": "/docs"
},
"secondaryCta": {
"label": "Compare Standards",
"href": "#comparison"
},
"tertiaryLink": {
"label": "View Documentation",
"href": "/docs"
}
},
"trustStrip": {
"label": "Standards overview",
"items": [
{
"label": "Standard",
"value": "ERC-20"
},
{
"label": "Role",
"value": "Baseline fungible token interface"
},
{
"label": "Category",
"value": "General-purpose tokenization"
},
{
"label": "Fit",
"value": "Interoperable token issuance and ecosystem compatibility"
},
{
"label": "Primary strength",
"value": "Simplicity and universal support"
}
]
},
"valueProps": {
"headline": "Optimized for interoperability and simplicity",
"intro": "ERC-20 established the common language for fungible tokens across the Ethereum ecosystem. Its strength is broad adoption, predictable behavior, and straightforward integration.",
"cards": [
{
"title": "Universal Compatibility",
"copy": "Supported across wallets, exchanges, custodians, and dApps as the baseline fungible token format."
},
{
"title": "Simple Token Logic",
"copy": "Provides a minimal and predictable interface for balances, transfers, approvals, and allowances."
},
{
"title": "Builder Friendly",
"copy": "Well understood by developers, infrastructure providers, and integrators across the Ethereum ecosystem."
},
{
"title": "Strong Liquidity Fit",
"copy": "Works naturally with trading venues, pools, bridges, and DeFi protocols designed around fungible tokens."
},
{
"title": "Composable Foundation",
"copy": "Frequently used as the compatibility layer for more specialized token models and compliance overlays."
},
{
"title": "Fast to Adopt",
"copy": "A practical choice for teams that need broad support without embedding complex policy logic directly into the token."
}
]
},
"useCases": {
"headline": "Best suited to general-purpose token issuance",
"intro": "ERC-20 is often the right choice when the goal is broad market compatibility rather than native compliance enforcement.",
"items": [
"Utility tokens",
"Stablecoins",
"Governance tokens",
"Rewards tokens",
"Exchange-listed assets",
"Protocol assets",
"Treasury instruments",
"Liquidity and settlement rails",
"Wrapped assets",
"Ecosystem incentives"
],
"supportingLine": "ERC-20 can still be used in regulated contexts, but compliance controls generally need to be added through external systems, wrappers, or custom extensions."
},
"proofPoints": {
"headline": "Why teams still start with ERC-20",
"intro": "ERC-20 remains the default standard for fungible token interoperability because it balances simplicity, widespread support, and implementation maturity.",
"items": [
"Widely supported across the Ethereum ecosystem and token infrastructure stack.",
"Simple enough to implement, audit, and integrate without specialized compliance architecture.",
"Well suited to liquid, broadly distributed token models and exchange-facing assets.",
"Acts as the reference point for comparing more specialized token standards such as ERC-3643."
]
},
"comparison": {
"headline": "When is ERC-20 enough, and when is it not?",
"intro": "ERC-20 is ideal when broad compatibility and simple fungible token behavior matter most. It becomes less suitable when regulated issuance requires native controls over identity, eligibility, and transfer permissions.",
"columns": [
"Category",
"ERC-20",
"ERC-3643"
],
"rows": [
{
"category": "Primary purpose",
"left": "General-purpose fungible tokens",
"right": "Compliant tokenization for regulated assets"
},
{
"category": "Compliance model",
"left": "Usually external, manual, or custom",
"right": "Identity-based and enforced onchain"
},
{
"category": "Transfer restrictions",
"left": "Not native",
"right": "Native"
},
{
"category": "Eligibility controls",
"left": "Not native",
"right": "Native"
},
{
"category": "Best fit",
"left": "Broad ecosystem issuance and liquidity",
"right": "Regulated assets and RWAs"
},
{
"category": "Operational complexity",
"left": "Lower baseline complexity",
"right": "Higher capability with more built-in controls"
}
],
"secondaryNote": "ERC-20 is the baseline standard. For regulated programs, it is often the starting point for comparison rather than the final architecture.",
"cta": {
"label": "View Standards Library",
"href": "/standards"
}
},
"faq": {
"headline": "Frequently asked questions",
"items": [
{
"question": "What is ERC-20?",
"answer": "ERC-20 is the standard interface for fungible tokens on Ethereum. It defines how balances, transfers, approvals, and allowances should behave so tokens can interoperate across the ecosystem."
},
{
"question": "Why is ERC-20 so widely used?",
"answer": "Its broad adoption comes from its simplicity and ecosystem support. Most wallets, exchanges, and DeFi applications are built to work with ERC-20 tokens by default."
},
{
"question": "Does ERC-20 include compliance controls?",
"answer": "Not natively. Teams that need regulated issuance or controlled transfers usually add compliance through external systems, custom extensions, or more specialized standards."
},
{
"question": "When should a team choose ERC-20?",
"answer": "ERC-20 is a strong choice when broad compatibility, liquidity integration, and straightforward token behavior matter more than embedding policy logic directly into the asset."
},
{
"question": "How does ERC-20 relate to ERC-3643?",
"answer": "ERC-3643 builds on ERC-20 compatibility while adding identity-based compliance and transfer controls. ERC-20 is the simpler baseline; ERC-3643 is the more compliance-oriented framework."
}
]
},
"footerCta": {
"headline": "Start with the right token standard for the job",
"copy": "Use ERC-20 when you need broad compatibility and simple fungible token behavior, or compare it against specialized standards when compliance and lifecycle controls are part of the requirement.",
"primaryCta": {
"label": "Explore Standards",
"href": "/standards"
},
"secondaryCta": {
"label": "Talk to Sales",
"href": "/contact"
}
}
}
}
],
"count": 2
}
@@ -0,0 +1,448 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://the-order.local/standards/schema.json",
"title": "The Order Standards Registry",
"description": "Canonical schema for standards registry entries used by the public portal.",
"type": "array",
"items": {
"$ref": "#/$defs/standardRegistryEntry"
},
"$defs": {
"standardIcon": {
"type": "string",
"enum": [
"shield",
"coins",
"scale",
"network",
"file-stack"
]
},
"standardLink": {
"type": "object",
"additionalProperties": false,
"required": [
"label",
"href",
"kind"
],
"properties": {
"label": {
"type": "string",
"minLength": 1
},
"href": {
"type": "string",
"format": "uri"
},
"kind": {
"type": "string",
"enum": [
"spec",
"repo",
"docs",
"submission"
]
}
}
},
"seo": {
"type": "object",
"additionalProperties": false,
"required": [
"title",
"description"
],
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"description": {
"type": "string",
"minLength": 1
},
"keywords": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
}
},
"complianceProfile": {
"type": "object",
"additionalProperties": false,
"required": [
"native",
"label",
"summary",
"enforcement"
],
"properties": {
"native": {
"type": "boolean"
},
"label": {
"type": "string",
"minLength": 1
},
"summary": {
"type": "string",
"minLength": 1
},
"enforcement": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
}
}
},
"maintainer": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"role"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"role": {
"type": "string",
"minLength": 1
},
"href": {
"type": "string",
"format": "uri"
}
}
},
"statusEvent": {
"type": "object",
"additionalProperties": false,
"required": [
"date",
"title",
"summary"
],
"properties": {
"date": {
"type": "string",
"minLength": 1
},
"title": {
"type": "string",
"minLength": 1
},
"summary": {
"type": "string",
"minLength": 1
}
}
},
"implementationExample": {
"type": "object",
"additionalProperties": false,
"required": [
"label",
"summary",
"href"
],
"properties": {
"label": {
"type": "string",
"minLength": 1
},
"summary": {
"type": "string",
"minLength": 1
},
"href": {
"type": "string",
"format": "uri"
}
}
},
"checklistItem": {
"type": "object",
"additionalProperties": false,
"required": [
"label",
"required",
"summary"
],
"properties": {
"label": {
"type": "string",
"minLength": 1
},
"required": {
"type": "boolean"
},
"summary": {
"type": "string",
"minLength": 1
}
}
},
"ecosystemSupport": {
"type": "object",
"additionalProperties": false,
"required": [
"area",
"status",
"summary"
],
"properties": {
"area": {
"type": "string",
"minLength": 1
},
"status": {
"type": "string",
"enum": [
"Strong",
"Moderate",
"Specialized"
]
},
"summary": {
"type": "string",
"minLength": 1
}
}
},
"marketingLink": {
"type": "object",
"additionalProperties": false,
"required": [
"label",
"href"
],
"properties": {
"label": {
"type": "string",
"minLength": 1
},
"href": {
"type": "string",
"minLength": 1
}
}
},
"standardRegistryEntry": {
"type": "object",
"additionalProperties": false,
"required": [
"slug",
"name",
"icon",
"summary",
"category",
"status",
"audience",
"standards",
"seo",
"specification",
"repos",
"submissionPoints",
"compliance",
"governanceBody",
"maintainers",
"statusHistory",
"implementationExamples",
"submissionChecklist",
"ecosystemSupport",
"order",
"href",
"landingPage"
],
"properties": {
"slug": {
"type": "string",
"minLength": 1
},
"name": {
"type": "string",
"minLength": 1
},
"shortName": {
"type": "string",
"minLength": 1
},
"eyebrow": {
"type": "string",
"minLength": 1
},
"icon": {
"$ref": "#/$defs/standardIcon"
},
"summary": {
"type": "string",
"minLength": 1
},
"category": {
"type": "string",
"minLength": 1
},
"status": {
"type": "string",
"enum": [
"Live",
"Preview"
]
},
"audience": {
"type": "string",
"minLength": 1
},
"standards": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
},
"publisher": {
"type": "string",
"minLength": 1
},
"auditor": {
"type": "string",
"minLength": 1
},
"seo": {
"$ref": "#/$defs/seo"
},
"specification": {
"$ref": "#/$defs/standardLink"
},
"docs": {
"type": "array",
"items": {
"$ref": "#/$defs/standardLink"
}
},
"repos": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/standardLink"
}
},
"submissionPoints": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/standardLink"
}
},
"compliance": {
"$ref": "#/$defs/complianceProfile"
},
"governanceBody": {
"type": "string",
"minLength": 1
},
"maintainers": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/maintainer"
}
},
"statusHistory": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/statusEvent"
}
},
"implementationExamples": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/implementationExample"
}
},
"submissionChecklist": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/checklistItem"
}
},
"ecosystemSupport": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/ecosystemSupport"
}
},
"comparisonTarget": {
"type": "string",
"minLength": 1
},
"featured": {
"type": "boolean"
},
"order": {
"type": "integer",
"minimum": 0
},
"href": {
"type": "string",
"minLength": 1
},
"landingPage": {
"type": "object",
"additionalProperties": false,
"required": [
"hero"
],
"properties": {
"hero": {
"type": "object",
"additionalProperties": false,
"required": [
"headline",
"subheadline"
],
"properties": {
"eyebrow": {
"type": "string"
},
"headline": {
"type": "string"
},
"subheadline": {
"type": "string"
},
"supportingCopy": {
"type": "string"
},
"primaryCta": {
"$ref": "#/$defs/marketingLink"
},
"secondaryCta": {
"$ref": "#/$defs/marketingLink"
},
"tertiaryLink": {
"$ref": "#/$defs/marketingLink"
}
}
}
}
}
}
}
}
}
+2 -3
View File
@@ -1,4 +1,4 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import { Card, CardContent, CardHeader, CardTitle } from '../../lib/ui-server';
export default function AboutPage(): JSX.Element {
return (
@@ -39,7 +39,7 @@ export default function AboutPage(): JSX.Element {
<span className="mr-2"></span>
<span>
<strong>eResidency:</strong> Digital residency credentials for individuals seeking
to participate in The Order's ecosystem
to participate in The Order&apos;s ecosystem
</span>
</li>
<li className="flex items-start">
@@ -113,4 +113,3 @@ export default function AboutPage(): JSX.Element {
</div>
);
}
@@ -0,0 +1,15 @@
import { NextResponse } from 'next/server';
import { getStandardBySlug } from '../../../../content/standards';
export async function GET(
_request: Request,
{ params }: { params: { slug: string } }
): Promise<NextResponse> {
const standard = getStandardBySlug(params.slug);
if (!standard) {
return NextResponse.json({ error: 'Standard not found' }, { status: 404 });
}
return NextResponse.json(standard);
}
@@ -0,0 +1,35 @@
import { NextResponse } from 'next/server';
import { getSortedStandards } from '../../../content/standards';
import { filterStandards } from '../../../content/standards/filter';
export async function GET(request: Request): Promise<NextResponse> {
const { searchParams } = new URL(request.url);
const query = searchParams.get('q')?.trim().toLowerCase() ?? '';
const category = searchParams.get('category')?.trim().toLowerCase() ?? '';
const status = searchParams.get('status')?.trim().toLowerCase() ?? '';
const featured = searchParams.get('featured');
const compliance = searchParams.get('compliance')?.trim().toLowerCase() ?? '';
const limit = Number(searchParams.get('limit') ?? '');
const standards = filterStandards(getSortedStandards(), {
q: query,
category,
status,
featured,
compliance,
limit: Number.isFinite(limit) && limit > 0 ? limit : null,
});
return NextResponse.json({
standards,
count: standards.length,
filters: {
q: query || null,
category: category || null,
status: status || null,
featured: featured ?? null,
compliance: compliance || null,
limit: Number.isFinite(limit) && limit > 0 ? limit : null,
},
});
}
@@ -0,0 +1,6 @@
import { NextResponse } from 'next/server';
import { standardsRegistryJsonSchema } from '../../../../content/standards/schema-json';
export async function GET(): Promise<NextResponse> {
return NextResponse.json(standardsRegistryJsonSchema);
}
+3 -4
View File
@@ -22,7 +22,7 @@ export default function ContactPage(): JSX.Element {
await new Promise((resolve) => setTimeout(resolve, 1000));
success('Message sent successfully!', 'We will get back to you soon.');
setFormData({ name: '', email: '', subject: '', message: '' });
} catch (err) {
} catch {
showError('Failed to send message', 'Please try again later.');
} finally {
setIsSubmitting(false);
@@ -35,14 +35,14 @@ export default function ContactPage(): JSX.Element {
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Contact Us</h1>
<p className="text-xl text-gray-600">
Have questions? We'd love to hear from you.
Have questions? We&apos;d love to hear from you.
</p>
</div>
<Card>
<CardHeader>
<CardTitle>Send us a message</CardTitle>
<CardDescription>Fill out the form below and we'll get back to you as soon as possible.</CardDescription>
<CardDescription>Fill out the form below and we&apos;ll get back to you as soon as possible.</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
@@ -118,4 +118,3 @@ export default function ContactPage(): JSX.Element {
</div>
);
}
+5 -6
View File
@@ -1,4 +1,4 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../lib/ui-server';
import Link from 'next/link';
export default function DocsPage(): JSX.Element {
@@ -8,7 +8,7 @@ export default function DocsPage(): JSX.Element {
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Documentation</h1>
<p className="text-xl text-gray-600">
Learn how to use The Order's services and APIs
Learn how to use The Order&apos;s services and APIs
</p>
</div>
@@ -26,7 +26,7 @@ export default function DocsPage(): JSX.Element {
<Link href="/apply" className="text-primary hover:underline">
application form
</Link>
. You'll need to provide personal information and identity documents.
. You&apos;ll need to provide personal information and identity documents.
</p>
</div>
<div>
@@ -43,7 +43,7 @@ export default function DocsPage(): JSX.Element {
<div>
<h3 className="font-semibold text-lg mb-2">3. Receive Credentials</h3>
<p className="text-gray-700">
Once approved, you'll receive verifiable credentials that you can use to access
Once approved, you&apos;ll receive verifiable credentials that you can use to access
services and prove your identity.
</p>
</div>
@@ -108,7 +108,7 @@ export default function DocsPage(): JSX.Element {
<Card>
<CardHeader>
<CardTitle>Support</CardTitle>
<CardDescription>Need help? We're here for you</CardDescription>
<CardDescription>Need help? We&apos;re here for you</CardDescription>
</CardHeader>
<CardContent>
<p className="text-gray-700 mb-4">
@@ -125,4 +125,3 @@ export default function DocsPage(): JSX.Element {
</div>
);
}
-6
View File
@@ -1,6 +1,5 @@
'use client';
import { useEffect } from 'react';
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
export default function Error({
@@ -10,10 +9,6 @@ export default function Error({
error: Error & { digest?: string };
reset: () => void;
}): JSX.Element {
useEffect(() => {
console.error(error);
}, [error]);
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center px-4">
<Card className="max-w-md w-full text-center">
@@ -40,4 +35,3 @@ export default function Error({
</div>
);
}
+48 -6
View File
@@ -10,6 +10,7 @@ export default function LoginPage(): JSX.Element {
const router = useRouter();
const { login } = useAuth();
const { success, error: showError } = useToast();
const isDev = process.env.NODE_ENV !== 'production';
const [formData, setFormData] = useState({
email: '',
password: '',
@@ -17,6 +18,18 @@ export default function LoginPage(): JSX.Element {
const [isLoading, setIsLoading] = useState(false);
const [loginError, setLoginError] = useState<string | null>(null);
const completeLogin = (user: {
id: string;
email: string;
name: string;
accessToken: string;
roles: string[];
}): void => {
login(user);
success('Login successful', 'Welcome back!');
router.push('/');
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
e.preventDefault();
setIsLoading(true);
@@ -33,14 +46,12 @@ export default function LoginPage(): JSX.Element {
const mockUser = {
id: 'user-123',
email: formData.email,
name: formData.email.split('@')[0],
name: formData.email.split('@')[0] || 'User',
accessToken: 'mock-access-token-' + Date.now(),
roles: ['user'],
};
login(mockUser);
success('Login successful', 'Welcome back!');
router.push('/');
completeLogin(mockUser);
} else {
throw new Error('Please enter email and password');
}
@@ -53,6 +64,18 @@ export default function LoginPage(): JSX.Element {
}
};
const handleDevSeedLogin = (role: 'admin' | 'editor' | 'user'): void => {
const mockUser = {
id: `dev-${role}`,
email: `${role}@local.dev`,
name: `Dev ${role[0]!.toUpperCase()}${role.slice(1)}`,
accessToken: `dev-token-${role}-${Date.now()}`,
roles: role === 'user' ? ['user'] : ['user', role],
};
completeLogin(mockUser);
};
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center py-12 px-4">
<Card className="w-full max-w-md">
@@ -105,7 +128,7 @@ export default function LoginPage(): JSX.Element {
</Button>
<div className="text-center text-sm text-gray-600">
Don't have an account?{' '}
Don&apos;t have an account?{' '}
<a href="/register" className="text-primary hover:underline">
Sign up
</a>
@@ -123,9 +146,28 @@ export default function LoginPage(): JSX.Element {
</Button>
</div>
</div>
{isDev ? (
<div className="mt-6 pt-6 border-t">
<p className="text-sm font-medium text-gray-700 text-center mb-4">Development quick access</p>
<div className="grid gap-2 sm:grid-cols-3">
<Button variant="outline" type="button" onClick={() => handleDevSeedLogin('admin')}>
Seed Admin
</Button>
<Button variant="outline" type="button" onClick={() => handleDevSeedLogin('editor')}>
Seed Editor
</Button>
<Button variant="outline" type="button" onClick={() => handleDevSeedLogin('user')}>
Seed User
</Button>
</div>
<p className="mt-3 text-center text-xs text-gray-500">
Use `Seed Admin` or `Seed Editor` to access `/standards/admin` locally.
</p>
</div>
) : null}
</CardContent>
</Card>
</div>
);
}
+5 -6
View File
@@ -1,5 +1,5 @@
import Link from 'next/link';
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../lib/ui-server';
export default function NotFound(): JSX.Element {
return (
@@ -11,19 +11,18 @@ export default function NotFound(): JSX.Element {
</CardHeader>
<CardContent className="space-y-4">
<p className="text-gray-600">
The page you're looking for doesn't exist or has been moved.
The page you&apos;re looking for doesn&apos;t exist or has been moved.
</p>
<div className="flex gap-4 justify-center">
<Link href="/">
<Button>Go Home</Button>
</Link>
<Button variant="outline" onClick={() => window.history.back()}>
Go Back
</Button>
<Link href="/docs">
<Button variant="outline">View Docs</Button>
</Link>
</div>
</CardContent>
</Card>
</div>
);
}
+184 -11
View File
@@ -1,21 +1,49 @@
import Link from 'next/link';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../lib/ui-server';
import { getComparisonTarget, getFeaturedStandards, getSortedStandards } from '../content/standards';
import { StandardIcon } from '../components/marketing/standard-icon';
export default function Home(): JSX.Element {
const featuredStandard = getFeaturedStandards()[0] ?? getSortedStandards()[0];
const standardsPreview = getSortedStandards().slice(0, 2);
return (
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">
<div className="min-h-screen bg-[linear-gradient(180deg,#fff_0%,#f8fafc_42%,#fff_100%)]">
<div className="container mx-auto px-4 py-16">
<div className="text-center mb-12">
<h1 className="text-5xl font-bold text-gray-900 mb-4">The Order</h1>
<p className="text-xl text-gray-600 max-w-2xl mx-auto mb-6">
Order of Military Hospitallers - Digital Identity & Governance Platform
</p>
<p className="text-lg text-gray-500 max-w-2xl mx-auto">
Apply for eResidency, verify credentials, and access our services
</p>
<div className="rounded-[2rem] border border-slate-200 bg-[radial-gradient(circle_at_top_left,_rgba(217,119,6,0.12),_transparent_30%),linear-gradient(180deg,_#fff,_#f8fafc)] px-8 py-14 shadow-sm">
<div className="max-w-3xl">
<Badge
variant="outline"
className="mb-6 border-amber-300 bg-amber-50 px-4 py-1 text-[11px] uppercase tracking-[0.24em] text-amber-900"
>
Public Portal
</Badge>
<h1 className="text-5xl font-bold text-slate-950 mb-4">The Order</h1>
<p className="text-xl text-slate-700 max-w-2xl mb-6">
Order of Military Hospitallers digital identity, standards, and governance platform.
</p>
<p className="text-lg text-slate-600 max-w-2xl">
Apply for eResidency, verify credentials, review implementation standards, and access
public documentation for regulated digital asset programs.
</p>
<div className="mt-8 flex flex-wrap gap-3">
<Link
href="/apply"
className="inline-flex items-center justify-center rounded-full bg-slate-950 px-5 py-3 text-sm font-semibold text-white transition hover:bg-slate-800"
>
Begin Application
</Link>
<Link
href="/standards"
className="inline-flex items-center justify-center rounded-full border border-slate-300 bg-white px-5 py-3 text-sm font-semibold text-slate-900 transition hover:bg-slate-50"
>
Explore Standards
</Link>
</div>
</div>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto">
<div className="mt-12 grid md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-6xl mx-auto">
<Card>
<CardHeader>
<CardTitle>Apply for eResidency</CardTitle>
@@ -88,6 +116,151 @@ export default function Home(): JSX.Element {
</CardContent>
</Card>
</div>
{featuredStandard ? (
<section className="mx-auto mt-16 max-w-6xl">
<div className="mb-8 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">
Featured standard
</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-slate-950">
Standards built for regulated digital asset programs
</h2>
</div>
<Link
href="/standards"
className="text-sm font-semibold text-slate-950 underline-offset-4 hover:underline"
>
Browse all standards
</Link>
</div>
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader className="gap-4">
<div className="flex flex-wrap items-center gap-3">
<Badge variant="outline" className="border-slate-300 bg-slate-50 text-slate-700">
{featuredStandard.status}
</Badge>
<Badge
className={
featuredStandard.compliance.native
? 'bg-emerald-600 text-white hover:bg-emerald-600'
: 'bg-slate-800 text-white hover:bg-slate-800'
}
>
{featuredStandard.compliance.native ? 'Native compliance' : 'External compliance'}
</Badge>
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 text-sm font-semibold text-slate-700">
<StandardIcon name={featuredStandard.icon} />
</span>
<span className="text-xs uppercase tracking-[0.2em] text-slate-500">
{featuredStandard.category}
</span>
</div>
<div>
<CardTitle className="text-3xl text-slate-950">{featuredStandard.name}</CardTitle>
<CardDescription className="mt-2 max-w-3xl text-base text-slate-600">
{featuredStandard.summary}
</CardDescription>
</div>
</CardHeader>
<CardContent className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(280px,0.9fr)]">
<div className="grid gap-4 sm:grid-cols-2">
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">Audience</div>
<p className="mt-2 text-sm leading-6 text-slate-800">{featuredStandard.audience}</p>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">Standards</div>
<p className="mt-2 text-sm leading-6 text-slate-800">
{featuredStandard.standards.join(', ')}
</p>
</div>
</div>
<div className="flex flex-col justify-between gap-4 rounded-2xl border border-slate-200 bg-slate-50 p-5">
<div className="space-y-2 text-sm text-slate-600">
{featuredStandard.publisher ? <p>Publisher: {featuredStandard.publisher}</p> : null}
{featuredStandard.auditor ? <p>Auditor: {featuredStandard.auditor}</p> : null}
{getComparisonTarget(featuredStandard) ? (
<p>Compare with: {getComparisonTarget(featuredStandard)?.name}</p>
) : null}
<p>Submission points: {featuredStandard.submissionPoints.length}</p>
</div>
<Link
href={featuredStandard.href}
className="inline-flex items-center text-sm font-semibold text-slate-950 underline-offset-4 hover:underline"
>
View {featuredStandard.name}
</Link>
</div>
</CardContent>
</Card>
</section>
) : null}
{standardsPreview.length > 0 ? (
<section className="mx-auto mt-10 max-w-6xl">
<div className="grid gap-6 lg:grid-cols-2">
{standardsPreview.map((standard) => (
<Card key={standard.slug} className="border-slate-200 bg-white shadow-sm">
<CardHeader className="gap-4">
<div className="flex flex-wrap items-center gap-3">
<Badge variant="outline" className="border-slate-300 bg-slate-50 text-slate-700">
{standard.status}
</Badge>
{standard.featured ? (
<Badge className="bg-slate-950 text-white hover:bg-slate-950">Featured</Badge>
) : null}
<Badge
className={
standard.compliance.native
? 'bg-emerald-600 text-white hover:bg-emerald-600'
: 'bg-slate-800 text-white hover:bg-slate-800'
}
>
{standard.compliance.native ? 'Native compliance' : 'External compliance'}
</Badge>
</div>
<div>
<CardTitle className="flex items-center gap-3 text-2xl text-slate-950">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 text-sm font-semibold text-slate-700">
<StandardIcon name={standard.icon} />
</span>
<span>{standard.name}</span>
</CardTitle>
<CardDescription className="mt-2 text-base text-slate-600">
{standard.summary}
</CardDescription>
</div>
</CardHeader>
<CardContent className="flex items-center justify-between gap-4">
<div className="space-y-2">
<p className="max-w-md text-sm leading-6 text-slate-600">{standard.audience}</p>
<p className="text-sm text-slate-500">
Submission points: {standard.submissionPoints.length}
</p>
{getComparisonTarget(standard) ? (
<Link
href={`${getComparisonTarget(standard)?.href}#comparison`}
className="inline-flex items-center text-sm font-medium text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
>
Compare with {getComparisonTarget(standard)?.name}
</Link>
) : null}
</div>
<Link
href={standard.href}
className="shrink-0 text-sm font-semibold text-slate-950 underline-offset-4 hover:underline"
>
View standard
</Link>
</CardContent>
</Card>
))}
</div>
</section>
) : null}
</div>
</div>
);
+1 -2
View File
@@ -1,4 +1,4 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import { Card, CardContent, CardHeader, CardTitle } from '../../lib/ui-server';
export default function PrivacyPage(): JSX.Element {
return (
@@ -91,4 +91,3 @@ export default function PrivacyPage(): JSX.Element {
</div>
);
}
@@ -0,0 +1,57 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { MarketingLandingPage } from '../../../components/marketing/landing-page';
import { getRelatedStandards, getSortedStandards, getStandardBySlug } from '../../../content/standards';
import { StandardRegistryDetails } from '../../../components/marketing/standard-registry-details';
import { RelatedStandards } from '../../../components/marketing/related-standards';
export function generateStaticParams(): { slug: string }[] {
return getSortedStandards().map((standard) => ({ slug: standard.slug }));
}
export function generateMetadata({
params,
}: {
params: { slug: string };
}): Metadata {
const standard = getStandardBySlug(params.slug);
if (!standard) {
return {
title: 'Standard Not Found | The Order',
};
}
return {
title: standard.seo.title,
description: standard.seo.description,
keywords: standard.seo.keywords,
openGraph: {
title: standard.seo.title,
description: standard.seo.description,
url: standard.href,
},
};
}
export default function StandardDetailPage({
params,
}: {
params: { slug: string };
}): JSX.Element {
const standard = getStandardBySlug(params.slug);
if (!standard) {
notFound();
}
const relatedStandards = getRelatedStandards(standard);
return (
<>
<StandardRegistryDetails standard={standard} />
<RelatedStandards standards={relatedStandards} />
<MarketingLandingPage {...standard.landingPage} />
</>
);
}
@@ -0,0 +1,11 @@
import type { Metadata } from 'next';
import { StandardsAdmin } from '../../../components/marketing/standards-admin';
export const metadata: Metadata = {
title: 'Standards Admin | The Order',
description: 'Authoring and validation interface for standards registry entries.',
};
export default function StandardsAdminPage(): JSX.Element {
return <StandardsAdmin />;
}
@@ -0,0 +1,13 @@
import type { Metadata } from 'next';
import { StandardsBrowser } from '../../components/marketing/standards-browser';
import { getSortedStandards } from '../../content/standards';
export const metadata: Metadata = {
title: 'Standards | The Order',
description:
'Explore standards and frameworks for compliant tokenization, regulated digital asset issuance, and real-world asset programs.',
};
export default function StandardsPage(): JSX.Element {
return <StandardsBrowser standards={getSortedStandards()} />;
}
+59 -49
View File
@@ -1,11 +1,32 @@
'use client';
import { Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
import { useQuery } from '@tanstack/react-query';
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Label } from '@the-order/ui';
import { getApiClient } from '@the-order/api-client';
export default function StatusPage(): JSX.Element {
function StatusCardShell({ children }: { children: React.ReactNode }): JSX.Element {
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto max-w-2xl px-4">{children}</div>
</div>
);
}
function StatusLoadingState(): JSX.Element {
return (
<StatusCardShell>
<Card>
<CardContent className="py-12 text-center">
<p className="text-gray-600">Loading application status...</p>
</CardContent>
</Card>
</StatusCardShell>
);
}
function StatusPageContent(): JSX.Element {
const searchParams = useSearchParams();
const applicationId = searchParams.get('id');
const apiClient = getApiClient();
@@ -21,63 +42,47 @@ export default function StatusPage(): JSX.Element {
if (!applicationId) {
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto px-4 max-w-2xl">
<Card>
<CardHeader>
<CardTitle>Application Status</CardTitle>
<CardDescription>Please provide an application ID to check status</CardDescription>
</CardHeader>
</Card>
</div>
</div>
<StatusCardShell>
<Card>
<CardHeader>
<CardTitle>Application Status</CardTitle>
<CardDescription>Please provide an application ID to check status</CardDescription>
</CardHeader>
</Card>
</StatusCardShell>
);
}
if (isLoading) {
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto px-4 max-w-2xl">
<Card>
<CardContent className="py-12 text-center">
<p className="text-gray-600">Loading application status...</p>
</CardContent>
</Card>
</div>
</div>
);
return <StatusLoadingState />;
}
if (error) {
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto px-4 max-w-2xl">
<Card>
<CardHeader>
<CardTitle>Error</CardTitle>
<CardDescription>Failed to load application status</CardDescription>
</CardHeader>
<CardContent>
<p className="text-red-600">{error instanceof Error ? error.message : 'Unknown error'}</p>
</CardContent>
</Card>
</div>
</div>
<StatusCardShell>
<Card>
<CardHeader>
<CardTitle>Error</CardTitle>
<CardDescription>Failed to load application status</CardDescription>
</CardHeader>
<CardContent>
<p className="text-red-600">{error instanceof Error ? error.message : 'Unknown error'}</p>
</CardContent>
</Card>
</StatusCardShell>
);
}
if (!application) {
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto px-4 max-w-2xl">
<Card>
<CardHeader>
<CardTitle>Application Not Found</CardTitle>
<CardDescription>No application found with the provided ID</CardDescription>
</CardHeader>
</Card>
</div>
</div>
<StatusCardShell>
<Card>
<CardHeader>
<CardTitle>Application Not Found</CardTitle>
<CardDescription>No application found with the provided ID</CardDescription>
</CardHeader>
</Card>
</StatusCardShell>
);
}
@@ -97,8 +102,7 @@ export default function StatusPage(): JSX.Element {
};
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="container mx-auto px-4 max-w-2xl">
<StatusCardShell>
<Card>
<CardHeader>
<CardTitle>Application Status</CardTitle>
@@ -163,9 +167,15 @@ export default function StatusPage(): JSX.Element {
)}
</CardContent>
</Card>
</div>
</div>
</StatusCardShell>
);
}
export default function StatusPage(): JSX.Element {
return (
<Suspense fallback={<StatusLoadingState />}>
<StatusPageContent />
</Suspense>
);
}
+2 -3
View File
@@ -1,4 +1,4 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import { Card, CardContent, CardHeader, CardTitle } from '../../lib/ui-server';
export default function TermsPage(): JSX.Element {
return (
@@ -16,7 +16,7 @@ export default function TermsPage(): JSX.Element {
</CardHeader>
<CardContent className="text-gray-700">
<p>
By accessing and using The Order's services, you accept and agree to be bound by
By accessing and using The Order&apos;s services, you accept and agree to be bound by
these Terms of Service. If you do not agree, you may not use our services.
</p>
</CardContent>
@@ -99,4 +99,3 @@ export default function TermsPage(): JSX.Element {
</div>
);
}
@@ -4,9 +4,19 @@ import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '../lib/auth';
export function AuthGuard({ children }: { children: React.ReactNode }): JSX.Element | null {
const { isAuthenticated, isLoading } = useAuth();
export function AuthGuard({
children,
requiredRoles,
}: {
children: React.ReactNode;
requiredRoles?: string[];
}): JSX.Element | null {
const { isAuthenticated, isLoading, user } = useAuth();
const router = useRouter();
const hasRequiredRole =
!requiredRoles || requiredRoles.length === 0
? true
: requiredRoles.some((role) => user?.roles?.includes(role));
useEffect(() => {
if (!isLoading && !isAuthenticated) {
@@ -14,6 +24,12 @@ export function AuthGuard({ children }: { children: React.ReactNode }): JSX.Elem
}
}, [isAuthenticated, isLoading, router]);
useEffect(() => {
if (!isLoading && isAuthenticated && !hasRequiredRole) {
router.push('/');
}
}, [hasRequiredRole, isAuthenticated, isLoading, router]);
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
@@ -26,6 +42,9 @@ export function AuthGuard({ children }: { children: React.ReactNode }): JSX.Elem
return null;
}
if (!hasRequiredRole) {
return null;
}
return <>{children}</>;
}
+5 -1
View File
@@ -39,6 +39,11 @@ export function Footer(): JSX.Element {
About
</Link>
</li>
<li>
<Link href="/standards" className="text-gray-600 hover:text-gray-900">
Standards
</Link>
</li>
<li>
<Link href="/docs" className="text-gray-600 hover:text-gray-900">
Documentation
@@ -74,4 +79,3 @@ export function Footer(): JSX.Element {
</footer>
);
}
@@ -8,6 +8,7 @@ import { useAuth } from '../lib/auth';
export function Header(): JSX.Element {
const router = useRouter();
const { isAuthenticated, user, logout } = useAuth();
const canAccessStandardsAdmin = !!user?.roles?.some((role) => role === 'admin' || role === 'editor');
const handleLogout = (): void => {
logout();
@@ -25,6 +26,9 @@ export function Header(): JSX.Element {
<Link href="/apply" className="text-gray-600 hover:text-gray-900">
Apply
</Link>
<Link href="/standards" className="text-gray-600 hover:text-gray-900">
Standards
</Link>
<Link href="/status" className="text-gray-600 hover:text-gray-900">
Status
</Link>
@@ -36,6 +40,11 @@ export function Header(): JSX.Element {
</Link>
{isAuthenticated ? (
<div className="flex items-center gap-4">
{canAccessStandardsAdmin ? (
<Link href="/standards/admin" className="text-gray-600 hover:text-gray-900">
Registry Admin
</Link>
) : null}
<span className="text-sm text-gray-600">{user?.email || user?.name}</span>
<Button variant="outline" size="sm" onClick={handleLogout}>
Logout
@@ -0,0 +1,450 @@
import type { ReactNode } from 'react';
import Link from 'next/link';
import {
Badge,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
cn,
} from '../../lib/ui-server';
export interface MarketingLink {
label: string;
href: string;
}
export interface MarketingHero {
eyebrow?: string;
headline: string;
subheadline: string;
supportingCopy?: string;
primaryCta?: MarketingLink;
secondaryCta?: MarketingLink;
tertiaryLink?: MarketingLink;
}
export interface MarketingTrustItem {
label: string;
value: string;
}
export interface MarketingTrustStrip {
label?: string;
items: MarketingTrustItem[];
}
export interface MarketingCardItem {
title: string;
copy: string;
}
export interface MarketingValueProps {
headline: string;
intro?: string;
cards: MarketingCardItem[];
}
export interface MarketingUseCases {
headline: string;
intro?: string;
items: string[];
supportingLine?: string;
}
export interface MarketingComparisonRow {
category: string;
left: string;
right: string;
}
export interface MarketingComparison {
headline: string;
intro?: string;
columns: [string, string, string];
rows: MarketingComparisonRow[];
secondaryNote?: string;
cta?: MarketingLink;
}
export interface MarketingProofPoints {
headline: string;
intro?: string;
items: string[];
}
export interface MarketingFaqItem {
question: string;
answer: string;
}
export interface MarketingFaq {
headline: string;
items: MarketingFaqItem[];
}
export interface MarketingFooterCta {
headline: string;
copy: string;
primaryCta?: MarketingLink;
secondaryCta?: MarketingLink;
}
export interface MarketingLandingPageProps {
hero: MarketingHero;
trustStrip?: MarketingTrustStrip;
valueProps?: MarketingValueProps;
useCases?: MarketingUseCases;
proofPoints?: MarketingProofPoints;
comparison?: MarketingComparison;
faq?: MarketingFaq;
footerCta?: MarketingFooterCta;
}
function SectionShell({
id,
className,
children,
}: {
id?: string;
className?: string;
children: ReactNode;
}): JSX.Element {
return (
<section id={id} className={cn('px-4 py-16 sm:px-6 lg:px-8', className)}>
<div className="mx-auto w-full max-w-6xl">{children}</div>
</section>
);
}
function SectionIntro({
headline,
intro,
align = 'left',
tone = 'default',
}: {
headline: string;
intro?: string;
align?: 'left' | 'center';
tone?: 'default' | 'inverse';
}): JSX.Element {
return (
<div className={cn('max-w-3xl', align === 'center' && 'mx-auto text-center')}>
<h2
className={cn(
'text-balance text-3xl font-semibold tracking-tight sm:text-4xl',
tone === 'default' ? 'text-slate-950' : 'text-white'
)}
>
{headline}
</h2>
{intro ? (
<p
className={cn(
'mt-4 text-pretty text-base leading-7 sm:text-lg',
tone === 'default' ? 'text-slate-600' : 'text-slate-300'
)}
>
{intro}
</p>
) : null}
</div>
);
}
function CtaLink({
href,
label,
variant = 'primary',
}: MarketingLink & {
variant?: 'primary' | 'secondary' | 'link';
}): JSX.Element {
return (
<Link
className={cn(
'inline-flex items-center justify-center rounded-full text-sm font-semibold transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
variant === 'primary' &&
'bg-slate-950 px-5 py-3 text-white hover:bg-slate-800',
variant === 'secondary' &&
'border border-slate-300 bg-white px-5 py-3 text-slate-900 hover:border-slate-400 hover:bg-slate-50',
variant === 'link' &&
'px-0 py-0 text-slate-700 underline-offset-4 hover:text-slate-950 hover:underline'
)}
href={href}
>
{label}
</Link>
);
}
export function MarketingHeroSection({
eyebrow,
headline,
subheadline,
supportingCopy,
primaryCta,
secondaryCta,
tertiaryLink,
}: MarketingHero): JSX.Element {
return (
<SectionShell className="overflow-hidden pb-12 pt-20 sm:pt-24">
<div className="relative rounded-[2rem] border border-slate-200 bg-[radial-gradient(circle_at_top_left,_rgba(217,119,6,0.12),_transparent_30%),radial-gradient(circle_at_bottom_right,_rgba(15,23,42,0.08),_transparent_35%),linear-gradient(180deg,_#fff,_#f8fafc)] px-6 py-12 shadow-sm sm:px-10 sm:py-16">
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-amber-400/60 to-transparent" />
<div className="grid gap-10 lg:grid-cols-[minmax(0,1.2fr)_minmax(280px,0.8fr)] lg:items-end">
<div className="max-w-3xl">
{eyebrow ? (
<Badge
variant="outline"
className="mb-6 border-amber-300 bg-amber-50 px-4 py-1 text-[11px] uppercase tracking-[0.24em] text-amber-900"
>
{eyebrow}
</Badge>
) : null}
<h1 className="text-balance text-4xl font-semibold tracking-tight text-slate-950 sm:text-5xl lg:text-6xl">
{headline}
</h1>
<p className="mt-6 max-w-2xl text-pretty text-lg leading-8 text-slate-700 sm:text-xl">
{subheadline}
</p>
{supportingCopy ? (
<p className="mt-5 max-w-2xl text-pretty text-base leading-7 text-slate-600">
{supportingCopy}
</p>
) : null}
<div className="mt-8 flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center">
{primaryCta ? <CtaLink {...primaryCta} variant="primary" /> : null}
{secondaryCta ? <CtaLink {...secondaryCta} variant="secondary" /> : null}
{tertiaryLink ? <CtaLink {...tertiaryLink} variant="link" /> : null}
</div>
</div>
<Card className="border-slate-200 bg-white/90 shadow-lg shadow-slate-200/70">
<CardHeader>
<CardDescription className="text-xs uppercase tracking-[0.22em] text-slate-500">
Operational focus
</CardDescription>
<CardTitle className="text-2xl text-slate-950">Designed for regulated issuance</CardTitle>
</CardHeader>
<CardContent className="space-y-4 text-sm leading-6 text-slate-600">
<div className="grid grid-cols-2 gap-4">
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-2xl font-semibold text-slate-950">120+</div>
<div className="mt-1 text-xs uppercase tracking-[0.18em] text-slate-500">Validated use cases</div>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-2xl font-semibold text-slate-950">100+</div>
<div className="mt-1 text-xs uppercase tracking-[0.18em] text-slate-500">Built-in functions</div>
</div>
</div>
<p>
Combine identity-aware transfer rules, lifecycle automation, and ERC-20 compatibility in one
implementation-ready standard.
</p>
</CardContent>
</Card>
</div>
</div>
</SectionShell>
);
}
export function MarketingTrustStripSection({ label, items }: MarketingTrustStrip): JSX.Element {
return (
<SectionShell className="py-8">
<div className="rounded-[1.5rem] border border-slate-200 bg-white px-6 py-6 shadow-sm">
<div className="flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between">
{label ? (
<div className="min-w-[200px]">
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">{label}</p>
</div>
) : null}
<dl className="grid flex-1 gap-4 sm:grid-cols-2 xl:grid-cols-5">
{items.map((item) => (
<div key={item.label} className="rounded-2xl bg-slate-50 px-4 py-4">
<dt className="text-xs uppercase tracking-[0.18em] text-slate-500">{item.label}</dt>
<dd className="mt-2 text-sm font-medium leading-6 text-slate-900">{item.value}</dd>
</div>
))}
</dl>
</div>
</div>
</SectionShell>
);
}
export function MarketingValueGridSection({ headline, intro, cards }: MarketingValueProps): JSX.Element {
return (
<SectionShell id="capabilities">
<SectionIntro headline={headline} intro={intro} />
<div className="mt-10 grid gap-6 md:grid-cols-2 xl:grid-cols-3">
{cards.map((card) => (
<Card key={card.title} className="border-slate-200 bg-white shadow-sm transition-transform hover:-translate-y-0.5">
<CardHeader>
<CardTitle className="text-xl text-slate-950">{card.title}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm leading-7 text-slate-600">{card.copy}</p>
</CardContent>
</Card>
))}
</div>
</SectionShell>
);
}
export function MarketingUseCasesSection({ headline, intro, items, supportingLine }: MarketingUseCases): JSX.Element {
return (
<SectionShell id="use-cases" className="bg-slate-950">
<SectionIntro headline={headline} intro={intro} tone="inverse" />
<div className="mt-10 flex flex-wrap gap-3">
{items.map((item) => (
<span
key={item}
className="rounded-full border border-white/15 bg-white/5 px-4 py-2 text-sm font-medium text-slate-100 backdrop-blur"
>
{item}
</span>
))}
</div>
{supportingLine ? <p className="mt-6 max-w-3xl text-sm leading-7 text-slate-300">{supportingLine}</p> : null}
</SectionShell>
);
}
export function MarketingProofPointsSection({ headline, intro, items }: MarketingProofPoints): JSX.Element {
return (
<SectionShell id="proof-points">
<div className="grid gap-8 lg:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)] lg:items-start">
<SectionIntro headline={headline} intro={intro} />
<div className="grid gap-4">
{items.map((item, index) => (
<Card key={item} className="border-slate-200 bg-slate-50 shadow-none">
<CardContent className="flex gap-4 p-6">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-white text-sm font-semibold text-slate-950 shadow-sm">
{index + 1}
</div>
<p className="text-sm leading-7 text-slate-700">{item}</p>
</CardContent>
</Card>
))}
</div>
</div>
</SectionShell>
);
}
export function MarketingComparisonSection({
headline,
intro,
columns,
rows,
secondaryNote,
cta,
}: MarketingComparison): JSX.Element {
return (
<SectionShell id="comparison">
<SectionIntro headline={headline} intro={intro} />
<div className="mt-10 overflow-hidden rounded-[1.5rem] border border-slate-200 bg-white shadow-sm">
<Table>
<TableHeader>
<TableRow className="bg-slate-50 hover:bg-slate-50">
<TableHead>{columns[0]}</TableHead>
<TableHead>{columns[1]}</TableHead>
<TableHead>{columns[2]}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row) => (
<TableRow key={row.category}>
<TableCell className="font-medium text-slate-950">{row.category}</TableCell>
<TableCell className="text-slate-700">{row.left}</TableCell>
<TableCell className="text-slate-600">{row.right}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
{secondaryNote ? <p className="mt-5 max-w-4xl text-sm leading-7 text-slate-600">{secondaryNote}</p> : null}
{cta ? (
<div className="mt-8">
<CtaLink {...cta} variant="secondary" />
</div>
) : null}
</SectionShell>
);
}
export function MarketingFaqSection({ headline, items }: MarketingFaq): JSX.Element {
return (
<SectionShell id="faq" className="bg-slate-50">
<SectionIntro headline={headline} align="center" />
<div className="mx-auto mt-10 grid max-w-4xl gap-4">
{items.map((item) => (
<details
key={item.question}
className="group rounded-[1.25rem] border border-slate-200 bg-white px-6 py-5 shadow-sm open:border-slate-300"
>
<summary className="flex cursor-pointer list-none items-center justify-between gap-4 text-left text-base font-semibold text-slate-950">
<span>{item.question}</span>
<span className="text-slate-400 transition group-open:rotate-45">+</span>
</summary>
<p className="mt-4 max-w-3xl text-sm leading-7 text-slate-600">{item.answer}</p>
</details>
))}
</div>
</SectionShell>
);
}
export function MarketingFooterCtaSection({
headline,
copy,
primaryCta,
secondaryCta,
}: MarketingFooterCta): JSX.Element {
return (
<SectionShell className="pt-10">
<div className="rounded-[2rem] border border-slate-200 bg-slate-950 px-6 py-12 sm:px-10 sm:py-16">
<div className="mx-auto max-w-3xl text-center">
<h2 className="text-balance text-3xl font-semibold tracking-tight text-white sm:text-4xl">{headline}</h2>
<p className="mt-4 text-pretty text-base leading-7 text-slate-300 sm:text-lg">{copy}</p>
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
{primaryCta ? <CtaLink {...primaryCta} variant="primary" /> : null}
{secondaryCta ? <CtaLink {...secondaryCta} variant="secondary" /> : null}
</div>
</div>
</div>
</SectionShell>
);
}
export function MarketingLandingPage({
hero,
trustStrip,
valueProps,
useCases,
proofPoints,
comparison,
faq,
footerCta,
}: MarketingLandingPageProps): JSX.Element {
return (
<div className="min-h-screen bg-[linear-gradient(180deg,#fff_0%,#f8fafc_45%,#fff_100%)]">
<MarketingHeroSection {...hero} />
{trustStrip ? <MarketingTrustStripSection {...trustStrip} /> : null}
{valueProps ? <MarketingValueGridSection {...valueProps} /> : null}
{useCases ? <MarketingUseCasesSection {...useCases} /> : null}
{proofPoints ? <MarketingProofPointsSection {...proofPoints} /> : null}
{comparison ? <MarketingComparisonSection {...comparison} /> : null}
{faq ? <MarketingFaqSection {...faq} /> : null}
{footerCta ? <MarketingFooterCtaSection {...footerCta} /> : null}
</div>
);
}
@@ -0,0 +1,62 @@
import Link from 'next/link';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../lib/ui-server';
import type { StandardRegistryEntry } from '../../content/standards';
import { StandardIcon } from './standard-icon';
export function RelatedStandards({
standards,
}: {
standards: StandardRegistryEntry[];
}): JSX.Element | null {
if (standards.length === 0) {
return null;
}
return (
<section className="px-4 py-10 sm:px-6 lg:px-8">
<div className="mx-auto max-w-6xl">
<div className="mb-8 max-w-3xl">
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">Related standards</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-slate-950">
Compare adjacent standards in the registry
</h2>
<p className="mt-4 text-base leading-7 text-slate-600">
These standards are related by explicit comparison targets, shared categories, or overlapping standard families.
</p>
</div>
<div className="grid gap-6 lg:grid-cols-2">
{standards.map((standard) => (
<Card key={standard.slug} className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="flex items-center gap-3 text-2xl text-slate-950">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 text-slate-700">
<StandardIcon name={standard.icon} />
</span>
<span>{standard.name}</span>
</CardTitle>
<CardDescription className="text-base text-slate-600">{standard.shortName ?? standard.category}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm leading-7 text-slate-600">{standard.summary}</p>
<div className="flex flex-wrap gap-4">
<Link
href={standard.href}
className="inline-flex items-center text-sm font-semibold text-slate-950 underline-offset-4 hover:underline"
>
View standard
</Link>
<Link
href={`${standard.href}#comparison`}
className="inline-flex items-center text-sm font-medium text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
>
Jump to comparison
</Link>
</div>
</CardContent>
</Card>
))}
</div>
</div>
</section>
);
}
@@ -0,0 +1,25 @@
import { Coins, FileStack, Network, Scale, ShieldCheck } from 'lucide-react';
import type { StandardIconName } from '../../content/standards/schema';
const iconClassName = 'h-5 w-5';
export function StandardIcon({
name,
className = iconClassName,
}: {
name: StandardIconName;
className?: string;
}): JSX.Element {
switch (name) {
case 'shield':
return <ShieldCheck className={className} aria-hidden="true" />;
case 'coins':
return <Coins className={className} aria-hidden="true" />;
case 'scale':
return <Scale className={className} aria-hidden="true" />;
case 'network':
return <Network className={className} aria-hidden="true" />;
case 'file-stack':
return <FileStack className={className} aria-hidden="true" />;
}
}
@@ -0,0 +1,266 @@
import Link from 'next/link';
import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../lib/ui-server';
import type { StandardRegistryEntry } from '../../content/standards';
import { StandardIcon } from './standard-icon';
function ExternalLinkList({
title,
links,
}: {
title: string;
links: StandardRegistryEntry['repos'] | StandardRegistryEntry['submissionPoints'] | StandardRegistryEntry['docs'];
}): JSX.Element | null {
if (!links || links.length === 0) {
return null;
}
return (
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">{title}</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{links.map((link) => (
<a
key={`${link.kind}-${link.href}`}
href={link.href}
target="_blank"
rel="noreferrer"
className="block rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-700 transition hover:border-slate-300 hover:bg-slate-100"
>
<span className="font-semibold text-slate-950">{link.label}</span>
<span className="mt-1 block break-all text-xs text-slate-500">{link.href}</span>
</a>
))}
</CardContent>
</Card>
);
}
export function StandardRegistryDetails({
standard,
}: {
standard: StandardRegistryEntry;
}): JSX.Element {
return (
<section className="px-4 py-10 sm:px-6 lg:px-8">
<div className="mx-auto max-w-6xl">
<div className="mb-8 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div className="max-w-3xl">
<p className="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">Registry details</p>
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-slate-950">
Canonical metadata, compliance posture, and submission points
</h2>
<p className="mt-4 text-base leading-7 text-slate-600">
This registry entry is designed to be implementation-friendly: canonical spec links,
repository references, and the official submission points are captured directly in content.
</p>
</div>
<Link href="/standards" className="text-sm font-semibold text-slate-950 underline-offset-4 hover:underline">
Back to standards
</Link>
</div>
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]">
<div className="grid gap-6">
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<div className="flex flex-wrap items-center gap-3">
<Badge variant="outline" className="border-slate-300 bg-slate-50 text-slate-700">
{standard.status}
</Badge>
<Badge
className={standard.compliance.native ? 'bg-emerald-600 text-white hover:bg-emerald-600' : 'bg-slate-800 text-white hover:bg-slate-800'}
>
{standard.compliance.label}
</Badge>
</div>
<CardTitle className="mt-2 flex items-center gap-3 text-2xl text-slate-950">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 text-sm font-semibold text-slate-700">
<StandardIcon name={standard.icon} />
</span>
<span>{standard.name}</span>
</CardTitle>
<CardDescription className="text-base text-slate-600">{standard.summary}</CardDescription>
</CardHeader>
<CardContent className="grid gap-4 sm:grid-cols-2">
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">Category</div>
<p className="mt-2 text-sm leading-6 text-slate-800">{standard.category}</p>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">Audience</div>
<p className="mt-2 text-sm leading-6 text-slate-800">{standard.audience}</p>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">Canonical standard</div>
<a
href={standard.specification.href}
target="_blank"
rel="noreferrer"
className="mt-2 inline-flex text-sm font-semibold text-slate-950 underline-offset-4 hover:underline"
>
{standard.specification.label}
</a>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">Submission coverage</div>
<p className="mt-2 text-sm leading-6 text-slate-800">
{standard.submissionPoints.length} submission point{standard.submissionPoints.length === 1 ? '' : 's'} tracked
</p>
</div>
</CardContent>
</Card>
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">Compliance profile</CardTitle>
<CardDescription className="text-base text-slate-600">
{standard.compliance.summary}
</CardDescription>
</CardHeader>
<CardContent>
<ul className="space-y-3">
{standard.compliance.enforcement.map((item) => (
<li key={item} className="rounded-2xl bg-slate-50 px-4 py-3 text-sm leading-6 text-slate-700">
{item}
</li>
))}
</ul>
</CardContent>
</Card>
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">Governance and maintainers</CardTitle>
<CardDescription className="text-base text-slate-600">
{standard.governanceBody}
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{standard.maintainers.map((maintainer) => (
<div key={`${maintainer.name}-${maintainer.role}`} className="rounded-2xl bg-slate-50 px-4 py-4">
<div className="text-sm font-semibold text-slate-950">{maintainer.name}</div>
<div className="mt-1 text-sm text-slate-600">{maintainer.role}</div>
{maintainer.href ? (
<a
href={maintainer.href}
target="_blank"
rel="noreferrer"
className="mt-2 inline-flex text-sm font-medium text-slate-700 underline-offset-4 hover:text-slate-950 hover:underline"
>
View maintainer
</a>
) : null}
</div>
))}
</CardContent>
</Card>
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">Status history</CardTitle>
<CardDescription className="text-base text-slate-600">
Important milestones that help explain how the standard evolved and where it sits today.
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{standard.statusHistory.map((event) => (
<div key={`${event.date}-${event.title}`} className="rounded-2xl bg-slate-50 px-4 py-4">
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">{event.date}</div>
<div className="mt-2 text-sm font-semibold text-slate-950">{event.title}</div>
<div className="mt-1 text-sm leading-6 text-slate-600">{event.summary}</div>
</div>
))}
</CardContent>
</Card>
</div>
<div className="grid gap-6">
<ExternalLinkList title="Canonical repositories" links={standard.repos} />
<ExternalLinkList title="Submission points" links={standard.submissionPoints} />
<ExternalLinkList title="Documentation" links={standard.docs} />
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">Implementation examples</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{standard.implementationExamples.map((example) => (
<a
key={example.href}
href={example.href}
target="_blank"
rel="noreferrer"
className="block rounded-2xl border border-slate-200 bg-slate-50 px-4 py-4 transition hover:border-slate-300 hover:bg-slate-100"
>
<div className="text-sm font-semibold text-slate-950">{example.label}</div>
<div className="mt-1 text-sm leading-6 text-slate-600">{example.summary}</div>
</a>
))}
</CardContent>
</Card>
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">Submission checklist</CardTitle>
<CardDescription className="text-base text-slate-600">
Minimum elements tracked in the registry to keep the entry complete and submission-ready.
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{standard.submissionChecklist.map((item) => (
<div key={item.label} className="rounded-2xl bg-slate-50 px-4 py-4">
<div className="flex items-center gap-3">
<Badge
className={
item.required
? 'bg-amber-600 text-white hover:bg-amber-600'
: 'bg-slate-700 text-white hover:bg-slate-700'
}
>
{item.required ? 'Required' : 'Optional'}
</Badge>
<div className="text-sm font-semibold text-slate-950">{item.label}</div>
</div>
<div className="mt-2 text-sm leading-6 text-slate-600">{item.summary}</div>
</div>
))}
</CardContent>
</Card>
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">Ecosystem support</CardTitle>
<CardDescription className="text-base text-slate-600">
Snapshot of how the standard tends to perform across core infrastructure and operating contexts.
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{standard.ecosystemSupport.map((item) => (
<div key={item.area} className="rounded-2xl bg-slate-50 px-4 py-4">
<div className="flex items-center gap-3">
<div className="text-sm font-semibold text-slate-950">{item.area}</div>
<Badge
className={
item.status === 'Strong'
? 'bg-emerald-600 text-white hover:bg-emerald-600'
: item.status === 'Moderate'
? 'bg-sky-700 text-white hover:bg-sky-700'
: 'bg-violet-700 text-white hover:bg-violet-700'
}
>
{item.status}
</Badge>
</div>
<div className="mt-2 text-sm leading-6 text-slate-600">{item.summary}</div>
</div>
))}
</CardContent>
</Card>
</div>
</div>
</div>
</section>
);
}
@@ -0,0 +1,223 @@
'use client';
import { useEffect, useMemo, useState } from 'react';
import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import { standardsRegistry, type StandardRegistryEntry } from '../../content/standards';
import { standardRegistryEntrySchema } from '../../content/standards/schema';
import { standardsRegistryTemplate } from '../../content/standards/template';
import { AuthGuard } from '../AuthGuard';
function prettyPrint(value: unknown): string {
return `${JSON.stringify(value, null, 2)}\n`;
}
const DRAFT_STORAGE_KEY = 'standards-admin-draft';
const SELECTION_STORAGE_KEY = 'standards-admin-selection';
export function StandardsAdmin(): JSX.Element {
const [selection, setSelection] = useState<string>('template');
const [draft, setDraft] = useState<string>(prettyPrint(standardsRegistryTemplate));
const [copyState, setCopyState] = useState<'idle' | 'copied'>('idle');
const sourceEntries: Record<string, StandardRegistryEntry | typeof standardsRegistryTemplate> = useMemo(() => {
const entries = Object.fromEntries(standardsRegistry.map((standard) => [standard.slug, standard]));
return {
template: standardsRegistryTemplate,
...entries,
};
}, []);
const validation = useMemo(() => {
try {
const parsedJson = JSON.parse(draft);
const parsedStandard = standardRegistryEntrySchema.safeParse(parsedJson);
if (!parsedStandard.success) {
return {
valid: false,
message: parsedStandard.error.issues[0]?.message ?? 'Validation failed.',
parsed: null as StandardRegistryEntry | null,
};
}
return {
valid: true,
message: 'Entry is valid against the standards registry schema.',
parsed: parsedStandard.data as StandardRegistryEntry,
};
} catch (error) {
return {
valid: false,
message: error instanceof Error ? error.message : 'Invalid JSON.',
parsed: null as StandardRegistryEntry | null,
};
}
}, [draft]);
useEffect(() => {
if (typeof window === 'undefined') return;
const storedSelection = localStorage.getItem(SELECTION_STORAGE_KEY);
const storedDraft = localStorage.getItem(DRAFT_STORAGE_KEY);
if (storedSelection && sourceEntries[storedSelection]) {
setSelection(storedSelection);
}
if (storedDraft) {
setDraft(storedDraft);
} else if (storedSelection && sourceEntries[storedSelection]) {
setDraft(prettyPrint(sourceEntries[storedSelection]));
}
}, [sourceEntries]);
useEffect(() => {
if (typeof window === 'undefined') return;
localStorage.setItem(DRAFT_STORAGE_KEY, draft);
}, [draft]);
useEffect(() => {
if (typeof window === 'undefined') return;
localStorage.setItem(SELECTION_STORAGE_KEY, selection);
}, [selection]);
const handleCopy = async (): Promise<void> => {
await navigator.clipboard.writeText(draft);
setCopyState('copied');
window.setTimeout(() => setCopyState('idle'), 1500);
};
const handleExport = (): void => {
const blob = new Blob([draft], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = `${selection || 'standard-entry'}.json`;
anchor.click();
URL.revokeObjectURL(url);
};
const handleReset = (): void => {
const nextDraft = prettyPrint(sourceEntries[selection] ?? standardsRegistryTemplate);
setDraft(nextDraft);
};
return (
<AuthGuard requiredRoles={['admin', 'editor']}>
<div className="min-h-screen bg-[linear-gradient(180deg,#fff_0%,#f8fafc_45%,#fff_100%)] px-4 py-16 sm:px-6 lg:px-8">
<div className="mx-auto max-w-6xl space-y-8">
<div className="max-w-3xl">
<Badge
variant="outline"
className="mb-6 border-amber-300 bg-amber-50 px-4 py-1 text-[11px] uppercase tracking-[0.24em] text-amber-900"
>
Registry Authoring
</Badge>
<h1 className="text-4xl font-semibold tracking-tight text-slate-950 sm:text-5xl">
Standards registry editor
</h1>
<p className="mt-6 text-base leading-8 text-slate-600">
Use this authoring surface to start from the template or an existing standard, edit raw JSON,
validate it against the registry schema, and prepare a submission-ready entry.
</p>
</div>
<div className="grid gap-6 lg:grid-cols-[320px_minmax(0,1fr)]">
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">Source entry</CardTitle>
<CardDescription className="text-base text-slate-600">
Load the template or an existing standard into the editor.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<select
className="w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition focus:border-slate-500"
value={selection}
onChange={(event) => {
const nextSelection = event.target.value;
setSelection(nextSelection);
setDraft(prettyPrint(sourceEntries[nextSelection]));
}}
>
<option value="template">Template</option>
{standardsRegistry.map((standard) => (
<option key={standard.slug} value={standard.slug}>
{standard.name}
</option>
))}
</select>
<div className="rounded-2xl bg-slate-50 px-4 py-4 text-sm leading-7 text-slate-600">
Validation status:
<div className="mt-3 flex items-center gap-3">
<Badge
className={
validation.valid
? 'bg-emerald-600 text-white hover:bg-emerald-600'
: 'bg-rose-600 text-white hover:bg-rose-600'
}
>
{validation.valid ? 'Valid' : 'Invalid'}
</Badge>
</div>
<p className="mt-3">{validation.message}</p>
</div>
{validation.parsed ? (
<div className="rounded-2xl bg-slate-50 px-4 py-4 text-sm leading-7 text-slate-600">
<div><strong className="text-slate-950">Slug:</strong> {validation.parsed.slug}</div>
<div><strong className="text-slate-950">Href:</strong> {validation.parsed.href}</div>
<div><strong className="text-slate-950">Compliance:</strong> {validation.parsed.compliance.label}</div>
<div><strong className="text-slate-950">Submission points:</strong> {validation.parsed.submissionPoints.length}</div>
</div>
) : null}
</CardContent>
</Card>
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-xl text-slate-950">JSON editor</CardTitle>
<CardDescription className="text-base text-slate-600">
Edit the raw registry entry. This surface validates structure, required fields, and normalized enums.
</CardDescription>
</CardHeader>
<CardContent>
<div className="mb-4 flex flex-wrap gap-3">
<button
type="button"
onClick={() => {
void handleCopy();
}}
className="rounded-full bg-slate-950 px-4 py-2 text-sm font-semibold text-white"
>
{copyState === 'copied' ? 'Copied' : 'Copy JSON'}
</button>
<button
type="button"
onClick={handleExport}
className="rounded-full border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-900"
>
Export JSON
</button>
<button
type="button"
onClick={handleReset}
className="rounded-full border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-900"
>
Reset draft
</button>
</div>
<textarea
className="min-h-[720px] w-full rounded-2xl border border-slate-300 bg-slate-950 p-4 font-mono text-sm leading-6 text-slate-100 outline-none transition focus:border-slate-500"
value={draft}
onChange={(event) => setDraft(event.target.value)}
spellCheck={false}
/>
</CardContent>
</Card>
</div>
</div>
</div>
</AuthGuard>
);
}
@@ -0,0 +1,309 @@
'use client';
import { useState } from 'react';
import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@the-order/ui';
import type { StandardRegistryEntry } from '../../content/standards';
import { StandardIcon } from './standard-icon';
import Link from 'next/link';
import { getComparisonTarget } from '../../content/standards';
export function StandardsBrowser({
standards,
}: {
standards: StandardRegistryEntry[];
}): JSX.Element {
const [query, setQuery] = useState('');
const [complianceFilter, setComplianceFilter] = useState<'all' | 'native' | 'external'>('all');
const [statusFilter, setStatusFilter] = useState<'all' | 'live' | 'preview'>('all');
const [featuredFilter, setFeaturedFilter] = useState<'all' | 'featured' | 'non-featured'>('all');
const [categoryFilter, setCategoryFilter] = useState<string>('all');
const [viewMode, setViewMode] = useState<'list' | 'table'>('list');
const normalizedQuery = query.trim().toLowerCase();
const categories = Array.from(new Set(standards.map((standard) => standard.category))).sort();
const filteredStandards = standards.filter((standard) => {
const matchesQuery =
normalizedQuery.length === 0 ||
standard.name.toLowerCase().includes(normalizedQuery) ||
(standard.shortName?.toLowerCase().includes(normalizedQuery) ?? false) ||
standard.summary.toLowerCase().includes(normalizedQuery) ||
standard.category.toLowerCase().includes(normalizedQuery) ||
standard.standards.some((item) => item.toLowerCase().includes(normalizedQuery));
const matchesCompliance =
complianceFilter === 'all' ||
(complianceFilter === 'native' && standard.compliance.native) ||
(complianceFilter === 'external' && !standard.compliance.native);
const matchesStatus =
statusFilter === 'all' ||
(statusFilter === 'live' && standard.status === 'Live') ||
(statusFilter === 'preview' && standard.status === 'Preview');
const matchesFeatured =
featuredFilter === 'all' ||
(featuredFilter === 'featured' && !!standard.featured) ||
(featuredFilter === 'non-featured' && !standard.featured);
const matchesCategory = categoryFilter === 'all' || standard.category === categoryFilter;
return matchesQuery && matchesCompliance && matchesStatus && matchesFeatured && matchesCategory;
});
return (
<div className="min-h-screen bg-[linear-gradient(180deg,#fff_0%,#f8fafc_45%,#fff_100%)]">
<section className="px-4 pt-10 sm:px-6 lg:px-8">
<div className="mx-auto max-w-6xl">
<Card className="border-slate-200 bg-white shadow-sm">
<CardHeader>
<CardTitle className="text-2xl text-slate-950">Search and filter standards</CardTitle>
<CardDescription className="text-base text-slate-600">
Narrow the registry by keyword or by whether compliance is native to the standard.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4 lg:grid-cols-2">
<label className="block">
<span className="mb-2 block text-sm font-medium text-slate-700">Search</span>
<input
className="w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition focus:border-slate-500"
type="search"
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Search by name, category, standard, or summary"
/>
</label>
<label className="block">
<span className="mb-2 block text-sm font-medium text-slate-700">Compliance mode</span>
<select
className="w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition focus:border-slate-500"
value={complianceFilter}
onChange={(event) =>
setComplianceFilter(event.target.value as 'all' | 'native' | 'external')
}
>
<option value="all">All standards</option>
<option value="native">Native compliance</option>
<option value="external">External compliance</option>
</select>
</label>
<label className="block">
<span className="mb-2 block text-sm font-medium text-slate-700">Status</span>
<select
className="w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition focus:border-slate-500"
value={statusFilter}
onChange={(event) => setStatusFilter(event.target.value as 'all' | 'live' | 'preview')}
>
<option value="all">All statuses</option>
<option value="live">Live</option>
<option value="preview">Preview</option>
</select>
</label>
<label className="block">
<span className="mb-2 block text-sm font-medium text-slate-700">Category</span>
<select
className="w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition focus:border-slate-500"
value={categoryFilter}
onChange={(event) => setCategoryFilter(event.target.value)}
>
<option value="all">All categories</option>
{categories.map((category) => (
<option key={category} value={category}>
{category}
</option>
))}
</select>
</label>
<label className="block">
<span className="mb-2 block text-sm font-medium text-slate-700">Featured</span>
<select
className="w-full rounded-xl border border-slate-300 bg-white px-4 py-3 text-sm text-slate-900 outline-none transition focus:border-slate-500"
value={featuredFilter}
onChange={(event) =>
setFeaturedFilter(event.target.value as 'all' | 'featured' | 'non-featured')
}
>
<option value="all">All entries</option>
<option value="featured">Featured only</option>
<option value="non-featured">Non-featured only</option>
</select>
</label>
</CardContent>
<CardContent className="flex flex-wrap items-center gap-3 pt-0">
<Badge variant="outline" className="border-slate-300 bg-slate-50 text-slate-700">
{filteredStandards.length} result{filteredStandards.length === 1 ? '' : 's'}
</Badge>
<div className="ml-auto flex items-center gap-2">
<button
type="button"
onClick={() => setViewMode('list')}
className={`rounded-full px-3 py-1 text-sm font-medium ${
viewMode === 'list' ? 'bg-slate-950 text-white' : 'bg-slate-100 text-slate-700'
}`}
>
List
</button>
<button
type="button"
onClick={() => setViewMode('table')}
className={`rounded-full px-3 py-1 text-sm font-medium ${
viewMode === 'table' ? 'bg-slate-950 text-white' : 'bg-slate-100 text-slate-700'
}`}
>
Table
</button>
</div>
{query ? (
<button
type="button"
onClick={() => setQuery('')}
className="text-sm font-medium text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
>
Clear search
</button>
) : null}
</CardContent>
</Card>
</div>
</section>
{filteredStandards.length > 0 ? (
viewMode === 'list' ? (
<section className="px-4 py-12 sm:px-6 lg:px-8">
<div className="mx-auto grid max-w-6xl gap-6 lg:grid-cols-2">
{filteredStandards.map((standard) => {
const comparisonTarget = getComparisonTarget(standard);
return (
<Card key={standard.slug} className="border-slate-200 bg-white shadow-sm">
<CardHeader className="gap-4">
<div className="flex flex-wrap items-center gap-3">
<Badge variant="outline" className="border-slate-300 bg-slate-50 text-slate-700">
{standard.status}
</Badge>
<Badge
className={
standard.compliance.native
? 'bg-emerald-600 text-white hover:bg-emerald-600'
: 'bg-slate-800 text-white hover:bg-slate-800'
}
>
{standard.compliance.native ? 'Native compliance' : 'External compliance'}
</Badge>
</div>
<div>
<CardTitle className="flex items-center gap-3 text-2xl text-slate-950">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 text-slate-700">
<StandardIcon name={standard.icon} />
</span>
<span>{standard.name}</span>
</CardTitle>
<CardDescription className="mt-2 text-base text-slate-600">
{standard.shortName ?? standard.summary}
</CardDescription>
</div>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm leading-7 text-slate-600">{standard.summary}</p>
<div className="flex flex-wrap gap-5">
<Link
href={standard.href}
className="inline-flex items-center text-sm font-semibold text-slate-950 underline-offset-4 hover:underline"
>
View standard
</Link>
{comparisonTarget ? (
<Link
href={`${comparisonTarget.href}#comparison`}
className="inline-flex items-center text-sm font-medium text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
>
Compare with {comparisonTarget.name}
</Link>
) : null}
</div>
</CardContent>
</Card>
);
})}
</div>
</section>
) : (
<section className="px-4 py-12 sm:px-6 lg:px-8">
<div className="mx-auto max-w-6xl overflow-hidden rounded-[1.5rem] border border-slate-200 bg-white shadow-sm">
<div className="overflow-x-auto">
<table className="w-full min-w-[760px] text-left text-sm">
<thead className="bg-slate-50 text-slate-500">
<tr>
<th className="px-4 py-3 font-medium">Standard</th>
<th className="px-4 py-3 font-medium">Category</th>
<th className="px-4 py-3 font-medium">Compliance</th>
<th className="px-4 py-3 font-medium">Status</th>
<th className="px-4 py-3 font-medium">Submission points</th>
<th className="px-4 py-3 font-medium">Actions</th>
</tr>
</thead>
<tbody>
{filteredStandards.map((standard) => {
const comparisonTarget = getComparisonTarget(standard);
return (
<tr key={standard.slug} className="border-t border-slate-200">
<td className="px-4 py-4">
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-full bg-slate-100 text-slate-700">
<StandardIcon name={standard.icon} />
</span>
<div>
<div className="font-semibold text-slate-950">{standard.name}</div>
<div className="text-slate-500">{standard.shortName ?? standard.summary}</div>
</div>
</div>
</td>
<td className="px-4 py-4 text-slate-700">{standard.category}</td>
<td className="px-4 py-4 text-slate-700">{standard.compliance.label}</td>
<td className="px-4 py-4 text-slate-700">{standard.status}</td>
<td className="px-4 py-4 text-slate-700">{standard.submissionPoints.length}</td>
<td className="px-4 py-4">
<div className="flex flex-wrap gap-4">
<Link
href={standard.href}
className="font-semibold text-slate-950 underline-offset-4 hover:underline"
>
View
</Link>
{comparisonTarget ? (
<Link
href={`${comparisonTarget.href}#comparison`}
className="text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
>
Compare
</Link>
) : null}
</div>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</section>
)
) : (
<section className="px-4 py-20 sm:px-6 lg:px-8">
<div className="mx-auto max-w-3xl">
<Card className="border-slate-200 bg-white shadow-sm">
<CardContent className="flex flex-col items-center justify-center px-6 py-16 text-center">
<span className="inline-flex h-14 w-14 items-center justify-center rounded-full bg-slate-100 text-slate-700">
<StandardIcon name="file-stack" className="h-6 w-6" />
</span>
<h2 className="mt-6 text-2xl font-semibold text-slate-950">No standards matched</h2>
<p className="mt-3 max-w-xl text-sm leading-7 text-slate-600">
Try a broader keyword or switch the compliance filter back to show all standards in the registry.
</p>
</CardContent>
</Card>
</div>
</section>
)}
</div>
);
}
@@ -0,0 +1,138 @@
import Link from 'next/link';
import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../lib/ui-server';
import { getComparisonTarget, type StandardRegistryEntry } from '../../content/standards';
import { StandardIcon } from './standard-icon';
export function StandardsIndexPage({
standards,
compactHero = false,
}: {
standards: StandardRegistryEntry[];
compactHero?: boolean;
}): JSX.Element {
return (
<div className={compactHero ? '' : 'min-h-screen bg-[linear-gradient(180deg,#fff_0%,#f8fafc_45%,#fff_100%)]'}>
<section className={`px-4 sm:px-6 lg:px-8 ${compactHero ? 'py-12' : 'py-20'}`}>
<div className="mx-auto max-w-6xl">
{!compactHero ? (
<div className="max-w-3xl">
<Badge
variant="outline"
className="mb-6 border-amber-300 bg-amber-50 px-4 py-1 text-[11px] uppercase tracking-[0.24em] text-amber-900"
>
Standards Library
</Badge>
<h1 className="text-balance text-4xl font-semibold tracking-tight text-slate-950 sm:text-5xl">
Standards and frameworks for regulated digital asset programs
</h1>
<p className="mt-6 max-w-2xl text-pretty text-lg leading-8 text-slate-600">
Explore token standards, compliance frameworks, and implementation-ready guidance for
digital asset issuance across regulated and real-world asset use cases.
</p>
</div>
) : null}
<div className={`grid gap-6 lg:grid-cols-2 ${compactHero ? '' : 'mt-12'}`}>
{standards.map((standard) => {
const comparisonTarget = getComparisonTarget(standard);
return (
<Card
key={standard.slug}
className="border-slate-200 bg-white shadow-sm transition-transform hover:-translate-y-0.5"
>
<CardHeader className="gap-4">
<div className="flex flex-wrap items-center gap-3">
<Badge variant="outline" className="border-slate-300 bg-slate-50 text-slate-700">
{standard.status}
</Badge>
{standard.featured ? (
<Badge className="bg-slate-950 text-white hover:bg-slate-950">Featured</Badge>
) : null}
<Badge
className={
standard.compliance.native
? 'bg-emerald-600 text-white hover:bg-emerald-600'
: 'bg-slate-800 text-white hover:bg-slate-800'
}
>
{standard.compliance.native ? 'Native compliance' : 'External compliance'}
</Badge>
<span className="text-xs uppercase tracking-[0.2em] text-slate-500">
{standard.category}
</span>
</div>
<div>
<CardTitle className="flex items-center gap-3 text-2xl text-slate-950">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 text-sm font-semibold text-slate-700">
<StandardIcon name={standard.icon} />
</span>
<span>{standard.name}</span>
</CardTitle>
{standard.shortName ? (
<CardDescription className="mt-2 text-base text-slate-600">
{standard.shortName}
</CardDescription>
) : null}
</div>
</CardHeader>
<CardContent className="space-y-5">
<p className="text-sm leading-7 text-slate-600">{standard.summary}</p>
<dl className="grid gap-3 sm:grid-cols-2">
<div className="rounded-2xl bg-slate-50 p-4">
<dt className="text-xs uppercase tracking-[0.18em] text-slate-500">Audience</dt>
<dd className="mt-2 text-sm leading-6 text-slate-800">{standard.audience}</dd>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<dt className="text-xs uppercase tracking-[0.18em] text-slate-500">Standards</dt>
<dd className="mt-2 text-sm leading-6 text-slate-800">
{standard.standards.join(', ')}
</dd>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<dt className="text-xs uppercase tracking-[0.18em] text-slate-500">Specification</dt>
<dd className="mt-2 text-sm leading-6 text-slate-800">{standard.specification.label}</dd>
</div>
<div className="rounded-2xl bg-slate-50 p-4">
<dt className="text-xs uppercase tracking-[0.18em] text-slate-500">Submission points</dt>
<dd className="mt-2 text-sm leading-6 text-slate-800">{standard.submissionPoints.length}</dd>
</div>
</dl>
<div className="flex flex-wrap gap-4 text-sm text-slate-600">
{standard.publisher ? <span>Publisher: {standard.publisher}</span> : null}
{standard.auditor ? <span>Auditor: {standard.auditor}</span> : null}
</div>
<div className="flex flex-wrap gap-5">
<Link
href={standard.href}
className="inline-flex items-center text-sm font-semibold text-slate-950 underline-offset-4 hover:underline"
>
View standard
</Link>
<a
href={standard.specification.href}
target="_blank"
rel="noreferrer"
className="inline-flex items-center text-sm font-medium text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
>
Open spec
</a>
{comparisonTarget ? (
<Link
href={`${comparisonTarget.href}#comparison`}
className="inline-flex items-center text-sm font-medium text-slate-600 underline-offset-4 hover:text-slate-950 hover:underline"
>
Compare with {comparisonTarget.name}
</Link>
) : null}
</div>
</CardContent>
</Card>
);
})}
</div>
</div>
</section>
</div>
);
}
+203
View File
@@ -0,0 +1,203 @@
import type { MarketingLandingPageProps } from '../components/marketing/landing-page';
export const erc20LandingPage: MarketingLandingPageProps = {
hero: {
eyebrow: 'Foundational Token Standard',
headline: 'The Baseline Standard for Fungible Tokens',
subheadline:
'ERC-20 defines the core interface for fungible tokens on Ethereum and remains the foundation for interoperable token issuance across wallets, exchanges, and applications.',
supportingCopy:
'For teams that need broad ecosystem support and straightforward fungible token behavior, ERC-20 provides the common baseline. It is best suited to general-purpose token issuance and often serves as the interoperability layer beneath more specialized token frameworks.',
primaryCta: {
label: 'Explore ERC-20',
href: '/docs',
},
secondaryCta: {
label: 'Compare Standards',
href: '#comparison',
},
tertiaryLink: {
label: 'View Documentation',
href: '/docs',
},
},
trustStrip: {
label: 'Standards overview',
items: [
{
label: 'Standard',
value: 'ERC-20',
},
{
label: 'Role',
value: 'Baseline fungible token interface',
},
{
label: 'Category',
value: 'General-purpose tokenization',
},
{
label: 'Fit',
value: 'Interoperable token issuance and ecosystem compatibility',
},
{
label: 'Primary strength',
value: 'Simplicity and universal support',
},
],
},
valueProps: {
headline: 'Optimized for interoperability and simplicity',
intro:
'ERC-20 established the common language for fungible tokens across the Ethereum ecosystem. Its strength is broad adoption, predictable behavior, and straightforward integration.',
cards: [
{
title: 'Universal Compatibility',
copy:
'Supported across wallets, exchanges, custodians, and dApps as the baseline fungible token format.',
},
{
title: 'Simple Token Logic',
copy:
'Provides a minimal and predictable interface for balances, transfers, approvals, and allowances.',
},
{
title: 'Builder Friendly',
copy:
'Well understood by developers, infrastructure providers, and integrators across the Ethereum ecosystem.',
},
{
title: 'Strong Liquidity Fit',
copy:
'Works naturally with trading venues, pools, bridges, and DeFi protocols designed around fungible tokens.',
},
{
title: 'Composable Foundation',
copy:
'Frequently used as the compatibility layer for more specialized token models and compliance overlays.',
},
{
title: 'Fast to Adopt',
copy:
'A practical choice for teams that need broad support without embedding complex policy logic directly into the token.',
},
],
},
useCases: {
headline: 'Best suited to general-purpose token issuance',
intro:
'ERC-20 is often the right choice when the goal is broad market compatibility rather than native compliance enforcement.',
items: [
'Utility tokens',
'Stablecoins',
'Governance tokens',
'Rewards tokens',
'Exchange-listed assets',
'Protocol assets',
'Treasury instruments',
'Liquidity and settlement rails',
'Wrapped assets',
'Ecosystem incentives',
],
supportingLine:
'ERC-20 can still be used in regulated contexts, but compliance controls generally need to be added through external systems, wrappers, or custom extensions.',
},
proofPoints: {
headline: 'Why teams still start with ERC-20',
intro:
'ERC-20 remains the default standard for fungible token interoperability because it balances simplicity, widespread support, and implementation maturity.',
items: [
'Widely supported across the Ethereum ecosystem and token infrastructure stack.',
'Simple enough to implement, audit, and integrate without specialized compliance architecture.',
'Well suited to liquid, broadly distributed token models and exchange-facing assets.',
'Acts as the reference point for comparing more specialized token standards such as ERC-3643.',
],
},
comparison: {
headline: 'When is ERC-20 enough, and when is it not?',
intro:
'ERC-20 is ideal when broad compatibility and simple fungible token behavior matter most. It becomes less suitable when regulated issuance requires native controls over identity, eligibility, and transfer permissions.',
columns: ['Category', 'ERC-20', 'ERC-3643'],
rows: [
{
category: 'Primary purpose',
left: 'General-purpose fungible tokens',
right: 'Compliant tokenization for regulated assets',
},
{
category: 'Compliance model',
left: 'Usually external, manual, or custom',
right: 'Identity-based and enforced onchain',
},
{
category: 'Transfer restrictions',
left: 'Not native',
right: 'Native',
},
{
category: 'Eligibility controls',
left: 'Not native',
right: 'Native',
},
{
category: 'Best fit',
left: 'Broad ecosystem issuance and liquidity',
right: 'Regulated assets and RWAs',
},
{
category: 'Operational complexity',
left: 'Lower baseline complexity',
right: 'Higher capability with more built-in controls',
},
],
secondaryNote:
'ERC-20 is the baseline standard. For regulated programs, it is often the starting point for comparison rather than the final architecture.',
cta: {
label: 'View Standards Library',
href: '/standards',
},
},
faq: {
headline: 'Frequently asked questions',
items: [
{
question: 'What is ERC-20?',
answer:
'ERC-20 is the standard interface for fungible tokens on Ethereum. It defines how balances, transfers, approvals, and allowances should behave so tokens can interoperate across the ecosystem.',
},
{
question: 'Why is ERC-20 so widely used?',
answer:
'Its broad adoption comes from its simplicity and ecosystem support. Most wallets, exchanges, and DeFi applications are built to work with ERC-20 tokens by default.',
},
{
question: 'Does ERC-20 include compliance controls?',
answer:
'Not natively. Teams that need regulated issuance or controlled transfers usually add compliance through external systems, custom extensions, or more specialized standards.',
},
{
question: 'When should a team choose ERC-20?',
answer:
'ERC-20 is a strong choice when broad compatibility, liquidity integration, and straightforward token behavior matter more than embedding policy logic directly into the asset.',
},
{
question: 'How does ERC-20 relate to ERC-3643?',
answer:
'ERC-3643 builds on ERC-20 compatibility while adding identity-based compliance and transfer controls. ERC-20 is the simpler baseline; ERC-3643 is the more compliance-oriented framework.',
},
],
},
footerCta: {
headline: 'Start with the right token standard for the job',
copy:
'Use ERC-20 when you need broad compatibility and simple fungible token behavior, or compare it against specialized standards when compliance and lifecycle controls are part of the requirement.',
primaryCta: {
label: 'Explore Standards',
href: '/standards',
},
secondaryCta: {
label: 'Talk to Sales',
href: '/contact',
},
},
};
+203
View File
@@ -0,0 +1,203 @@
import type { MarketingLandingPageProps } from '../components/marketing/landing-page';
export const erc3643LandingPage: MarketingLandingPageProps = {
hero: {
eyebrow: 'Compliant Token Standard',
headline: 'Compliant Tokenization for Real-World Assets',
subheadline:
'ERC-3643 is the token standard for issuing, managing, and distributing regulated digital assets with identity-based compliance built into every transfer.',
supportingCopy:
'Designed for regulated markets, ERC-3643 combines token issuance, transfer control, lifecycle automation, and ecosystem compatibility in a single standard. It is built for teams bringing securities, funds, private debt, real estate, and other real-world assets onchain.',
primaryCta: {
label: 'Explore ERC-3643',
href: '/docs',
},
secondaryCta: {
label: 'Compare Standards',
href: '#comparison',
},
tertiaryLink: {
label: 'View Documentation',
href: '/docs',
},
},
trustStrip: {
label: 'Standards and assurance',
items: [
{
label: 'Standard',
value: 'ERC-3643, ERC-20 compatible',
},
{
label: 'Publisher',
value: 'Tokeny',
},
{
label: 'Auditor',
value: 'Hacken',
},
{
label: 'Category',
value: 'Compliant tokenization',
},
{
label: 'Fit',
value: 'Regulated digital assets and RWAs',
},
],
},
valueProps: {
headline: 'Built for compliant digital asset issuance',
intro:
'ERC-3643 embeds identity, eligibility, and transfer controls directly into the token so compliance is enforced as part of the asset itself, not added as a separate layer.',
cards: [
{
title: 'Built-In Compliance',
copy:
'Ensure only eligible participants can hold and transfer assets through identity-based controls enforced onchain.',
},
{
title: 'Lifecycle Automation',
copy:
'Support issuance, restrictions, corporate actions, and ongoing administration with extensive built-in functionality.',
},
{
title: 'Regulated Asset Ready',
copy:
'Well suited for securities, funds, private debt, commodities, real estate, and other tokenized real-world assets.',
},
{
title: 'ERC-20 Compatibility',
copy:
'Integrates with wallets, marketplaces, exchanges, and dApps while maintaining compliance enforcement.',
},
{
title: 'Configurable Rules',
copy:
'Adapt compliance policies over time to meet changing operational, legal, or market requirements.',
},
{
title: 'Proven Model',
copy: 'Validated across more than 120 use cases for compliant issuance and asset management.',
},
],
},
useCases: {
headline: 'Designed for regulated and real-world asset use cases',
intro:
'ERC-3643 can be applied across a broad range of tokenized asset classes and identity-driven applications.',
items: [
'Equities',
'Loans',
'Currencies',
'Funds and securities',
'Commodities',
'Private debt',
'Bonds',
'Real estate',
'Real-world assets',
'Identity',
'Loyalty',
],
supportingLine:
'From institutional issuance to programmable asset distribution, ERC-3643 provides a consistent compliance framework across asset classes.',
},
proofPoints: {
headline: 'Trusted for compliant tokenization',
intro:
'ERC-3643 is designed for teams that need more than simple token issuance. It provides the operational and compliance foundation required for regulated digital asset programs.',
items: [
'More than 120 validated use cases across compliant digital asset issuance and management.',
'Over 100 built-in functions for lifecycle operations, automation, and corporate actions.',
'Identity-based compliance enforced directly at the token level.',
'Broad compatibility with exchanges, marketplaces, wallets, and dApps.',
],
},
comparison: {
headline: 'Why ERC-3643 instead of a basic token standard?',
intro:
'ERC-20 is foundational for fungible tokens, but it does not natively provide the compliance controls needed for regulated issuance. ERC-3643 extends ERC-20 compatibility with identity-aware transfer rules and operational controls built into the token.',
columns: ['Category', 'ERC-3643', 'ERC-20'],
rows: [
{
category: 'Primary purpose',
left: 'Compliant tokenization for regulated assets',
right: 'General-purpose fungible tokens',
},
{
category: 'Compliance',
left: 'Enforced onchain through identity and rules',
right: 'Typically external or manual',
},
{
category: 'Transfer restrictions',
left: 'Native',
right: 'Not native',
},
{
category: 'Eligibility controls',
left: 'Native',
right: 'Not native',
},
{
category: 'Lifecycle management',
left: 'Extensive built-in functions',
right: 'Minimal core functions',
},
{
category: 'Best fit',
left: 'Regulated assets and RWAs',
right: 'Simple token issuance',
},
],
secondaryNote:
'Compared with ERC-1400-style positioning, ERC-3643 is often easier to present as a practical, issuer-friendly standard for compliant tokenization with strong interoperability and lifecycle flexibility.',
cta: {
label: 'View Full Comparison',
href: '#comparison',
},
},
faq: {
headline: 'Frequently asked questions',
items: [
{
question: 'What is ERC-3643?',
answer:
'ERC-3643 is an Ethereum token standard for compliant tokenization. It combines ERC-20 compatibility with identity-based compliance controls that govern who can hold, transfer, and interact with tokenized assets.',
},
{
question: 'How is ERC-3643 different from ERC-20?',
answer:
'ERC-20 defines the baseline behavior for fungible tokens, but it does not natively enforce compliance. ERC-3643 adds identity, eligibility, and transfer restrictions directly into token logic, making it better suited for regulated assets.',
},
{
question: 'What types of assets can use ERC-3643?',
answer:
'ERC-3643 is well suited for equities, funds, securities, private debt, bonds, commodities, real estate, currencies, and other tokenized real-world assets.',
},
{
question: 'Does ERC-3643 still work with the broader ecosystem?',
answer:
'Yes. ERC-3643 is designed to remain compatible with much of the broader Ethereum ecosystem, including wallets, dApps, marketplaces, and exchanges, while preserving compliance enforcement.',
},
{
question: 'Who is ERC-3643 for?',
answer:
'It is particularly relevant for financial institutions, asset issuers, tokenization platforms, fund operators, and enterprises that need compliant digital asset issuance and lifecycle management.',
},
],
},
footerCta: {
headline: 'Bring regulated assets onchain with greater control',
copy:
'Use ERC-3643 to issue compliant digital assets with identity-based transfer enforcement, lifecycle automation, and compatibility across the broader digital asset ecosystem.',
primaryCta: {
label: 'Get Started',
href: '/contact',
},
secondaryCta: {
label: 'Talk to Sales',
href: '/contact',
},
},
};
@@ -0,0 +1,66 @@
# Standards Registry Authoring Guide
This registry is the canonical content system for standards pages in the public portal.
## What a complete entry must include
Each standard entry should be complete enough to power:
- the homepage featured/preview cards
- the `/standards` browser and filters
- the detail page
- the API export routes
- future search, integrations, or content syndication
Minimum required content:
- canonical specification link
- at least one repository reference
- at least one submission point
- compliance posture
- governance and maintainer information
- status history
- implementation example
- submission checklist
- ecosystem support notes
## How to add a new standard
1. Copy the shape from [template.ts](./template.ts).
2. Create or import the marketing landing page content file.
3. Fill in all registry metadata fields.
4. Add the entry to [index.ts](./index.ts).
5. Confirm the `href`, `slug`, and `comparisonTarget` values line up.
6. Run `pnpm --dir the-order/apps/portal-public type-check`.
## Native compliance policy
Use `compliance.native = true` only when the standard itself natively enforces compliance or transfer eligibility at the token or protocol layer.
Use `compliance.native = false` when compliance depends on:
- wrappers
- external policy engines
- offchain operating controls
- custom overlays beyond the standard itself
## Submission points
Every entry should identify where updates or official proposals are submitted.
Examples:
- Ethereum EIPs repository
- official pull request queue
- canonical standards repo
- maintainer-owned implementation repo used for amendments or reference updates
## Icons
Use one of the normalized icon names defined in `schema.ts`:
- `shield`
- `coins`
- `scale`
- `network`
- `file-stack`
@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest';
import { getSortedStandards } from './index';
import { filterStandards } from './filter';
describe('filterStandards', () => {
const standards = getSortedStandards();
it('filters by query', () => {
const result = filterStandards(standards, { q: 'compliant' });
expect(result.map((standard) => standard.slug)).toEqual(['erc-3643']);
});
it('filters by compliance mode', () => {
const result = filterStandards(standards, { compliance: 'external' });
expect(result.map((standard) => standard.slug)).toEqual(['erc-20']);
});
it('filters featured entries', () => {
const result = filterStandards(standards, { featured: 'true' });
expect(result.map((standard) => standard.slug)).toEqual(['erc-3643']);
});
it('applies a limit', () => {
const result = filterStandards(standards, { limit: 1 });
expect(result).toHaveLength(1);
});
});
@@ -0,0 +1,50 @@
import type { StandardRegistryEntry } from './index';
export interface StandardsFilterInput {
q?: string | null;
category?: string | null;
status?: string | null;
featured?: string | null;
compliance?: string | null;
limit?: number | null;
}
export function filterStandards(
standards: StandardRegistryEntry[],
filters: StandardsFilterInput
): StandardRegistryEntry[] {
const query = filters.q?.trim().toLowerCase() ?? '';
const category = filters.category?.trim().toLowerCase() ?? '';
const status = filters.status?.trim().toLowerCase() ?? '';
const featured = filters.featured ?? null;
const compliance = filters.compliance?.trim().toLowerCase() ?? '';
const limit = filters.limit ?? null;
let filtered = standards.filter((standard) => {
if (query) {
const matchesQuery =
standard.name.toLowerCase().includes(query) ||
standard.summary.toLowerCase().includes(query) ||
standard.category.toLowerCase().includes(query) ||
(standard.shortName?.toLowerCase().includes(query) ?? false) ||
standard.standards.some((item) => item.toLowerCase().includes(query));
if (!matchesQuery) return false;
}
if (category && standard.category.toLowerCase() !== category) return false;
if (status && standard.status.toLowerCase() !== status) return false;
if (featured === 'true' && !standard.featured) return false;
if (featured === 'false' && standard.featured) return false;
if (compliance === 'native' && !standard.compliance.native) return false;
if (compliance === 'external' && standard.compliance.native) return false;
return true;
});
if (typeof limit === 'number' && Number.isFinite(limit) && limit > 0) {
filtered = filtered.slice(0, limit);
}
return filtered;
}
@@ -0,0 +1,35 @@
import { mkdir, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { getSortedStandards } from './index';
import { standardsRegistryJsonSchema } from './schema-json';
async function main(): Promise<void> {
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const projectRoot = path.resolve(currentDir, '..', '..', '..');
const outputDir = path.join(projectRoot, 'public', 'standards');
await mkdir(outputDir, { recursive: true });
const standards = getSortedStandards();
await writeFile(
path.join(outputDir, 'registry.json'),
`${JSON.stringify({ standards, count: standards.length }, null, 2)}\n`,
'utf8'
);
await writeFile(
path.join(outputDir, 'schema.json'),
`${JSON.stringify(standardsRegistryJsonSchema, null, 2)}\n`,
'utf8'
);
process.stdout.write(`Generated ${path.join(outputDir, 'registry.json')}\n`);
process.stdout.write(`Generated ${path.join(outputDir, 'schema.json')}\n`);
}
main().catch((error) => {
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
process.exitCode = 1;
});
@@ -0,0 +1,31 @@
import { describe, expect, it } from 'vitest';
import { getComparisonTarget, getFeaturedStandards, getRelatedStandards, getSortedStandards } from './index';
describe('standards registry helpers', () => {
it('returns standards sorted by order', () => {
const standards = getSortedStandards();
expect(standards.map((standard) => standard.slug)).toEqual(['erc-3643', 'erc-20']);
});
it('returns featured standards', () => {
const featured = getFeaturedStandards();
expect(featured).toHaveLength(1);
expect(featured[0]?.slug).toBe('erc-3643');
});
it('resolves explicit comparison targets', () => {
const erc3643 = getSortedStandards()[0]!;
const comparisonTarget = getComparisonTarget(erc3643);
expect(comparisonTarget?.slug).toBe('erc-20');
});
it('returns related standards ordered by score', () => {
const erc3643 = getSortedStandards()[0]!;
const related = getRelatedStandards(erc3643);
expect(related.map((standard) => standard.slug)).toContain('erc-20');
});
});
@@ -0,0 +1,399 @@
import type { MarketingLandingPageProps } from '../../components/marketing/landing-page';
import { erc20LandingPage } from '../erc20';
import { erc3643LandingPage } from '../erc3643';
import { standardsRegistrySchema, type StandardRegistryEntrySchema } from './schema';
export type StandardRegistryEntry = StandardRegistryEntrySchema & {
landingPage: MarketingLandingPageProps;
};
const standardsRegistrySource: StandardRegistryEntry[] = [
{
slug: 'erc-3643',
name: 'ERC-3643',
shortName: 'Compliant Tokenization',
eyebrow: 'Compliant Token Standard',
icon: 'shield',
summary:
'Issue, manage, and distribute regulated digital assets with identity-based compliance enforced directly at the token level.',
category: 'Compliance and tokenization',
status: 'Live',
audience: 'Financial institutions, issuers, tokenization platforms, and regulated RWA teams.',
standards: ['ERC-3643', 'ERC-20'],
publisher: 'Tokeny',
auditor: 'Hacken',
seo: {
title: 'ERC-3643 | The Order',
description:
'Compliant tokenization for real-world assets with identity-based compliance, lifecycle automation, and ERC-20 compatibility.',
keywords: ['ERC-3643', 'compliant tokenization', 'real-world assets', 'regulated digital assets'],
},
specification: {
label: 'EIP-3643 Specification',
href: 'https://eips.ethereum.org/EIPS/eip-3643',
kind: 'spec',
},
docs: [
{
label: 'Tokeny ERC-3643 Overview',
href: 'https://www.erc3643.org/',
kind: 'docs',
},
],
repos: [
{
label: 'ethereum/EIPs',
href: 'https://github.com/ethereum/EIPs',
kind: 'repo',
},
{
label: 'TokenySolutions/T-REX',
href: 'https://github.com/TokenySolutions/T-REX',
kind: 'repo',
},
],
submissionPoints: [
{
label: 'Ethereum EIPs Repository',
href: 'https://github.com/ethereum/EIPs',
kind: 'submission',
},
{
label: 'EIP-3643 Pull Requests',
href: 'https://github.com/ethereum/EIPs/pulls',
kind: 'submission',
},
],
compliance: {
native: true,
label: 'Native compliance',
summary:
'Identity, eligibility, and transfer rules are enforced directly in token behavior rather than being bolted on externally.',
enforcement: [
'Identity-aware transfer restrictions',
'Eligibility controls for holders and counterparties',
'Lifecycle rule updates during issuance and administration',
],
},
governanceBody: 'Ethereum Improvement Proposal process with ecosystem implementation led by Tokeny and related adopters.',
maintainers: [
{
name: 'Ethereum EIPs editors',
role: 'Canonical specification governance',
href: 'https://github.com/ethereum/EIPs',
},
{
name: 'Tokeny',
role: 'Reference implementation and ecosystem steward',
href: 'https://github.com/TokenySolutions/T-REX',
},
],
statusHistory: [
{
date: '2021-11-09',
title: 'EIP-3643 created',
summary: 'The compliant tokenization standard was introduced through the Ethereum EIP process.',
},
{
date: '2022-01-26',
title: 'T-REX reference implementation publicized',
summary: 'Production-oriented implementation guidance and contracts became widely available through Tokeny.',
},
{
date: '2024-01-01',
title: 'Ecosystem adoption broadened',
summary: 'ERC-3643 continued to mature as a practical framework for regulated asset issuance and lifecycle management.',
},
],
implementationExamples: [
{
label: 'Tokeny T-REX reference contracts',
summary: 'Production-oriented smart contract suite implementing identity-aware compliance controls for ERC-3643 assets.',
href: 'https://github.com/TokenySolutions/T-REX',
},
{
label: 'EIP-3643 canonical specification',
summary: 'The normative source for interface behavior, goals, and compliance-oriented design expectations.',
href: 'https://eips.ethereum.org/EIPS/eip-3643',
},
],
submissionChecklist: [
{
label: 'Canonical EIP specification reference',
required: true,
summary: 'Point to the accepted EIP text and normative standard behavior.',
},
{
label: 'Reference implementation repository',
required: true,
summary: 'Include a public implementation repo that demonstrates how the standard is realized in production.',
},
{
label: 'Compliance posture declaration',
required: true,
summary: 'Document whether compliance is natively enforced or relies on external controls.',
},
{
label: 'Submission points and governance path',
required: true,
summary: 'Identify the repo, pull request surface, and governance venue for amendments or follow-on proposals.',
},
],
ecosystemSupport: [
{
area: 'Wallet compatibility',
status: 'Moderate',
summary: 'Leverages ERC-20 compatibility, though full compliance-aware workflows depend on supporting infrastructure.',
},
{
area: 'Exchange and marketplace support',
status: 'Specialized',
summary: 'Best fit for venues prepared to handle regulated asset onboarding and rule-aware transfer flows.',
},
{
area: 'Issuer administration tooling',
status: 'Strong',
summary: 'Well suited to issuer-facing workflows, lifecycle events, and identity-driven operating models.',
},
{
area: 'Regulated asset operations',
status: 'Strong',
summary: 'Purpose-built for permissioned or regulated issuance where transfer and holder eligibility matter.',
},
],
comparisonTarget: 'erc-20',
featured: true,
order: 10,
href: '/standards/erc-3643',
landingPage: erc3643LandingPage,
},
{
slug: 'erc-20',
name: 'ERC-20',
shortName: 'Fungible Token Baseline',
eyebrow: 'Foundational Token Standard',
icon: 'coins',
summary:
'The core fungible token standard for broad wallet, exchange, and application compatibility across the Ethereum ecosystem.',
category: 'General-purpose tokenization',
status: 'Live',
audience: 'Protocol teams, issuers, exchanges, treasury teams, and builders focused on interoperability.',
standards: ['ERC-20'],
seo: {
title: 'ERC-20 | The Order',
description:
'The foundational fungible token standard for broad wallet, exchange, and application compatibility across Ethereum.',
keywords: ['ERC-20', 'fungible token', 'Ethereum token standard', 'token interoperability'],
},
specification: {
label: 'EIP-20 Specification',
href: 'https://eips.ethereum.org/EIPS/eip-20',
kind: 'spec',
},
docs: [
{
label: 'OpenZeppelin ERC-20 Guide',
href: 'https://docs.openzeppelin.com/contracts/4.x/erc20',
kind: 'docs',
},
],
repos: [
{
label: 'ethereum/EIPs',
href: 'https://github.com/ethereum/EIPs',
kind: 'repo',
},
{
label: 'OpenZeppelin/openzeppelin-contracts',
href: 'https://github.com/OpenZeppelin/openzeppelin-contracts',
kind: 'repo',
},
],
submissionPoints: [
{
label: 'Ethereum EIPs Repository',
href: 'https://github.com/ethereum/EIPs',
kind: 'submission',
},
{
label: 'EIP-20 Pull Requests',
href: 'https://github.com/ethereum/EIPs/pulls',
kind: 'submission',
},
],
compliance: {
native: false,
label: 'External compliance required',
summary:
'ERC-20 does not natively enforce compliance. Regulated issuance typically depends on wrappers, policy layers, or additional contract systems.',
enforcement: [
'External policy engines or offchain controls',
'Custom transfer restrictions when needed',
'Operational overlays for regulated counterparties',
],
},
governanceBody: 'Ethereum Improvement Proposal process with widespread ecosystem stewardship across libraries, wallets, and exchanges.',
maintainers: [
{
name: 'Ethereum EIPs editors',
role: 'Canonical specification governance',
href: 'https://github.com/ethereum/EIPs',
},
{
name: 'OpenZeppelin',
role: 'Widely used implementation maintainer',
href: 'https://github.com/OpenZeppelin/openzeppelin-contracts',
},
],
statusHistory: [
{
date: '2015-11-19',
title: 'EIP-20 proposed',
summary: 'The fungible token interface was introduced as a standard for interoperable Ethereum assets.',
},
{
date: '2017-09-20',
title: 'EIP-20 finalized',
summary: 'ERC-20 became the canonical baseline for fungible token behavior across the ecosystem.',
},
{
date: '2024-01-01',
title: 'Baseline ecosystem standard maintained',
summary: 'ERC-20 remains the reference layer for fungible token interoperability and exchange integration.',
},
],
implementationExamples: [
{
label: 'OpenZeppelin ERC-20 implementation',
summary: 'Widely used and audited Solidity implementation for fungible token behavior.',
href: 'https://github.com/OpenZeppelin/openzeppelin-contracts',
},
{
label: 'EIP-20 canonical specification',
summary: 'Normative interface definition for fungible token balances, transfers, and approvals.',
href: 'https://eips.ethereum.org/EIPS/eip-20',
},
],
submissionChecklist: [
{
label: 'Canonical EIP specification reference',
required: true,
summary: 'Link the accepted EIP text as the normative basis for the registry entry.',
},
{
label: 'Reference implementation repository',
required: true,
summary: 'Provide at least one widely adopted implementation repo to anchor ecosystem usage.',
},
{
label: 'Compliance dependency disclosure',
required: true,
summary: 'State clearly that compliance is external if the token standard does not enforce it natively.',
},
{
label: 'Submission and amendment path',
required: true,
summary: 'Document the repository and PR path for future revisions to the standard.',
},
],
ecosystemSupport: [
{
area: 'Wallet compatibility',
status: 'Strong',
summary: 'Near-universal support across Ethereum wallets and custody providers.',
},
{
area: 'Exchange and marketplace support',
status: 'Strong',
summary: 'Deep support across centralized and decentralized trading venues.',
},
{
area: 'Issuer administration tooling',
status: 'Moderate',
summary: 'Strong for basic issuance, though advanced compliance workflows usually require add-on systems.',
},
{
area: 'Regulated asset operations',
status: 'Specialized',
summary: 'Can be used as a base layer, but regulated issuance typically needs additional policy and control infrastructure.',
},
],
comparisonTarget: 'erc-3643',
featured: false,
order: 20,
href: '/standards/erc-20',
landingPage: erc20LandingPage,
},
];
export const standardsRegistry = standardsRegistrySchema.parse(standardsRegistrySource) as StandardRegistryEntry[];
export function getSortedStandards(): StandardRegistryEntry[] {
return [...standardsRegistry].sort((left, right) => {
if (left.order !== right.order) {
return left.order - right.order;
}
return left.name.localeCompare(right.name);
});
}
export function getFeaturedStandards(): StandardRegistryEntry[] {
return getSortedStandards().filter((entry) => entry.featured);
}
export function getStandardBySlug(slug: string): StandardRegistryEntry | undefined {
return standardsRegistry.find((entry) => entry.slug === slug);
}
export function getComparisonTarget(standard: StandardRegistryEntry): StandardRegistryEntry | undefined {
if (!standard.comparisonTarget) {
return undefined;
}
return getStandardBySlug(standard.comparisonTarget);
}
export function getRelatedStandards(
standard: StandardRegistryEntry,
limit = 2
): StandardRegistryEntry[] {
const others = getSortedStandards().filter((entry) => entry.slug !== standard.slug);
const scored = others.map((entry) => {
let score = 0;
if (entry.slug === standard.comparisonTarget) {
score += 100;
}
if (entry.comparisonTarget === standard.slug) {
score += 80;
}
if (entry.category === standard.category) {
score += 30;
}
const sharedStandards = entry.standards.filter((item) => standard.standards.includes(item)).length;
score += sharedStandards * 10;
if (entry.compliance.native === standard.compliance.native) {
score += 5;
}
return { entry, score };
});
return scored
.filter((item) => item.score > 0)
.sort((left, right) => {
if (left.score !== right.score) {
return right.score - left.score;
}
return left.entry.order - right.entry.order;
})
.slice(0, limit)
.map((item) => item.entry);
}
@@ -0,0 +1,227 @@
export const standardsRegistryJsonSchema = {
$schema: 'https://json-schema.org/draft/2020-12/schema',
$id: 'https://the-order.local/standards/schema.json',
title: 'The Order Standards Registry',
description: 'Canonical schema for standards registry entries used by the public portal.',
type: 'array',
items: {
$ref: '#/$defs/standardRegistryEntry',
},
$defs: {
standardIcon: {
type: 'string',
enum: ['shield', 'coins', 'scale', 'network', 'file-stack'],
},
standardLink: {
type: 'object',
additionalProperties: false,
required: ['label', 'href', 'kind'],
properties: {
label: { type: 'string', minLength: 1 },
href: { type: 'string', format: 'uri' },
kind: { type: 'string', enum: ['spec', 'repo', 'docs', 'submission'] },
},
},
seo: {
type: 'object',
additionalProperties: false,
required: ['title', 'description'],
properties: {
title: { type: 'string', minLength: 1 },
description: { type: 'string', minLength: 1 },
keywords: {
type: 'array',
items: { type: 'string', minLength: 1 },
},
},
},
complianceProfile: {
type: 'object',
additionalProperties: false,
required: ['native', 'label', 'summary', 'enforcement'],
properties: {
native: { type: 'boolean' },
label: { type: 'string', minLength: 1 },
summary: { type: 'string', minLength: 1 },
enforcement: {
type: 'array',
minItems: 1,
items: { type: 'string', minLength: 1 },
},
},
},
maintainer: {
type: 'object',
additionalProperties: false,
required: ['name', 'role'],
properties: {
name: { type: 'string', minLength: 1 },
role: { type: 'string', minLength: 1 },
href: { type: 'string', format: 'uri' },
},
},
statusEvent: {
type: 'object',
additionalProperties: false,
required: ['date', 'title', 'summary'],
properties: {
date: { type: 'string', minLength: 1 },
title: { type: 'string', minLength: 1 },
summary: { type: 'string', minLength: 1 },
},
},
implementationExample: {
type: 'object',
additionalProperties: false,
required: ['label', 'summary', 'href'],
properties: {
label: { type: 'string', minLength: 1 },
summary: { type: 'string', minLength: 1 },
href: { type: 'string', format: 'uri' },
},
},
checklistItem: {
type: 'object',
additionalProperties: false,
required: ['label', 'required', 'summary'],
properties: {
label: { type: 'string', minLength: 1 },
required: { type: 'boolean' },
summary: { type: 'string', minLength: 1 },
},
},
ecosystemSupport: {
type: 'object',
additionalProperties: false,
required: ['area', 'status', 'summary'],
properties: {
area: { type: 'string', minLength: 1 },
status: { type: 'string', enum: ['Strong', 'Moderate', 'Specialized'] },
summary: { type: 'string', minLength: 1 },
},
},
marketingLink: {
type: 'object',
additionalProperties: false,
required: ['label', 'href'],
properties: {
label: { type: 'string', minLength: 1 },
href: { type: 'string', minLength: 1 },
},
},
standardRegistryEntry: {
type: 'object',
additionalProperties: false,
required: [
'slug',
'name',
'icon',
'summary',
'category',
'status',
'audience',
'standards',
'seo',
'specification',
'repos',
'submissionPoints',
'compliance',
'governanceBody',
'maintainers',
'statusHistory',
'implementationExamples',
'submissionChecklist',
'ecosystemSupport',
'order',
'href',
'landingPage',
],
properties: {
slug: { type: 'string', minLength: 1 },
name: { type: 'string', minLength: 1 },
shortName: { type: 'string', minLength: 1 },
eyebrow: { type: 'string', minLength: 1 },
icon: { $ref: '#/$defs/standardIcon' },
summary: { type: 'string', minLength: 1 },
category: { type: 'string', minLength: 1 },
status: { type: 'string', enum: ['Live', 'Preview'] },
audience: { type: 'string', minLength: 1 },
standards: {
type: 'array',
minItems: 1,
items: { type: 'string', minLength: 1 },
},
publisher: { type: 'string', minLength: 1 },
auditor: { type: 'string', minLength: 1 },
seo: { $ref: '#/$defs/seo' },
specification: { $ref: '#/$defs/standardLink' },
docs: {
type: 'array',
items: { $ref: '#/$defs/standardLink' },
},
repos: {
type: 'array',
minItems: 1,
items: { $ref: '#/$defs/standardLink' },
},
submissionPoints: {
type: 'array',
minItems: 1,
items: { $ref: '#/$defs/standardLink' },
},
compliance: { $ref: '#/$defs/complianceProfile' },
governanceBody: { type: 'string', minLength: 1 },
maintainers: {
type: 'array',
minItems: 1,
items: { $ref: '#/$defs/maintainer' },
},
statusHistory: {
type: 'array',
minItems: 1,
items: { $ref: '#/$defs/statusEvent' },
},
implementationExamples: {
type: 'array',
minItems: 1,
items: { $ref: '#/$defs/implementationExample' },
},
submissionChecklist: {
type: 'array',
minItems: 1,
items: { $ref: '#/$defs/checklistItem' },
},
ecosystemSupport: {
type: 'array',
minItems: 1,
items: { $ref: '#/$defs/ecosystemSupport' },
},
comparisonTarget: { type: 'string', minLength: 1 },
featured: { type: 'boolean' },
order: { type: 'integer', minimum: 0 },
href: { type: 'string', minLength: 1 },
landingPage: {
type: 'object',
additionalProperties: false,
required: ['hero'],
properties: {
hero: {
type: 'object',
additionalProperties: false,
required: ['headline', 'subheadline'],
properties: {
eyebrow: { type: 'string' },
headline: { type: 'string' },
subheadline: { type: 'string' },
supportingCopy: { type: 'string' },
primaryCta: { $ref: '#/$defs/marketingLink' },
secondaryCta: { $ref: '#/$defs/marketingLink' },
tertiaryLink: { $ref: '#/$defs/marketingLink' },
},
},
},
},
},
},
},
} as const;
@@ -0,0 +1,151 @@
import { z } from 'zod';
export const standardIconSchema = z.enum(['shield', 'coins', 'scale', 'network', 'file-stack']);
export const standardLinkSchema = z.object({
label: z.string().min(1),
href: z.string().url(),
kind: z.enum(['spec', 'repo', 'docs', 'submission']),
});
export const standardSeoSchema = z.object({
title: z.string().min(1),
description: z.string().min(1),
keywords: z.array(z.string().min(1)).optional(),
});
export const standardComplianceProfileSchema = z.object({
native: z.boolean(),
label: z.string().min(1),
summary: z.string().min(1),
enforcement: z.array(z.string().min(1)).min(1),
});
export const standardMaintainerSchema = z.object({
name: z.string().min(1),
role: z.string().min(1),
href: z.string().url().optional(),
});
export const standardStatusEventSchema = z.object({
date: z.string().min(1),
title: z.string().min(1),
summary: z.string().min(1),
});
export const standardImplementationExampleSchema = z.object({
label: z.string().min(1),
summary: z.string().min(1),
href: z.string().url(),
});
export const standardChecklistItemSchema = z.object({
label: z.string().min(1),
required: z.boolean(),
summary: z.string().min(1),
});
export const standardEcosystemSupportItemSchema = z.object({
area: z.string().min(1),
status: z.enum(['Strong', 'Moderate', 'Specialized']),
summary: z.string().min(1),
});
export const standardRegistryEntrySchema = z.object({
slug: z.string().min(1),
name: z.string().min(1),
shortName: z.string().min(1).optional(),
eyebrow: z.string().min(1).optional(),
icon: standardIconSchema,
summary: z.string().min(1),
category: z.string().min(1),
status: z.enum(['Live', 'Preview']),
audience: z.string().min(1),
standards: z.array(z.string().min(1)).min(1),
publisher: z.string().min(1).optional(),
auditor: z.string().min(1).optional(),
seo: standardSeoSchema,
specification: standardLinkSchema,
docs: z.array(standardLinkSchema).optional(),
repos: z.array(standardLinkSchema).min(1),
submissionPoints: z.array(standardLinkSchema).min(1),
compliance: standardComplianceProfileSchema,
governanceBody: z.string().min(1),
maintainers: z.array(standardMaintainerSchema).min(1),
statusHistory: z.array(standardStatusEventSchema).min(1),
implementationExamples: z.array(standardImplementationExampleSchema).min(1),
submissionChecklist: z.array(standardChecklistItemSchema).min(1),
ecosystemSupport: z.array(standardEcosystemSupportItemSchema).min(1),
comparisonTarget: z.string().min(1).optional(),
featured: z.boolean().optional(),
order: z.number().int().nonnegative(),
href: z.string().min(1),
landingPage: z.object({
hero: z.object({
eyebrow: z.string().optional(),
headline: z.string(),
subheadline: z.string(),
supportingCopy: z.string().optional(),
primaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
secondaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
tertiaryLink: z.object({ label: z.string(), href: z.string() }).optional(),
}),
trustStrip: z
.object({
label: z.string().optional(),
items: z.array(z.object({ label: z.string(), value: z.string() })),
})
.optional(),
valueProps: z
.object({
headline: z.string(),
intro: z.string().optional(),
cards: z.array(z.object({ title: z.string(), copy: z.string() })),
})
.optional(),
useCases: z
.object({
headline: z.string(),
intro: z.string().optional(),
items: z.array(z.string()),
supportingLine: z.string().optional(),
})
.optional(),
proofPoints: z
.object({
headline: z.string(),
intro: z.string().optional(),
items: z.array(z.string()),
})
.optional(),
comparison: z
.object({
headline: z.string(),
intro: z.string().optional(),
columns: z.tuple([z.string(), z.string(), z.string()]),
rows: z.array(z.object({ category: z.string(), left: z.string(), right: z.string() })),
secondaryNote: z.string().optional(),
cta: z.object({ label: z.string(), href: z.string() }).optional(),
})
.optional(),
faq: z
.object({
headline: z.string(),
items: z.array(z.object({ question: z.string(), answer: z.string() })),
})
.optional(),
footerCta: z
.object({
headline: z.string(),
copy: z.string(),
primaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
secondaryCta: z.object({ label: z.string(), href: z.string() }).optional(),
})
.optional(),
}),
});
export const standardsRegistrySchema = z.array(standardRegistryEntrySchema);
export type StandardIconName = z.infer<typeof standardIconSchema>;
export type StandardRegistryEntrySchema = z.infer<typeof standardRegistryEntrySchema>;
@@ -0,0 +1,99 @@
import type { StandardRegistryEntry } from './index';
export const standardsRegistryTemplate: StandardRegistryEntry = {
slug: 'new-standard-slug',
name: 'New Standard',
shortName: 'Short positioning label',
eyebrow: 'Optional eyebrow',
icon: 'file-stack',
summary: 'One-sentence summary for cards, search, SEO previews, and registry exports.',
category: 'Category label',
status: 'Preview',
audience: 'Who this standard is for.',
standards: ['PRIMARY-STANDARD-ID'],
publisher: 'Optional publisher',
auditor: 'Optional auditor',
seo: {
title: 'New Standard | The Order',
description: 'SEO description for the detail page.',
keywords: ['keyword-one', 'keyword-two'],
},
specification: {
label: 'Canonical specification',
href: 'https://example.com/spec',
kind: 'spec',
},
docs: [
{
label: 'Documentation link',
href: 'https://example.com/docs',
kind: 'docs',
},
],
repos: [
{
label: 'Reference repo',
href: 'https://github.com/example/repo',
kind: 'repo',
},
],
submissionPoints: [
{
label: 'Canonical submission point',
href: 'https://github.com/example/repo/pulls',
kind: 'submission',
},
],
compliance: {
native: false,
label: 'External compliance required',
summary: 'State clearly whether compliance is native or external.',
enforcement: ['Describe enforcement mechanism'],
},
governanceBody: 'Who governs the standard and how changes are made.',
maintainers: [
{
name: 'Maintainer name',
role: 'Maintainer role',
href: 'https://example.com/maintainer',
},
],
statusHistory: [
{
date: 'YYYY-MM-DD',
title: 'Milestone title',
summary: 'Why this milestone matters.',
},
],
implementationExamples: [
{
label: 'Example implementation',
summary: 'Why this implementation is relevant.',
href: 'https://example.com/example',
},
],
submissionChecklist: [
{
label: 'Checklist item',
required: true,
summary: 'What this item proves or covers.',
},
],
ecosystemSupport: [
{
area: 'Wallet compatibility',
status: 'Moderate',
summary: 'How well the standard is supported here.',
},
],
comparisonTarget: 'existing-standard-slug',
featured: false,
order: 999,
href: '/standards/new-standard-slug',
landingPage: {
hero: {
headline: 'Hero headline',
subheadline: 'Hero subheadline',
},
},
};
+13
View File
@@ -87,6 +87,16 @@ class AuthStore {
const authStore = typeof window !== 'undefined' ? new AuthStore() : null;
function writeAuthCookie(accessToken?: string): void {
if (typeof document === 'undefined') return;
if (!accessToken) {
document.cookie = 'auth_token=; Path=/; Max-Age=0; SameSite=Lax';
return;
}
document.cookie = `auth_token=${encodeURIComponent(accessToken)}; Path=/; Max-Age=2592000; SameSite=Lax`;
}
// React hook to use auth state
export function useAuth(): AuthState {
const [state, setState] = React.useState<AuthState>(
@@ -114,13 +124,16 @@ export function useAuth(): AuthState {
if (user.accessToken) {
localStorage.setItem('auth_token', user.accessToken);
}
writeAuthCookie(user.accessToken);
},
logout: () => {
authStore?.setState({ user: null, isAuthenticated: false });
localStorage.removeItem('auth_token');
writeAuthCookie();
},
setUser: (user: AuthUser | null) => {
authStore?.setState({ user, isAuthenticated: !!user });
writeAuthCookie(user?.accessToken);
},
};
}
+1
View File
@@ -0,0 +1 @@
export * from '../../../../packages/ui/src/server';
+2 -3
View File
@@ -4,7 +4,7 @@ import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest): NextResponse {
// Public portal routes are generally open
// Only protect specific routes if needed
const protectedRoutes: string[] = []; // Add protected routes here if needed
const protectedRoutes: string[] = ['/standards/admin'];
const isProtectedRoute = protectedRoutes.some((route) => request.nextUrl.pathname.startsWith(route));
@@ -22,6 +22,5 @@ export function middleware(request: NextRequest): NextResponse {
}
export const config = {
matcher: [], // Add protected routes here if needed
matcher: ['/standards/admin/:path*'],
};
+11 -3
View File
@@ -3,6 +3,7 @@
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "esnext"],
"rootDir": "../../",
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -22,10 +23,17 @@
"paths": {
"@/*": ["./src/*"],
"@the-order/ui": ["../../packages/ui/src"],
"@the-order/ui/server": ["../../packages/ui/src/server"],
"@the-order/schemas": ["../../packages/schemas/src"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"../../packages/ui/src/**/*.ts",
"../../packages/ui/src/**/*.tsx"
],
"exclude": ["node_modules", "**/*.test.ts", "vitest.config.mts"]
}
+8
View File
@@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
include: ['src/**/*.test.ts'],
},
});
+23
View File
@@ -0,0 +1,23 @@
/**
* Server-safe UI exports.
*
* Keep this entrypoint limited to components and utilities that do not depend
* on client-only hooks or browser APIs so Next.js server components can import
* from it safely.
*/
export { Button } from './components/Button';
export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './components/Card';
export { Input } from './components/Input';
export { Label } from './components/Label';
export { Select } from './components/Select';
export { Textarea } from './components/Textarea';
export { Alert, AlertTitle, AlertDescription } from './components/Alert';
export { Badge } from './components/Badge';
export { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from './components/Table';
export { Skeleton } from './components/Skeleton';
export { Breadcrumbs } from './components/Breadcrumbs';
export { Checkbox } from './components/Checkbox';
export { Radio } from './components/Radio';
export { Switch } from './components/Switch';
export { cn } from './lib/utils';
+20 -9
View File
@@ -175,7 +175,7 @@ importers:
version: 8.5.6
tailwindcss:
specifier: ^3.4.1
version: 3.4.18
version: 3.4.18([email protected])
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7([email protected])
@@ -249,6 +249,9 @@ importers:
autoprefixer:
specifier: ^10.4.16
version: 10.4.22([email protected])
baseline-browser-mapping:
specifier: ^2.10.19
version: 2.10.19
eslint:
specifier: ^8.57.1
version: 8.57.1
@@ -260,13 +263,19 @@ importers:
version: 8.5.6
tailwindcss:
specifier: ^3.4.1
version: 3.4.18
version: 3.4.18([email protected])
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7([email protected])
tsx:
specifier: ^4.20.5
version: 4.20.6
typescript:
specifier: ^5.3.3
version: 5.9.3
vitest:
specifier: ^1.6.1
version: 1.6.1(@types/[email protected])(@vitest/[email protected])
packages/api-client:
dependencies:
@@ -6495,8 +6504,9 @@ packages:
/[email protected]:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
/baseline-browser-mapping@2.8.27:
resolution: {integrity: sha512-2CXFpkjVnY2FT+B6GrSYxzYf65BJWEqz5tIRHCvNsZZ2F3CmsCB37h8SpYgKG7y9C4YAeTipIPWG7EmFmhAeXA==}
/baseline-browser-mapping@2.10.19:
resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==}
engines: {node: '>=6.0.0'}
hasBin: true
dev: true
@@ -6563,7 +6573,7 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
baseline-browser-mapping: 2.8.27
baseline-browser-mapping: 2.10.19
caniuse-lite: 1.0.30001754
electron-to-chromium: 1.5.250
node-releases: 2.0.27
@@ -10324,7 +10334,7 @@ packages:
postcss: 8.5.6
dev: true
/[email protected]([email protected])([email protected]):
/[email protected]([email protected])([email protected])([email protected]):
resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
engines: {node: '>= 18'}
peerDependencies:
@@ -10345,6 +10355,7 @@ packages:
jiti: 1.21.7
lilconfig: 3.1.3
postcss: 8.5.6
tsx: 4.20.6
dev: true
/[email protected]([email protected]):
@@ -11471,10 +11482,10 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
dependencies:
tailwindcss: 3.4.18
tailwindcss: 3.4.18([email protected])
dev: true
/[email protected]:
/[email protected]([email protected]):
resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -11496,7 +11507,7 @@ packages:
postcss: 8.5.6
postcss-import: 15.1.0([email protected])
postcss-js: 4.1.0([email protected])
postcss-load-config: 6.0.1([email protected])([email protected])
postcss-load-config: 6.0.1([email protected])([email protected])([email protected])
postcss-nested: 6.2.0([email protected])
postcss-selector-parser: 6.1.2
resolve: 1.22.11
+5 -6
View File
@@ -30,6 +30,7 @@
"baseUrl": ".",
"paths": {
"@the-order/ui": ["./packages/ui/src"],
"@the-order/ui/server": ["./packages/ui/src/server"],
"@the-order/schemas": ["./packages/schemas/src"],
"@the-order/auth": ["./packages/auth/src"],
"@the-order/storage": ["./packages/storage/src"],
@@ -46,12 +47,10 @@
"@the-order/notifications": ["./packages/notifications/src"],
"@the-order/eu-lp": ["./packages/eu-lp/src"],
"@the-order/cache": ["./packages/cache/src"],
"@the-order/secrets": ["./packages/secrets/src"],
"@the-order/workflows": ["./packages/workflows/src"],
"@the-order/verifier-sdk": ["./packages/verifier-sdk/src"],
"@the-order/api-client": ["./packages/api-client/src"]
}
"@the-order/secrets": ["./packages/secrets/src"],
"@the-order/verifier-sdk": ["./packages/verifier-sdk/src"],
"@the-order/api-client": ["./packages/api-client/src"]
}
},
"exclude": ["node_modules", "dist", "build", ".next", "coverage"]
}