Skip to content

feat: Transactions & Spending Limits API#1

Open
johnephraim949-web wants to merge 58 commits into
stagingfrom
feat/transactions-spending-limits-api
Open

feat: Transactions & Spending Limits API#1
johnephraim949-web wants to merge 58 commits into
stagingfrom
feat/transactions-spending-limits-api

Conversation

@johnephraim949-web

Copy link
Copy Markdown
Owner

This PR wires the Transactions UI to fetch transactions from the backend and loads/saves spending limits via API. It adds a small API helper and graceful fallback behavior when the backend is unreachable. Includes lint fixes.

utilityjnr and others added 30 commits May 29, 2026 15:22
…— dashboard routing, nav cleanup, wallets link, PageHeader component

## Summary of Changes

This commit addresses four GitHub issues simultaneously, all related to
routing/navigation improvements and app shell quality for the Mux Protocol
developer dashboard.

---

### Issue mux-labs#28 — Routing & navigation: Consolidate dashboard under /dashboard

**Problem:** The dashboard was only accessible under /demo/dashboard, which is
a development/demo path and not suitable as the canonical dashboard URL.

**What was done:**
- Created a new route tree at src/app/dashboard/ mirroring the existing
  src/app/demo/dashboard/ structure.
- Added src/app/dashboard/layout.tsx wrapping children with DashboardLayout,
  with proper metadata (title: 'Dashboard', description for Mux Protocol).
- Added src/app/dashboard/page.tsx as the dashboard home.
- Added src/app/dashboard/wallets/page.tsx for wallet monitoring.
- Added src/app/dashboard/analytics/page.tsx for analytics.
- Added src/app/dashboard/api-keys/page.tsx for API key management.
- Added src/app/dashboard/spending-limits/page.tsx for spending limit config.
- The /demo/dashboard routes remain intact for backward compatibility.

---

### Issue mux-labs#30 — Routing & navigation: Remove or stub documents nav item

**Problem:** The Sidebar contained a 'Documents' nav item pointing to
/demo/dashboard/documents, a route that does not exist and has no
implementation. This created a broken link in the navigation.

**What was done:**
- Removed the Documents nav entry (name, href, icon) from the navigation
  array in src/components/layouts/Sidebar.tsx.
- Removed the DocumentTextIcon import from @heroicons/react/24/outline since
  it was only used by the Documents nav item.

---

### Issue mux-labs#33 — Routing & navigation: Fix sidebar links to wallets page

**Problem:** The Sidebar had no link to the wallets page. Users had no way to
navigate to wallet monitoring from the sidebar.

**What was done:**
- Added a 'Wallets' nav item to the Sidebar navigation array pointing to
  /dashboard/wallets, using the WalletIcon from @heroicons/react/24/outline.
