From 3909c18d63135df6f6ed566865f9ab9d17fd6142 Mon Sep 17 00:00:00 2001
From: Awopetu Feyisayo <32976424+feyibosslady@users.noreply.github.com>
Date: Mon, 31 Aug 2026 10:33:09 +0000
Subject: [PATCH] feat: implement Stellar asset balance card grid with token
conversion summary
- Create types for AssetBalance and WalletSummary
- Add mock data with XLM, USDC, EURC, ASTRO tokens
- Build AssetCard component showing ticker, balance, and USD equivalent
- Build AssetBalanceGrid with responsive card grid layout
- Handle loading and empty wallet states gracefully
- Use design tokens from tokens.css with responsive grid
- Export barrel for wallet feature
Closes #61
---
.../wallet/components/AssetBalanceGrid.tsx | 72 +++++++++++++++++++
src/features/wallet/components/AssetCard.tsx | 56 +++++++++++++++
src/features/wallet/index.ts | 4 ++
src/features/wallet/mock-data.ts | 47 ++++++++++++
src/features/wallet/types.ts | 18 +++++
5 files changed, 197 insertions(+)
create mode 100644 src/features/wallet/components/AssetBalanceGrid.tsx
create mode 100644 src/features/wallet/components/AssetCard.tsx
create mode 100644 src/features/wallet/index.ts
create mode 100644 src/features/wallet/mock-data.ts
create mode 100644 src/features/wallet/types.ts
diff --git a/src/features/wallet/components/AssetBalanceGrid.tsx b/src/features/wallet/components/AssetBalanceGrid.tsx
new file mode 100644
index 0000000..e9f6556
--- /dev/null
+++ b/src/features/wallet/components/AssetBalanceGrid.tsx
@@ -0,0 +1,72 @@
+'use client';
+
+import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card';
+import { Wallet } from 'lucide-react';
+import { formatCurrency, formatRelativeTime } from '@/lib/format';
+import { AssetCard } from './AssetCard';
+import type { WalletSummary } from '../types';
+
+interface AssetBalanceGridProps {
+ summary: WalletSummary | null;
+ isLoading?: boolean;
+}
+
+export function AssetBalanceGrid({ summary, isLoading }: AssetBalanceGridProps) {
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ if (!summary || summary.assets.length === 0) {
+ return (
+
+
+
+
No assets found
+
+ Your wallet does not hold any Stellar assets yet. Once you receive tokens, they will appear here.
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ Asset Balances
+
+ Multi-asset overview across your Stellar wallet
+
+
+
+
+ {formatCurrency(summary.totalUsdValue, 'USD')}
+
+
+ Total Value · {formatRelativeTime(summary.lastUpdated)}
+
+
+
+
+
+
+ {summary.assets.map((asset) => (
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/features/wallet/components/AssetCard.tsx b/src/features/wallet/components/AssetCard.tsx
new file mode 100644
index 0000000..8404ebf
--- /dev/null
+++ b/src/features/wallet/components/AssetCard.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import { Card, CardContent } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import { formatCurrency } from '@/lib/format';
+import type { AssetBalance } from '../types';
+
+interface AssetCardProps {
+ asset: AssetBalance;
+}
+
+const ASSET_COLORS: Record = {
+ XLM: 'bg-info-soft text-info',
+ USDC: 'bg-success-soft text-success',
+ EURC: 'bg-gold-soft text-gold-strong',
+ ASTRO: 'bg-warning-soft text-warning',
+};
+
+export function AssetCard({ asset }: AssetCardProps) {
+ const usdValue = asset.balance * asset.usdPrice;
+ const colorClass = ASSET_COLORS[asset.code] ?? 'bg-surface-secondary text-foreground-secondary';
+
+ return (
+
+
+
+
+
+ {asset.code}
+
+
+
{asset.name}
+
{asset.code}
+
+
+
+ ${asset.usdPrice.toFixed(2)}
+
+
+
+
+
+
+ {formatCurrency(asset.balance, asset.code)}
+
+
+
+ {formatCurrency(usdValue, 'USD')} equivalent
+
+
+
+
+ );
+}
diff --git a/src/features/wallet/index.ts b/src/features/wallet/index.ts
new file mode 100644
index 0000000..5236035
--- /dev/null
+++ b/src/features/wallet/index.ts
@@ -0,0 +1,4 @@
+export { AssetBalanceGrid } from './components/AssetBalanceGrid';
+export { AssetCard } from './components/AssetCard';
+export type { AssetBalance, AssetCode, WalletSummary } from './types';
+export { MOCK_WALLET_SUMMARY, MOCK_ASSET_BALANCES } from './mock-data';
diff --git a/src/features/wallet/mock-data.ts b/src/features/wallet/mock-data.ts
new file mode 100644
index 0000000..cacf2c4
--- /dev/null
+++ b/src/features/wallet/mock-data.ts
@@ -0,0 +1,47 @@
+import type { AssetBalance, WalletSummary } from './types';
+
+export const MOCK_ASSET_BALANCES: AssetBalance[] = [
+ {
+ id: 'ast-xlm',
+ code: 'XLM',
+ name: 'Stellar Lumens',
+ balance: 12450.75,
+ usdPrice: 0.12,
+ decimals: 7,
+ },
+ {
+ id: 'ast-usdc',
+ code: 'USDC',
+ name: 'USD Coin',
+ balance: 8420.5,
+ usdPrice: 1.0,
+ issuer: 'GA5ZSEJ6B3AE8RGX3C古城L4PBRPSNY7HO6AZGEBTXOZMOI57DPMGBGI',
+ decimals: 7,
+ },
+ {
+ id: 'ast-eurc',
+ code: 'EURC',
+ name: 'Euro Coin',
+ balance: 3200.0,
+ usdPrice: 1.09,
+ issuer: 'GAXLKZUT5RRPOOMN6HEB3TTSX5IREHBWMFT76Y2G4Q377E3VZE5BN2EQ',
+ decimals: 7,
+ },
+ {
+ id: 'ast-astro',
+ code: 'ASTRO',
+ name: 'Astroid Token',
+ balance: 45000.0,
+ usdPrice: 0.05,
+ decimals: 7,
+ },
+];
+
+export const MOCK_WALLET_SUMMARY: WalletSummary = {
+ totalUsdValue: MOCK_ASSET_BALANCES.reduce(
+ (sum, asset) => sum + asset.balance * asset.usdPrice,
+ 0,
+ ),
+ assets: MOCK_ASSET_BALANCES,
+ lastUpdated: new Date().toISOString(),
+};
diff --git a/src/features/wallet/types.ts b/src/features/wallet/types.ts
new file mode 100644
index 0000000..dbc20f3
--- /dev/null
+++ b/src/features/wallet/types.ts
@@ -0,0 +1,18 @@
+export type AssetCode = 'XLM' | 'USDC' | 'EURC' | 'ASTRO';
+
+export interface AssetBalance {
+ id: string;
+ code: AssetCode;
+ name: string;
+ balance: number;
+ usdPrice: number;
+ iconUrl?: string;
+ issuer?: string;
+ decimals: number;
+}
+
+export interface WalletSummary {
+ totalUsdValue: number;
+ assets: AssetBalance[];
+ lastUpdated: string;
+}