From 55c90a24b0ea7dbe8398dd39877cd853e41939ea Mon Sep 17 00:00:00 2001 From: best2025j Date: Tue, 28 Jul 2026 17:37:43 +0100 Subject: [PATCH 1/3] gallary created --- README.md | 2 + docs/transaction-components.md | 11 +++ .../TransactionFixtureGallery.test.tsx | 18 +++++ .../TransactionFixtureGallery.tsx | 80 +++++++++++++++++++ src/components/transactions/fixtures.ts | 74 +++++++++++++++++ src/features/transactions/index.ts | 1 + src/pages/transactions.tsx | 13 ++- 7 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 src/components/transactions/TransactionFixtureGallery.test.tsx create mode 100644 src/components/transactions/TransactionFixtureGallery.tsx diff --git a/README.md b/README.md index a282403..ddbf419 100644 --- a/README.md +++ b/README.md @@ -52,5 +52,7 @@ Before submitting a PR, review [Low-Effort PR Examples](docs/low-effort-pr-examp ## Transaction History The dashboard now includes a normalized transaction history view at `/transactions`. See `docs/transaction-history.md` for model details, supported operation types, fixture coverage, and current data source limitations. +The transactions page also includes a contributor-ready fixture gallery for transaction review, progress, and receipt states. See [docs/transaction-components.md](docs/transaction-components.md) for the shared component contract, fixture expectations, and compliance-safe wording guidance. + ## Route Access Role-aware route guards protect admin, issuer, investor, and read-only sections. See `docs/route-access.md` for route mapping, guard states, SDK assumptions, and mock wallet fixtures. diff --git a/docs/transaction-components.md b/docs/transaction-components.md index b238384..8e83a1c 100644 --- a/docs/transaction-components.md +++ b/docs/transaction-components.md @@ -161,6 +161,17 @@ import { transferDetailsFixture, unknownResultFixture } from '@/components/trans /> ``` +### Contributor fixture gallery + +Use the gallery component at `src/components/transactions/TransactionFixtureGallery.tsx` +for a single-page preview of review, progress, and receipt states. It is meant for +UI review and contributor QA, not as a substitute for SDK or contract-level +validation. The gallery lives on the `/transactions` page and should be updated +whenever new transaction states or messaging are introduced. + +Keep any compliance-facing copy conservative and avoid presenting protocol-level +status as legal or financial advice. + ## Flows using these components - **`src/features/investor/components/TransferModal.tsx`** — the KYC whitelist check runs first, diff --git a/src/components/transactions/TransactionFixtureGallery.test.tsx b/src/components/transactions/TransactionFixtureGallery.test.tsx new file mode 100644 index 0000000..b175291 --- /dev/null +++ b/src/components/transactions/TransactionFixtureGallery.test.tsx @@ -0,0 +1,18 @@ +import { render, screen } from '@testing-library/react'; +import TransactionFixtureGallery from './TransactionFixtureGallery'; + +describe('TransactionFixtureGallery', () => { + it('renders the review, progress, and receipt states for contributors', () => { + render(); + + expect( + screen.getByRole('heading', { name: /component fixture gallery/i }), + ).toBeInTheDocument(); + expect(screen.getByText(/review transfer before signing/i)).toBeInTheDocument(); + expect(screen.getByText(/waiting for signature/i)).toBeInTheDocument(); + expect(screen.getByText(/submitting to the network/i)).toBeInTheDocument(); + expect(screen.getByText(/transaction confirmed/i)).toBeInTheDocument(); + expect(screen.getByText(/transaction failed/i)).toBeInTheDocument(); + expect(screen.getByText(/transaction status unknown/i)).toBeInTheDocument(); + }); +}); diff --git a/src/components/transactions/TransactionFixtureGallery.tsx b/src/components/transactions/TransactionFixtureGallery.tsx new file mode 100644 index 0000000..a6eb5c1 --- /dev/null +++ b/src/components/transactions/TransactionFixtureGallery.tsx @@ -0,0 +1,80 @@ +import TransactionProgress from './TransactionProgress'; +import TransactionReceipt from './TransactionReceipt'; +import TransactionReview from './TransactionReview'; +import { transferDetailsFixture, transactionFixtureGalleryEntries } from './fixtures'; + +export default function TransactionFixtureGallery() { + return ( +
+
+

+ Component fixture gallery +

+

+ Preview transaction states for contributors and reviewers +

+

+ Use these fixtures to validate review copy, progress indicators, and receipt + states without running an on-chain flow. They are intentionally conservative and + should be reviewed alongside the SDK and contract boundaries before launch. +

+
+ +
+ {transactionFixtureGalleryEntries.map((entry) => { + const isReceipt = entry.kind === 'receipt'; + const isReview = entry.kind === 'review'; + const isProgress = entry.kind === 'progress'; + + return ( +
+
+
+

{entry.title}

+

{entry.description}

+
+ + {entry.kind} + +
+ +
+ {isReview && ( + undefined} + onCancel={() => undefined} + /> + )} + + {isProgress && ( + + )} + + {isReceipt && ( + undefined} + explorerUrl={entry.explorerUrl} + /> + )} +
+
+ ); + })} +
+ +
+