- Added the WalletIcon import alongside the other heroicon imports.
- All other sidebar hrefs were also updated from /demo/dashboard/* to
  /dashboard/* to match the new canonical route structure (issue mux-labs#28).

---

### Issue mux-labs#34 — App shell: Add shared PageHeader component

**Problem:** Each page was implementing its own page header (h1 + description
paragraph) inline with inconsistent markup, class names, and structure. There
was no shared component for this common pattern.

**What was done:**
- Created src/components/ui/PageHeader.tsx — a minimal, reusable component
  accepting title (required), description (optional), and actions (optional
  ReactNode for right-side action buttons/links).
- Updated all dashboard pages to use PageHeader instead of inline header
  markup:
    - src/app/dashboard/page.tsx
    - src/app/dashboard/wallets/page.tsx
    - src/app/dashboard/analytics/page.tsx
    - src/app/dashboard/api-keys/page.tsx
    - src/app/dashboard/spending-limits/page.tsx
    - src/app/demo/dashboard/wallets/page.tsx
    - src/app/demo/dashboard/api-keys/page.tsx
    - src/app/demo/dashboard/spending-limits/page.tsx
- The demo wallets page also had its own full-page layout wrapper (min-h-screen
  bg-zinc-50 p-6) and a 'Back to Home' link removed, since it now lives inside
  DashboardLayout which provides the shell.

---

Closes mux-labs#28
Closes mux-labs#30
Closes mux-labs#33
Closes mux-labs#34
…add favicon/OG images, not-found page, env validation, and Mux branding metadata
…torage

Closes mux-labs#120 — Add global testnet/mainnet toggle
Closes mux-labs#121 — Persist network in localStorage

## What was done

### mux-labs#120 — Global testnet/mainnet toggle

A shared NetworkContext (src/context/NetworkContext.tsx) was created to
hold the selected network ('mainnet' | 'testnet') as React state and
expose it via a useNetwork() hook. NetworkProvider was added to
DashboardLayout so every dashboard page shares the same network state.

A toggle UI was added to TopNav (src/components/layouts/TopNav.tsx)
with two buttons — Testnet and Mainnet — styled with amber and blue
highlights respectively, matching the existing NetworkBadge color
conventions. Switching the toggle updates the context, which
immediately re-filters the WalletTable (src/app/demo/dashboard/wallets/
page.tsx filters dummyWallets by network before passing to WalletTable).
WalletTable itself was updated to render an inline empty-state row
('No wallets found for this network.') when the filtered list is empty.

The selected network is also surfaced in the page title: the h1 and
breadcrumb in TopNav now include a colored pill badge showing the active
network, and document.title is synced to '{Page} · {Network} — Mux'
via useEffect on every page/network change.

### mux-labs#121 — Persist network in localStorage

NetworkContext was updated to read the stored value from localStorage
on first mount (after hydration, to avoid SSR mismatch) and write back
on every setNetwork() call. The storage key is 'mux_network'.

Stale or invalid values in localStorage are handled gracefully:
readStoredNetwork() validates the stored string against the
VALID_NETWORKS whitelist (['mainnet', 'testnet']) and falls back to
'mainnet' if the value is missing, unrecognised, or if localStorage is
unavailable (e.g. SSR, private-browsing restrictions). All
localStorage access is wrapped in try/catch so write failures are
silently ignored without breaking the UI.

## How it was done

- NetworkContext.tsx: useState initialises to DEFAULT_NETWORK ('mainnet')
  to keep server and client renders in sync. A useEffect fires after
  mount to hydrate from localStorage. setNetwork() updates both React
  state and localStorage atomically.
- DashboardLayout.tsx: wrapped the layout JSX with <NetworkProvider> so
  the context is available to all child routes including TopNav.
- TopNav.tsx: imported useNetwork(); added networkLabel and
  networkBadgeClass lookup maps; rendered the toggle buttons and the
  network badge in the h1/breadcrumb; added useEffect for document.title.
- WalletTable.tsx: added a ternary in TableBody to render a single
  colspan row with an empty-state message when wallets.length === 0.
- wallets/page.tsx: replaced the static wallet list with a filtered
  view derived from useNetwork() and dummyWallets.filter().

## Testing

Vitest + React Testing Library were added (vitest.config.ts, src/test/
setup.ts). Tests cover:
- NetworkContext: default state, switching, throws outside provider
- WalletTable: renders wallets, empty state, mainnet-only filter,
  testnet-only filter
- TopNav: network badge in h1, document.title on default and after
  switching in both directions (src/test/topnav-network-title.test.tsx)

All 13 tests pass.
Closes mux-labs#122 — Show network in page titles
Closes mux-labs#123 — Filter tables by selected network

## Overview

Both issues share the same foundation: a React context (NetworkContext)
that holds the active network selection ('mainnet' | 'testnet') and
exposes it to all dashboard pages via a useNetwork() hook.

---

## mux-labs#122 — Show network in page titles

### What was done
The selected network is now visible in three places:

1. The <h1> page title in TopNav — a coloured pill badge is rendered
   inline next to the page name. Mainnet uses blue (bg-blue-100
   text-blue-800), Testnet uses amber (bg-amber-100 text-amber-800),
   matching the existing NetworkBadge component colour conventions.

2. The breadcrumb last segment (desktop only) — same pill badge appended
   after the page name so the network is visible in the navigation trail.

3. The browser tab title (document.title) — a useEffect in TopNav syncs
   document.title to the format '{Page} · {Network} — Mux' whenever
   either the pathname or the active network changes.

### How it was done
- Added networkLabel and networkBadgeClass lookup objects in TopNav.tsx
  keyed by WalletNetwork ('mainnet' | 'testnet').
- Updated the <h1> to use flex + gap-2 and appended a <span> with the
  badge classes and label.
- Updated the breadcrumb <li> similarly.
- Added useEffect([pageTitle, network]) that writes document.title.
  This runs client-side only, avoiding any SSR mismatch.

---

## mux-labs#123 — Filter tables by selected network

### What was done
The Wallets table now shows only wallets that belong to the currently
selected network. Switching the network switcher in TopNav immediately
re-filters the list. When no wallets exist for the selected network the
table renders an inline empty-state row ('No wallets found for this
network.') instead of an empty tbody.

### How it was done

**NetworkContext** (src/context/NetworkContext.tsx — new file):
- Created a React context with NetworkProvider and useNetwork() hook.
- State is initialised to 'mainnet' (safe SSR default).
- A useEffect hydrates from localStorage after mount to restore the
  user's last selection across page reloads (stale/invalid values are
  validated against the ['mainnet', 'testnet'] whitelist and fall back
  to 'mainnet'; all localStorage access is wrapped in try/catch to
  handle unavailability gracefully).
- setNetwork() updates both React state and localStorage atomically.

**DashboardLayout** (src/components/layouts/DashboardLayout.tsx):
- Wrapped the layout JSX with <NetworkProvider> so every dashboard
  child route (TopNav, pages) shares the same network state without
  prop drilling.

**TopNav** (src/components/layouts/TopNav.tsx):
- Imported useNetwork() and added a Testnet / Mainnet toggle button
  group in the right-side actions area. The active button is highlighted
  with the corresponding network colour.

**WalletTable** (src/components/wallet/WalletTable.tsx):
- Added a ternary in TableBody: when wallets.length === 0 a single
  full-width row with the empty-state message is rendered; otherwise
  the existing row mapping runs unchanged.

**WalletsPage** (src/app/demo/dashboard/wallets/page.tsx):
- Replaced the static wallet list with useNetwork() + dummyWallets
  .filter(w => w.network === network) so the table always reflects the
  active network. The EmptyState import and manual Wallet[] type
  annotation were removed as they are no longer needed.
- Add Transaction type with Stellar fields: hash, from, to,
  amountXlm, memo, ledger, fee, network, status, createdAt
- Add mock-data/transactions.ts with 12 realistic Stellar txs
- Rewrite TransactionsTable: columns hash, from/to, amount XLM,
  status, network, date; filters by search/status/network;
  responsive mobile card layout
- Add node:test suite (9 tests) covering schema validation
- AddWalletModal: multi-step form (form -> submitting -> success)
  with Stellar address validation, network select, accessibility
  (role=dialog, aria-modal, Escape/backdrop close, aria-invalid)
- WalletTable: header bar with wallet count + Add Wallet button
  (rendered only when onAddWallet prop is provided)
- Wallets page: local state wires new wallets into the list;
  EmptyState action also opens the modal
- validateStellarAddress util: checks G-prefix, 56-char length,
  base32 alphabet (A-Z, 2-7)
- Vitest + Testing Library setup (vitest.config.ts, test/setup.ts)
- 32 tests across addressFormatting, AddWalletModal, WalletTable
- package.json: test/test:watch scripts + pinned devDependencies
- Add dynamic route /demo/dashboard/wallets/[id]
- Make WalletTable rows clickable (router.push on click)
- Copy button uses stopPropagation to avoid navigation
- Set up Vitest + Testing Library; add 5 tests for row linking
- Add useWallets / useWallet hooks fetching from NEXT_PUBLIC_API_URL
- Handle loading, error (incl. 404 -> notFound), empty, and success states
- Add WalletTableSkeleton for loading UI
- 9 new hook tests (14 total passing)
- Document NEXT_PUBLIC_API_URL in .env.example
…limits via API; graceful fallback; lint fixes
- Add dedicated Date column header with click-to-sort (asc/desc toggle)
- Default sort is date desc (newest first) on initial render
- Export Transaction types, SortConfig, and INITIAL_DATA for testability
- Fix pre-existing bug: handleSort now resets currentPage to 1
- clearFilters restores default date-desc sort instead of null
- Add aria-sort attributes on all sortable column headers for a11y
- Add SortIndicator helper component to reduce repetition
- Co-locate TransactionsTable.test.tsx with 16 tests covering:
  default sort order, asc/desc toggle, aria-sort, full ordering
  correctness, search+sort interaction, status filter+sort,
  clearFilters reset, pagination reset, and empty state
- Configure Vitest with jsdom, @vitejs/plugin-react, and path aliases
- Add global test setup: jest-dom matchers, next/navigation mock,
  next/link mock, navigator.clipboard stub
- Unit tests for truncateAddress and formatDate utilities
- Unit tests for useCopyToClipboard hook (state, timing, clipboard calls)
- Component tests for NetworkBadge, StatusIndicator, WalletTable
- Integration tests for WalletsPage (/demo/dashboard/wallets)
- Component tests for EmptyState and ErrorState UI primitives
- Add test, test:watch, test:coverage scripts to package.json
- Add vitest, @testing-library/* and jsdom as devDependencies
test: add Wallets UI test suite with Vitest + Testing Library
Jambox11 added 28 commits May 31, 2026 19:51
…and-table-filter

feat: show network in page titles and filter tables by selected network
…ersistence

feat: add global testnet/mainnet toggle and persist network in localS…
feature:Network & Stellar UX: Add explorer link component
…ions-schema-alignment

feat(mux-labs#93): align Transactions UI schema with Mux/Stellar
feature:Network & Stellar UX: Show friendbot hint on testnet
feature:Network & Stellar UX: Validate address copy format
feature:Network & Stellar UX: Format Stellar addresses helper
…ate-cta-stub

feat: add initiate recovery CTA stub with state machine and tests
…s-badge-styles

feat: Feature/recovery status badge styles
feat(wallets): add Add Wallet button and modal flow
…ng-state

feat: Feature/recovery loading state
…tail-link

feat: link wallet table rows to detail route
108 Spending limits UI: Add limits empty state
…ection

feat:Feature/recovery faq section
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.