feat: Transactions & Spending Limits API#1
Open
johnephraim949-web wants to merge 58 commits into
Open
Conversation
…— 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.
…ctive nav highlighting, mobile drawer
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
fix: resolve issues mux-labs#28, mux-labs#30, mux-labs#33, mux-labs#34 — dashboard routing, nav clean…
…_#25_#27 Implement issues mux-labs#20, mux-labs#23, mux-labs#25, mux-labs#27:
…_#36_#39 Somzilla issues mux-labs#37 mux-labs#38 mux-labs#36 mux-labs#39
…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
…-layout feat: add analytics page layout
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
…y-date Feat/transactions sort by date
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.