Compliance note

+

+ These examples are for UI preview and review workflows only. Protocol-level + compliance information should not be presented as legal or financial advice. +

+
+
+ ); +} diff --git a/src/components/transactions/fixtures.ts b/src/components/transactions/fixtures.ts index b217141..170bcef 100644 --- a/src/components/transactions/fixtures.ts +++ b/src/components/transactions/fixtures.ts @@ -1,5 +1,16 @@ import type { TransactionDetails, TransactionResult } from './types'; +interface TransactionFixtureGalleryEntry { + id: string; + kind: 'review' | 'progress' | 'receipt'; + title: string; + description: string; + details?: TransactionDetails; + state?: 'signing' | 'pending'; + result?: TransactionResult; + explorerUrl?: string | null; +} + /** * Sample data for previewing the transaction components without running a real * flow — useful for manual QA of the failure and unknown states, which the @@ -87,3 +98,66 @@ export const transactionResultFixtures: TransactionResult[] = [ failureResultFixture, unknownResultFixture, ]; + +export const transactionFixtureGalleryEntries: TransactionFixtureGalleryEntry[] = [ + { + id: 'review-transfer', + kind: 'review', + title: 'Review transfer before signing', + description: 'Preview the confirmation screen for a transfer request.', + details: transferDetailsFixture, + }, + { + id: 'review-mint', + kind: 'review', + title: 'Review mint request', + description: 'Preview the confirmation copy for an issuance workflow.', + details: mintDetailsFixture, + }, + { + id: 'review-compliance', + kind: 'review', + title: 'Review compliance update', + description: 'Preview the review screen for a compliance action.', + details: complianceDetailsFixture, + }, + { + id: 'progress-signing', + kind: 'progress', + title: 'Waiting for wallet signature', + description: 'Shows the in-flight state while the wallet prompt is open.', + state: 'signing', + }, + { + id: 'progress-pending', + kind: 'progress', + title: 'Submitting to the network', + description: 'Shows the pending state after the transaction is submitted.', + state: 'pending', + }, + { + id: 'receipt-success', + kind: 'receipt', + title: 'Success receipt', + description: 'Preview the final confirmation screen for a successful transaction.', + details: transferDetailsFixture, + result: successResultFixture, + explorerUrl: 'https://stellar.expert/explorer/testnet/tx/b9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e', + }, + { + id: 'receipt-failure', + kind: 'receipt', + title: 'Failure receipt', + description: 'Preview the error state that appears when a transaction is rejected.', + details: transferDetailsFixture, + result: failureResultFixture, + }, + { + id: 'receipt-unknown', + kind: 'receipt', + title: 'Unknown outcome receipt', + description: 'Preview the guarded unknown-state copy for ambiguous outcomes.', + details: transferDetailsFixture, + result: unknownResultFixture, + }, +]; diff --git a/src/features/transactions/index.ts b/src/features/transactions/index.ts index 57b1a39..2553ee0 100644 --- a/src/features/transactions/index.ts +++ b/src/features/transactions/index.ts @@ -1,5 +1,6 @@ export * from '@/features/transactions/types'; export * from '@/features/transactions/normalize'; export * from '@/features/transactions/fixtures'; +export { default as TransactionFixtureGallery } from '@/components/transactions/TransactionFixtureGallery'; export * from '@/features/transactions/filters'; export * from '@/features/transactions/store'; diff --git a/src/pages/transactions.tsx b/src/pages/transactions.tsx index 4cd5071..8fde989 100644 --- a/src/pages/transactions.tsx +++ b/src/pages/transactions.tsx @@ -1,6 +1,7 @@ import Head from 'next/head'; import TransactionHistory from '@/components/TransactionHistory'; import RouteGuard from '@/components/RouteGuard'; +import TransactionFixtureGallery from '@/components/transactions/TransactionFixtureGallery'; export default function TransactionsPage() { return ( @@ -8,8 +9,16 @@ export default function TransactionsPage() { Transaction History | Aegis RWA -
-

Dashboard Transaction History

+
+
+

Dashboard Transaction History

+

+ Review the live history feed and the contributor fixture gallery for transaction + review, progress, and receipt states. +

+
+ +
From d87561ea7f948beaf644d953553491e90b1fcb15 Mon Sep 17 00:00:00 2001 From: best2025j Date: Tue, 28 Jul 2026 18:53:22 +0100 Subject: [PATCH 2/3] test successful --- .../transactions/TransactionFixtureGallery.test.tsx | 3 ++- src/components/transactions/TransactionFixtureGallery.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/transactions/TransactionFixtureGallery.test.tsx b/src/components/transactions/TransactionFixtureGallery.test.tsx index b175291..317204b 100644 --- a/src/components/transactions/TransactionFixtureGallery.test.tsx +++ b/src/components/transactions/TransactionFixtureGallery.test.tsx @@ -10,7 +10,8 @@ describe('TransactionFixtureGallery', () => { ).toBeInTheDocument(); expect(screen.getByText(/review transfer before signing/i)).toBeInTheDocument(); expect(screen.getByText(/waiting for signature/i)).toBeInTheDocument(); - expect(screen.getByText(/submitting to the network/i)).toBeInTheDocument(); + // expect(screen.getByText(/submitting to the network/i)).toBeInTheDocument(); + expect(screen.getAllByText(/submitting to the network/i),).toHaveLength(2); expect(screen.getByText(/transaction confirmed/i)).toBeInTheDocument(); expect(screen.getByText(/transaction failed/i)).toBeInTheDocument(); expect(screen.getByText(/transaction status unknown/i)).toBeInTheDocument(); diff --git a/src/components/transactions/TransactionFixtureGallery.tsx b/src/components/transactions/TransactionFixtureGallery.tsx index a6eb5c1..2fc9497 100644 --- a/src/components/transactions/TransactionFixtureGallery.tsx +++ b/src/components/transactions/TransactionFixtureGallery.tsx @@ -7,12 +7,12 @@ export default function TransactionFixtureGallery() { return (
-

+

Component fixture gallery -

-

- Preview transaction states for contributors and reviewers

+

+ Preview transaction states for contributors and reviewers +

Use these fixtures to validate review copy, progress indicators, and receipt states without running an on-chain flow. They are intentionally conservative and From c204a67703858d5c5813f885ea00dc31ed26ed65 Mon Sep 17 00:00:00 2001 From: best2025j Date: Fri, 31 Jul 2026 17:42:25 +0100 Subject: [PATCH 3/3] implement reusable compliance-safe transfer restrictions explainer for investor transfer flows. --- README.md | 3 ++ docs/investor-transfer-eligibility.md | 8 +-- .../assets/components/AssetCard.test.tsx | 38 ++++++++++++++ src/features/assets/components/AssetCard.tsx | 7 ++- .../investor/components/TransferModal.tsx | 6 +-- .../TransferRestrictionExplainer.tsx | 37 ++++++++++++++ src/lib/eligibility.ts | 51 +++++++++++++++++++ vitest-results.json | 1 + 8 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 src/features/assets/components/AssetCard.test.tsx create mode 100644 src/features/investor/components/TransferRestrictionExplainer.tsx create mode 100644 vitest-results.json diff --git a/README.md b/README.md index ddbf419..aedfd69 100644 --- a/README.md +++ b/README.md @@ -54,5 +54,8 @@ The dashboard now includes a normalized transaction history view at `/transactio The transactions page also includes a contributor-ready fixture gallery for transaction review, progress, and receipt states. See [docs/transaction-components.md](docs/transaction-components.md) for the shared component contract, fixture expectations, and compliance-safe wording guidance. +## Transfer Restriction Explainer +Investor-facing transfer flows now include a reusable explainer for transfer restrictions. The logic lives in [src/lib/eligibility.ts](src/lib/eligibility.ts) and the UI is surfaced in the investor portfolio and transfer modal. See [docs/investor-transfer-eligibility.md](docs/investor-transfer-eligibility.md) for the typed state model, edge cases, and compliance-safe wording guidance. + ## Route Access Role-aware route guards protect admin, issuer, investor, and read-only sections. See `docs/route-access.md` for route mapping, guard states, SDK assumptions, and mock wallet fixtures. diff --git a/docs/investor-transfer-eligibility.md b/docs/investor-transfer-eligibility.md index 6d68492..158b9e1 100644 --- a/docs/investor-transfer-eligibility.md +++ b/docs/investor-transfer-eligibility.md @@ -31,11 +31,13 @@ This panel surfaces a human-readable explanation instead of a bare error. - No wording implies a legal or regulatory guarantee. ## Files -- `src/lib/eligibility.ts` — pure evaluation engine (no React; fully unit-tested). +- `src/lib/eligibility.ts` — pure evaluation engine (no React; fully unit-tested) plus the portfolio-facing explainer helper. - `src/lib/__fixtures__/eligibility.ts` — sample inputs for all major states. -- `src/features/investor/InvestorEligibilityPanel.tsx` — the UI panel. +- `src/features/investor/InvestorEligibilityPanel.tsx` — the reusable panel UI. +- `src/features/investor/components/TransferRestrictionExplainer.tsx` — the portfolio/transfer-modal explainer used in investor flows. - `src/lib/eligibility.test.ts` — engine unit tests. - `src/features/investor/InvestorEligibilityPanel.test.tsx` — component tests. +- `src/features/assets/components/AssetCard.test.tsx` — regression coverage for the investor asset card experience. ## SDK mapping The dashboard should feed real signals into `EligibilityInput`: @@ -45,4 +47,4 @@ The dashboard should feed real signals into `EligibilityInput`: - `serviceAvailable` ← backend/SDK reachability. Until those signals are wired, the panel can be rendered with `serviceAvailable: false` -to honestly show an `unknown` state rather than guessing. +to honestly show an `unknown` state rather than guessing. For portfolio and transfer modal contexts, the same logic is exposed through `explainPortfolioTransferEligibility(asset)` so contributors can reuse one consistent explanation path rather than branching into ad-hoc copy. diff --git a/src/features/assets/components/AssetCard.test.tsx b/src/features/assets/components/AssetCard.test.tsx new file mode 100644 index 0000000..8bec588 --- /dev/null +++ b/src/features/assets/components/AssetCard.test.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import AssetCard from './AssetCard'; +import type { PortfolioAsset } from '@/lib/aegis/types'; + +const asset: PortfolioAsset = { + id: 'asset-1', + name: 'Example Bond', + ticker: 'BND', + balance: 1250, + decimals: 7, + metadata: { + assetClass: 'Private Credit', + issuer: 'Example Issuer', + jurisdiction: 'US', + description: 'Example bond', + }, + compliance: { + state: 'restricted', + label: 'Restricted', + detail: 'This asset has transfer restrictions.', + }, + transferEligibility: { + state: 'ineligible', + reasons: ['Restricted to accredited investors only.'], + }, + isDataAvailable: true, +}; + +describe('AssetCard', () => { + it('shows a transfer restriction explainer when the asset is not eligible', () => { + render( {}} />); + + expect(screen.getByText(/Transfer eligibility/i)).toBeInTheDocument(); + expect(screen.getByText('Not eligible')).toBeInTheDocument(); + }); +}); diff --git a/src/features/assets/components/AssetCard.tsx b/src/features/assets/components/AssetCard.tsx index f681d07..cacc322 100644 --- a/src/features/assets/components/AssetCard.tsx +++ b/src/features/assets/components/AssetCard.tsx @@ -1,6 +1,7 @@ import { formatAmount } from '@/utils/formatting'; import ComplianceBadge from './ComplianceBadge'; import TransferEligibilityBadge from './TransferEligibilityBadge'; +import TransferRestrictionExplainer from '@/features/investor/components/TransferRestrictionExplainer'; import type { PortfolioAsset } from '@/lib/aegis/types'; interface AssetCardProps { @@ -41,10 +42,14 @@ export default function AssetCard({ asset, onTransferClick }: AssetCardProps) {

-
+
+
+ +
+
+ ); +} diff --git a/src/lib/eligibility.ts b/src/lib/eligibility.ts index 17680c3..0b69afc 100644 --- a/src/lib/eligibility.ts +++ b/src/lib/eligibility.ts @@ -155,3 +155,54 @@ export function evaluateBothDirections(input: Omit