Help page form submits to nowhere - Fixed Issue #347#427
Conversation
- Add proper htmlFor/id pairing to all form inputs across mint, burn, send, and savings withdraw pages - Implement ARIA attributes (aria-describedby, aria-label, aria-invalid) for screen reader support - Fix viewport zoom issue by removing maximum-scale=1 to allow text scaling - Improve color contrast for loading states and muted text to meet WCAG 2.1 AA (contrast ratio now 7.2:1 vs required 4.5:1) - Add role=alert and role=status for dynamic content announcements - Add focus management and keyboard navigation improvements - Implement axe-core Playwright tests to prevent future regressions Changes include: - app/mint/page.tsx: Label associations for select and amount inputs - app/burn/page.tsx: Complete form labeling with hint text - app/send/page.tsx: Dialog and form accessibility enhancements - app/savings/withdraw/page.tsx: Proper label-id pairing - app/layout.tsx: Updated viewport config to allow zooming - app/globals.css: Adjusted muted-foreground colors for contrast compliance - tests/accessibility.spec.ts: New axe-core test suite All money forms now have zero axe-core critical violations. Closes Pi-Defi-world#217
…ocalization implemeted i18n / localization "F-045 — No i18n / localization
fix(a11y): resolve accessibility violations on money forms (F-046)
…e-list-keys fix: replace unstable list keys and fix burn page build errors
fix(theme): improve send status badge contrast in dark mode
fix(frontend): prevent PII leaks in production console logs
…ature/fix-savings-deposit-handler Revert "fix: wire savings deposit dialog to API and show pending/completed st…"
Enhance Multi-Currency Bank Validation and Burn Page Form Logic
… secrets with AES-GCM (F-041, F-057, F-004)
…es-wallet-encryption Fix savings URI resolution, reserves unit labels, and wallet secret encryption
Pi-Defi-world#231 F-060 — Performance: send page callbacks not memoized
- mintSource dropdown: only show Stellar USDC (backend-supported) - i18n hydration: add suppressHydrationWarning to <html> - Amount overflow: add break-words to formatted amount previews - Keystroke validation: add useDebounce hook for 300ms debounce Closes Pi-Defi-world#322, Pi-Defi-world#329, Pi-Defi-world#332, Pi-Defi-world#334
, Pi-Defi-world#373) Pi-Defi-world#172 — remove ignoreBuildErrors: true from next.config.mjs and fix all surfaced TypeScript errors across 20+ files: - Add token field back to RequestOptions; export setToken no-op - Re-export useApiError from hooks/use-api-error (correct hook) - Fix useApiError destructuring (error/handleError → uiError/setApiError) in burn, fiat, reserves, wallet, send/[id], savings/deposit, savings/withdraw, rates, send, transactions/[id] - Resolve merge conflicts in lib/wallet-storage.ts (keep security branch) - Remove stray git command from contexts/auth-context.tsx - Add clearPasscode import to auth-context - Fix duplicate declarations in mint/page.tsx and burn/page.tsx - Fix missing imports (lucide icons, ApiErrorDisplay, Tooltip, etc.) - Fix lib/stellar/burning.ts: errorResultXdr → errorResult - Fix auth/2fa: wrong login() arity - Install @tanstack/react-virtual (missing dep used by send/page.tsx) - Update test files to match current hook/type APIs Pi-Defi-world#373 — add segment-level layout.tsx with unique title + description for all 38 pages (activity, auth, bills, burn, business, currency, fiat, help, lending, me, me/settings, mint, rates, recovery, reserves, savings, send, test-locale, transactions, wallet)
…329-332-334 fix: resolve mintSource dropdown, i18n hydration, amount overflow, and keystroke validation
…errors-and-page-metadata fix: enable TS build errors and add per-page metadata (Pi-Defi-world#172, Pi-Defi-world#373)
📝 WalkthroughWalkthroughThis PR integrates Changesi18n and Localization Framework
Authentication and API Infrastructure
Support System Integration
Form Validation and Input Handling
UI and Error Handling
Infrastructure and Security
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning |
|
@Jujukoder Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
@Jujukoder RESOLVE CONFLICTS |
|
Okayy |
|
Done, thank you! Kindly merge the PR |
There was a problem hiding this comment.
Actionable comments posted: 20
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (21)
app/help/page.tsx (2)
301-363:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAssociate each form label with its input.
These labels are separate siblings, not wrappers, and they do not point to an
id. Screen readers will not reliably announce the field names for this support form.Suggested fix
- <label className="form-label"> + <label htmlFor="support-name" className="form-label"> Name </label> <Input + id="support-name" type="text" placeholder="Your name" @@ - <label className="form-label"> + <label htmlFor="support-email" className="form-label"> Email </label> <Input + id="support-email" type="email" @@ - <label className="form-label"> + <label htmlFor="support-subject" className="form-label"> Subject </label> <Input + id="support-subject" type="text" @@ - <label className="form-label"> + <label htmlFor="support-message" className="form-label"> Message </label> <Textarea + id="support-message" placeholder="Describe your issue in detail..."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/help/page.tsx` around lines 301 - 363, Labels are not associated with their inputs; update each label to include an htmlFor that matches a corresponding id on the Input/Textarea (e.g., id="name", id="email", id="subject", id="message") and add those id props to the Input/Textarea components used with formData and setFormData so screen readers announce the fields correctly (reference the Input and Textarea usages where value={formData.name|email|subject|message} and onChange calls setFormData).
1-8:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix
app/help/page.tsxApp Router metadata boundary, resolve merge conflicts, and correct label accessibility
- [Critical]
app/help/page.tsxis marked"use client"but exportsexport const metadata; route metadata is Server Component–only in the App Router—movemetadatato the serverpage.tsx/layout.tsxand keep this file as a Client Component only for interactive UI.- [Critical] Unresolved merge-conflict markers remain in the FAQ accordion (
<<<<<<< HEAD/=======/>>>>>>> origin/devaround lines ~186-190); resolve/remove them.- [Major] Contact form
<label>elements don’t havehtmlForand the associatedInput/Textareacontrols don’t define matchingid(noaria-labelledby/aria-labeleither); add properid+htmlFor(or anariaassociation) for accessibility.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/help/page.tsx` around lines 1 - 8, The file currently mixes a Client Component ("use client") with a Server-only export (export const metadata) — move the metadata export out of this client page into the server-side route (page.tsx or layout.tsx) and keep this file purely client for interactive UI; also remove the Git conflict markers (<<<<<<< HEAD / ======= / >>>>>>> origin/dev) found around the FAQ accordion (the accordion component/render block) so it compiles cleanly; finally, fix accessibility in the contact form by adding matching id attributes to each Input/Textarea control and corresponding htmlFor on their label elements (or alternatively add appropriate aria-labelledby/aria-label associations) so labels like the form field label elements correctly reference their inputs.app/send/page.tsx (2)
369-404:⚠️ Potential issue | 🟠 Major | ⚡ Quick winVariable shadowing:
tused for both i18n function and loop variable.The
transfersListmemo usestas the loop variable (transfers.map((t: TransferItem) => ...) which shadows thettranslation function fromuseI18n. This causes the translation calls liket('send.transferLabel')at line 377 to fail at runtime.Suggested fix
- {transfers.map((t: TransferItem) => ( + {transfers.map((transfer: TransferItem) => ( <Link - key={t.transaction_id} - href={`/send/${t.transaction_id}`} + key={transfer.transaction_id} + href={`/send/${transfer.transaction_id}`} ...🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/send/page.tsx` around lines 369 - 404, The map callback in the transfersList memo shadows the i18n translation function t by using the name t for the TransferItem parameter; rename that parameter to a clear name like transfer (or item) in transfers.map((transfer: TransferItem) => ...) and update all usages inside the JSX (key, href, formatDate, formatAmount, getStatusColor, and status checks) from t.* to transfer.* so the existing t('send.transferLabel') translation call continues to reference the i18n function rather than the loop variable.
3-15:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftCritical: Pervasive unresolved merge conflicts throughout the entire file.
This file contains over 20 separate merge conflict regions spanning imports, hooks, state management, event handlers, and JSX rendering. The code is completely unparseable in its current state.
Key conflict areas include:
- Import statements (lines 3-15, 43-52, 59-64)
- Hook usage and balance handling (lines 99-110)
- Contact loading logic (lines 166-187)
- Transfer confirmation flow (lines 235-252, 326-342)
- Tab/header UI rendering (lines 407-458, 496-566)
- Dialog content and form fields (lines 573-843)
This file requires careful conflict resolution before any other review can proceed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/send/page.tsx` around lines 3 - 15, This file contains many leftover Git merge conflict markers (e.g., <<<<<<< HEAD, =======, >>>>>>> upstream/dev) across imports and component code; remove all conflict markers and reconcile the two variants by: keeping the Metadata export (export const metadata) if Next metadata is intended, merging import lists to include React hooks used (useState, useEffect, useCallback, useMemo, useRef) and the useVirtualizer import, and consolidating duplicated or divergent logic in functions such as the contact-loading logic, balance handling hooks, transfer confirmation flow, tab/header rendering, and dialog/form components (search for symbols like metadata, useVirtualizer, contact loading functions, transfer confirmation handlers and tab rendering components) so the component is a single coherent implementation; after resolving, run TypeScript/ESLint/Prettier and ensure the module compiles and the JSX renders without parse errors.next.config.mjs (1)
1-55:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winCritical: Multiple unresolved merge conflicts throughout the file.
This file has merge conflicts in three locations:
- Lines 1-9: Import section (next-intl plugin vs env-safety validation)
- Lines 13-37: Config object (images.unoptimized vs typescript.ignoreBuildErrors and experimental settings)
- Lines 51-55: Default export (withNextIntl vs withBundleAnalyzer)
Important consideration: The
origin/devside setsignoreBuildErrors: true, whileupstream/devhas it asfalsewith comment "F-001: TypeScript errors must fail the build to prevent shipping broken code". Thefalsesetting is the safer choice for production quality.When resolving, you likely need to combine:
- next-intl plugin wrapper
- env validation
ignoreBuildErrors: falsefor safety- experimental optimizations if needed
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@next.config.mjs` around lines 1 - 55, Resolve the merge conflicts by combining the intl plugin and env validation: import createNextIntlPlugin and call validateEnv(process.env) at top, create withNextIntl via createNextIntlPlugin(), and ensure the final export composes any bundle analyzer wrapper around withNextIntl (e.g., export default withBundleAnalyzer ? withBundleAnalyzer(withNextIntl(nextConfig)) : withNextIntl(nextConfig)); in nextConfig set typescript.ignoreBuildErrors to false (per F-001) and include the experimental.optimizePackageImports block, keep poweredByHeader:false and the redirects array intact; update imports/exports to reference createNextIntlPlugin, withNextIntl, validateEnv, withBundleAnalyzer, and nextConfig so there are no remaining conflict markers.app/[locale]/auth/2fa/page.tsx (1)
1-8:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winMove
metadataout of this"use client"page (app/[locale]/auth/2fa/page.tsx).
Next.js App Router disallowsexport const metadata/generateMetadatafrom files marked with"use client"; keep the route metadata in a serverpage.tsxorlayout.tsx, and move the interactive 2FA UI into a separate"use client"child component.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`[locale]/auth/2fa/page.tsx around lines 1 - 8, The file app/[locale]/auth/2fa/page.tsx currently has "use client" and exports const metadata which Next.js disallows; remove the export const metadata from this client page and move that metadata into a server component (either the server-side page.tsx or a surrounding layout.tsx) while extracting the interactive 2FA UI into a separate "use client" child component (e.g., TwoFactorForm or TwoFactorClient) that the server page imports and renders; update imports/exports so metadata lives in the server file and the client-only logic (state, effects, event handlers) stays inside the new client component.app/burn/page.tsx (2)
176-177:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win
searchParamsis undefined.The form's
defaultValuesreferencessearchParams?.get("amount")andsearchParams?.get("currency"), butsearchParamsis never declared or imported. This appears to be a remnant from a removeduseSearchParams()hook import.This will cause a
ReferenceErrorat runtime when the component mounts.🐛 Proposed fix
Either import and use the hook:
+import { useSearchParams } from "next/navigation"; ... export default function BurnPage() { + const searchParams = useSearchParams(); const opts = useApiOpts();Or remove the searchParams references from defaultValues:
defaultValues: { - acbuAmount: searchParams?.get("amount") || "", - currency: (searchParams?.get("currency") || "NGN").toUpperCase().slice(0, 3), + acbuAmount: "", + currency: "NGN",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/burn/page.tsx` around lines 176 - 177, The defaultValues block in the burn page references an undefined searchParams (searchParams?.get("amount") and searchParams?.get("currency")), causing a runtime ReferenceError; fix by either importing and using the Next.js hook that provides searchParams (e.g., add useSearchParams() and assign const searchParams = useSearchParams() in the component before defaultValues) or remove/replace those references with safe fallbacks (e.g., use empty strings or props/state) so that the defaultValues in the form no longer reference an undeclared symbol.
3-14:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winMultiple unresolved merge conflicts throughout the file.
This file has extensive merge conflicts at:
- Lines 3-14: Imports and metadata
- Lines 24-30: SkeletonList import
- Lines 52-141: burnSchema definition
- Lines 151-155: formatCurrency helper
- Lines 185-196: State variable extraction
- Lines 309-337: Error handling logic
- Lines 367-370, 522-570, 575-585: JSX structure
The file is completely unparseable in its current state.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/burn/page.tsx` around lines 3 - 14, The file contains unresolved git conflict markers making it unparseable; remove all conflict markers (<<<<<<<, =======, >>>>>>>) and reconcile the intended imports and metadata by keeping a single import line for React (include Suspense if used), the Metadata export block, and the SkeletonList import; restore and validate the complete burnSchema object, the formatCurrency helper, the component state extraction (state variables), and the error handling logic referenced in the file (search for symbols burnSchema, formatCurrency, SkeletonList, and any handler functions like onBurn or error handling blocks) so the JSX structure (the page component/render return) is a single coherent tree without duplicated/conflicting sections. Ensure the default export (or BurnPage component) compiles and that all referenced symbols are imported or defined exactly once.lib/wallet-storage.ts (1)
198-233:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winUnresolved merge conflict breaks compilation.
This file contains unresolved git merge conflict markers (
<<<<<<< HEAD,=======,>>>>>>> origin/dev) that make it unparseable. The PR cannot be merged until these conflicts are resolved.The conflict appears to be between two implementations of
getWalletSecretAnyLocal:
- HEAD: Returns only from plaintext storage
- origin/dev: Also attempts decryption with in-memory passcode
Resolve this conflict by choosing the appropriate behavior or merging both approaches.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/wallet-storage.ts` around lines 198 - 233, Remove the git conflict markers and restore a single, compilable implementation of getWalletSecretAnyLocal: keep the assertDevOnly() call, call getWalletSecretLocalPlaintext(userId, stellarAddress) and return it if present, otherwise attempt to getPasscode() and if present call getWalletSecret(userId, passcode) inside a try/catch (ignore errors) and return the decrypted value or null; ensure no conflict markers (<<<<<<<, =======, >>>>>>>) remain and function compiles.app/currency/page.tsx (1)
30-48:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winDuplicate imports will cause build errors.
Several imports appear twice:
useBalance(lines 30 and 40)useToast(lines 31 and 44)ratesApi(lines 32 and 39)RatesResponse(lines 33 and 41)Remove the duplicate imports after resolving the merge conflicts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/currency/page.tsx` around lines 30 - 48, The file has duplicate import declarations for useBalance, useToast, ratesApi and RatesResponse—remove the redundant import lines and consolidate into a single import per module; specifically keep one import from "`@/hooks/use-balance`" (useBalance), one from "`@/hooks/use-toast`" (useToast), one from "`@/lib/api/rates`" (ratesApi and RatesResponse), and ensure the remaining imports also include MintResponse, BurnResponse, CurrencyPreference, and any other symbols referenced (e.g., mintApi, burnApi, logger, useAuth, useStellarWalletsKit, getWalletSecretAnyLocal, Keypair, submitBurnRedeemSingleClient) so no identifiers are lost after deduplication.app/test-locale/page.tsx (1)
1-8:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix unsupported
metadataexport in client component (app/test-locale/page.tsx).
app/test-locale/page.tsxis marked with"use client"but exportsexport const metadata, which Next.js App Router does not support from Client Components (it will error or be ignored). Sinceapp/test-locale/layout.tsxalready exportsmetadatafor this route, remove themetadataexport frompage.tsxand keep the page as a client component for theuseStateUI.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/test-locale/page.tsx` around lines 1 - 8, Remove the unsupported client-side metadata export: since this file is a Client Component (it begins with "use client") and the route's metadata is already provided by the layout's metadata export, delete the line/export "export const metadata" from the page component (keep the "use client" and any useState/UI code intact) so the page no longer exports metadata and relies on the layout's metadata.contexts/auth-context.tsx (2)
72-142:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftCritical: Unresolved merge conflict between two session hydration strategies.
This conflict is between:
- HEAD branch (lines 73-76): Simple
useEffectthat callsgetStoredAuth()on mount- upstream/dev branch (lines 78-141): Complex
validateSessionthat makes an API call togetMe()to verify the httpOnly cookie is still validGiven the PR's shift to httpOnly cookie authentication, the upstream/dev branch's approach (validating the session server-side) appears more correct. The HEAD branch would trust stale sessionStorage data without server verification.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@contexts/auth-context.tsx` around lines 72 - 142, Remove the unresolved merge markers and discard the simple HEAD useEffect; keep the upstream/dev session validation logic: preserve the validateSession useCallback that calls getStoredAuth(), imports and awaits getMe(), handles 401 by removing USER_ID_KEY and STELLAR_ADDRESS_KEY from sessionStorage, calls clearPasscode(), sets sessionError and setState appropriately, and on other errors sets sessionError and a non-authenticated state; ensure the useEffect that calls void validateSession() remains and that validateSession is included in its dependency array so session validation runs on mount.
220-223:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
validateSessionis referenced but may not exist after conflict resolution.Line 221 includes
validateSessionin theuseMemodependencies, and line 223 exposes it asrefetchSession. However,validateSessionis only defined in the upstream/dev branch of the merge conflict (lines 79-137). If the HEAD branch is kept, this will cause a reference error.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@contexts/auth-context.tsx` around lines 220 - 223, The memoized auth object references validateSession (in the dependency array and as refetchSession) but that function is missing after the merge; restore the validateSession implementation (the function from upstream that performs session validation) and re-export it as refetchSession, or if the intended function is named differently in HEAD, replace validateSession with the actual function name used (and update the dependency array) so the useMemo and refetchSession export only reference defined symbols (check functions around lines where validateSession originally lived and the useMemo block).app/[locale]/page.tsx (2)
4-9:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
metadataexport is invalid in a'use client'component.Next.js metadata exports (
export const metadata) only work in Server Components. This file has'use client'at line 1, so the metadata export will be silently ignored. Move the metadata to a separatelayout.tsxor convert this to a server component if metadata is needed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`[locale]/page.tsx around lines 4 - 9, The file exports a client-only component (it has the 'use client' directive) but also exports Next.js page metadata via export const metadata, which only works in Server Components; fix by removing the metadata export from this client component and moving the metadata object into the corresponding route-level layout (create or update layout.tsx to export const metadata = { ... }), or alternatively remove the 'use client' directive (making page.tsx a Server Component) if the page can be server-side — update the symbol export named metadata and ensure only server components (e.g., layout.tsx or a server-converted page) export metadata.
215-343:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winCritical: Multiple unresolved merge conflicts throughout the JSX render.
The file has at least 4 additional unresolved merge conflicts in the JSX (lines 215-231, 241-275, 287-294, 337-343). These conflicts primarily involve:
- Responsive className additions (
md:variants)- Translation function usage vs hardcoded strings
BalanceSkeletonvs simple...loading statesThe file is currently unparseable and must be resolved before merge.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`[locale]/page.tsx around lines 215 - 343, The JSX contains unresolved Git conflict markers that make the file unparseable; remove all conflict markers and pick consistent variants for the affected pieces: in the wallet block (the JSX around showBalance / balanceLoading / acbuUsd) keep the responsive md: className additions and the i18n calls (use t(...) instead of hardcoded "ACBU"/"Wallet balance"/"Fiat"/"Recent Activity"/"View all"); reconcile the loading UI to use the new BalanceSkeleton and formatAmount utilities (replace the plain "..."/hardcoded strings with <BalanceSkeleton /> and formatAmount(acbuUsd, 2)), ensure the currency fallback texts use t(...) where present, and preserve behavior in RetryErrorBlock and the features mapping; search for the unique symbols showBalance, balanceLoading, acbuUsd, BalanceSkeleton, formatAmount, fiatUsdInfo, fiatAccounts, RetryErrorBlock, features and remove all <<<<<<<, =======, >>>>>>> markers while keeping one consistent implementation.components/error-boundary.tsx (1)
28-36:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
this.props.levelis referenced butlevelis not defined in Props.The
componentDidCatchmethod referencesthis.props.level(line 30), but thePropsinterface (lines 8-11) no longer includeslevel. This will always beundefined, causing the error reporter to receive'component'as the fallback.If the
levelprop was intentionally removed, remove the reference. If it's still needed, add it back to the Props interface.🔧 Option 1: Remove the reference
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { errorReporter.reportError(error, { - level: this.props.level ?? 'component', + level: 'component', context: {🔧 Option 2: Add level back to Props
interface Props { children: ReactNode; fallback?: ReactNode; + level?: 'page' | 'component' | 'app'; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/error-boundary.tsx` around lines 28 - 36, The Props interface is missing the level property while componentDidCatch reads this.props.level; restore level by adding an optional level?: string (or more specific union if desired) to the Props interface so errorReporter.reportError receives the intended prop value, or if the prop is intentionally removed, delete the this.props.level reference and pass only the fallback 'component' to errorReporter.reportError; locate the Props interface and the componentDidCatch method to make the corresponding addition or removal (symbols: Props, componentDidCatch, this.props.level, errorReporter.reportError).app/layout.tsx (1)
88-161:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winCritical: Unresolved merge conflict in RootLayout function.
The
RootLayoutfunction has unresolved merge conflict markers that break the entire application. One branch returns onlychildren(line 92), while the other returns the full HTML document structure with providers, theme, and analytics (lines 103-159).The
return children;branch appears incorrect for a root layout, as it would strip away all providers, theming, and the HTML document wrapper.🔧 Suggested resolution
Keep the full HTML structure from the
upstream/devbranch and integrate the accessibility fix for zoom:-<<<<<<< HEAD -}: { - children: React.ReactNode; -}) { - return children; -======= }: Readonly<{ children: React.ReactNode }>) { // ... full HTML layout implementation ->>>>>>> origin/dev }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/layout.tsx` around lines 88 - 161, The file has unresolved git conflict markers in the RootLayout function: one branch returns only "return children;" which removes the full HTML structure and all providers; the correct fix is to remove the conflict markers and keep the full HTML document version that uses "const headersList = await headers(); const nonce = headersList.get('x-nonce') ?? undefined; const lang = 'en';" and returns the <html>... structure containing the <head> script (matchMedia), <body> with ThemeProvider, GlobalErrorHandler, OfflineIndicator, ErrorBoundary, I18nProvider, AuthProvider, AppLayout wrapping {children}, WalletSetupModal, Toaster and <Analytics nonce={nonce} crossOrigin="anonymous" />; ensure you also integrate the accessibility zoom fix referenced in the comment into that full layout, and remove the erroneous "return children;" branch and all <<<<<<<, =======, >>>>>>> markers so the module exports a single valid RootLayout.middleware.ts (1)
3-75:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftFix unresolved merge conflicts in middleware.ts (compose next-intl + CSP logic into a single middleware)
middleware.tsstill contains<<<<<<</>>>>>>>conflict markers, leaving two different middleware implementations (next-intlcreateMiddleware(...)vs a custommiddleware(request: NextRequest)with.md404 + CSP nonce directives) and two conflictingexport const config.matcherdefinitions.- Resolve the conflicts by keeping only one exported
middleware+ oneexport const config, and compose next-intl’s middleware with the CSP/security logic (e.g., call the next-intl handler and return its redirect/rewrite response, then apply CSP headers in the composed flow).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@middleware.ts` around lines 3 - 75, Resolve the merge markers and produce one exported middleware that composes next-intl's createMiddleware with the CSP/security logic: remove the conflict markers and keep a single export function middleware(request: NextRequest) that first blocks .md (return new NextResponse(null,{status:404})), then generates the nonce (const nonce = ...), builds the cspDirectives string (from the existing cspDirectives object), then invoke the next-intl handler produced by createMiddleware (call it intlMiddleware or createMiddleware({... defaultLocale: 'en', locales: [...]}) and await its result for the given request), capture its NextResponse, and before returning set the CSP header (response.headers.set('Content-Security-Policy', assembledCsp)) and any nonce-related headers; finally export a single export const config = { matcher: [...] } using the intended matcher block (remove the duplicate). Ensure you reference and use createMiddleware, middleware(request: NextRequest), NextResponse, cspDirectives, nonce, and config.matcher when applying these changes.app/savings/withdraw/page.tsx (2)
108-132:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winResolve the checked-in merge conflict in the recipient section.
This hunk still contains
<<<<<<<,=======, and>>>>>>>, so the page will not compile. It also leaves two incompatible UIs merged together (Your accountvs. editable recipient flow).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/savings/withdraw/page.tsx` around lines 108 - 132, Resolve the merge conflict markers and pick the intended recipient UI: remove the <<<<<<< HEAD / ======= / >>>>>>> origin/dev lines and either restore the original static label block (label htmlFor="withdraw-account" with text "Your account") or keep the editable recipient flow (div with label htmlFor="withdraw-recipient", Button using handleToggleEdit and text based on editingRecipient); ensure the remaining label's htmlFor matches the corresponding input id used elsewhere (withdraw-account or withdraw-recipient) and keep the Button, handleToggleEdit, and editingRecipient references intact if you choose the editable flow so the component compiles.
54-60:⚠️ Potential issue | 🟠 Major | ⚡ Quick winShow an error when the receive address cannot be loaded.
Right now this path only logs. If
getReceive()fails,userstays empty, the form stays disabled, and the user gets no actionable feedback.💡 Proposed fix
.catch((e) => { logger.error( e instanceof Error ? e.message : "Failed to load receive address", ); + if (!cancelled) { + setError( + e instanceof Error + ? e.message + : "Failed to load receive address", + ); + } });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/savings/withdraw/page.tsx` around lines 54 - 60, The catch block for getReceive() currently only logs via logger.error; update it to set a user-facing error state (e.g., receiveError using useState) and clear any loading flag so the UI can show feedback and enable the form for retry, while still logging the original error; then render that receiveError message in the component near the form (so the user sees actionable feedback) and reference getReceive and logger.error in the updated catch handling.app/error.tsx (1)
38-111:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winResolve the leftover merge conflict markers before merge.
<<<<<<<,=======, and>>>>>>>are still checked in here, so this file does not parse. It also leaves two competing error UIs and reset flows in the same component.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/error.tsx` around lines 38 - 111, Remove the leftover Git conflict markers and duplicate blocks so the file parses: delete the lines with <<<<<<<, =======, and >>>>>>> and keep a single coherent error UI (prefer the newer block that includes AlertTriangle, development details, and the action buttons). Ensure handleGoHome and handleReset (and their uses on the two Buttons) are present and wired to startTransition/router.refresh/reset as in the newer block, remove the older simplistic error div, and verify imports for AlertTriangle, RefreshCw, Home, Button and any hooks (router/startTransition/reset) are present and used consistently with the kept UI; finally confirm error.digest rendering remains intact.
🟠 Major comments (7)
tests/accessibility.spec.ts-121-157 (1)
121-157:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFail the interaction tests when the target controls are missing.
Both flows silently skip the interaction when the selector lookup fails, then run Axe on the untouched page and still pass. That turns these into false-positive tests for the mint/send dialogs instead of real regression coverage.
Suggested tightening
- if (fiatSelect) { - // Check if there are options - const options = await fiatSelect.locator('option').count(); - if (options > 1) { - await fiatSelect.selectOption({ index: 1 }); - } - } + await expect(fiatSelect, 'Mint flow should expose a fiat account selector').not.toBeNull(); + const options = await fiatSelect!.locator('option').count(); + expect(options).toBeGreaterThan(1); + await fiatSelect!.selectOption({ index: 1 }); - if (amountInput) { - await amountInput.fill('100'); - } + await expect(amountInput, 'Mint flow should expose an amount input').not.toBeNull(); + await amountInput!.fill('100'); - if (await mintButton.isVisible({ timeout: 3000 }).catch(() => false)) { - await mintButton.click(); - - // Check dialog accessibility - const dialog = page.locator('[role="dialog"]'); - await expect(dialog).toBeVisible({ timeout: 5000 }); - } + await expect(mintButton).toBeVisible(); + await mintButton.click(); + await expect(page.locator('[role="dialog"]')).toBeVisible({ timeout: 5000 });- if (newTransferButton) { - await newTransferButton.click(); - - // Check dialog - const dialog = page.locator('[role="dialog"]'); - await expect(dialog).toBeVisible({ timeout: 5000 }); - - // Test tab navigation (just a few tabs to check focus management) - await page.keyboard.press('Tab'); - await page.waitForTimeout(100); - await page.keyboard.press('Tab'); - await page.waitForTimeout(100); - } + await expect(newTransferButton, 'Send flow should expose a way to create a transfer').not.toBeNull(); + await newTransferButton!.click(); + await expect(page.locator('[role="dialog"]')).toBeVisible({ timeout: 5000 }); + await page.keyboard.press('Tab'); + await page.waitForTimeout(100); + await page.keyboard.press('Tab'); + await page.waitForTimeout(100);Also applies to: 193-205
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/accessibility.spec.ts` around lines 121 - 157, The tests currently skip interactions when controls are missing, causing false positives; update the interaction flow to assert and fail fast when required elements are not found or not visible: for fiatSelect ensure the locator (fiatSelect) exists and is visible (or throw/expect to fail) before counting options; for the amount input replace the optional fallback logic by asserting that at least one of amountSelectors yields a visible locator (amountInput) and fail the test if none are found; for mintButton assert it is visible before clicking and fail if not, and similarly assert the dialog locator is visible after click; apply the same presence-and-fail checks to the corresponding code block at the later lines (the 193-205 flow) so missing controls cause test failures rather than silent skips.tests/accessibility.spec.ts-46-50 (1)
46-50:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRe-enable
color-contrastin Axe scans and assert expected interactions occurred.
disableRules(['color-contrast'])removes WCAG 2 AA “Contrast (Minimum)” (common WCAG 1.4.3 failures), so the scans can stay green while contrast issues slip through. (tests/accessibility.spec.ts: 46-50, 60-64, 74-78, 88-92, 160-163, 208-211)- In the
mint form interactions should be accessibleandsend form interactions should be accessibletests, clicks/fills are conditional (e.g., only click if the button/inputs are visible), but there’s no assertion that the intended interaction actually happened before running Axe—so the test can pass even if the interaction never occurred.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/accessibility.spec.ts` around lines 46 - 50, The Axe scans currently disable the "color-contrast" rule via disableRules(['color-contrast']) in the AxeBuilder usage, and the interaction tests "mint form interactions should be accessible" and "send form interactions should be accessible" perform conditional clicks/fills without asserting that the interaction actually occurred; remove "color-contrast" from the disableRules call (or remove the disableRules invocation entirely) in all AxeBuilder instances so WCAG 1.4.3 is enforced, and in the two interaction tests add explicit assertions after each conditional interaction (for example assert the button was clicked by checking the expected DOM change, the modal became visible, or inputs contain the filled value) so the Axe analyze() runs only after confirming the intended interaction completed.app/send/[id]/page.tsx-264-288 (1)
264-288:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard the
dateTimeattribute against malformed timestamps.
safeFormatDateavoids bad display text, butnew Date(createdAt).toISOString()andnew Date(completedAt).toISOString()still throw for non-empty invalid values. A malformed API timestamp will crash the whole detail view instead of degrading gracefully.🛠️ Suggested fix
+function getSafeDateTime(iso: string | undefined) { + if (!iso) return undefined; + const date = new Date(iso); + return Number.isNaN(date.getTime()) ? undefined : date.toISOString(); +} + +function safeFormatDate(iso: string | undefined) { + if (!iso) return ''; + const date = new Date(iso); + if (Number.isNaN(date.getTime())) return ''; + return date.toLocaleString(undefined, { + dateStyle: "medium", + timeStyle: "short", + }); +} + +const createdDateTime = getSafeDateTime(createdAt); +const completedDateTime = getSafeDateTime(completedAt); + - {createdAt && ( + {createdAt && safeFormatDate(createdAt) && ( <div className="flex justify-between text-sm"> <span id="created-label" className="text-muted-foreground font-medium"> Created </span> <time - dateTime={new Date(createdAt).toISOString()} + dateTime={createdDateTime} aria-labelledby="created-label" > {safeFormatDate(createdAt)} </time> </div> )} - {completedAt && ( + {completedAt && safeFormatDate(completedAt) && ( <div className="flex justify-between text-sm"> <span id="completed-label" className="text-muted-foreground font-medium"> Completed </span> <time - dateTime={new Date(completedAt).toISOString()} + dateTime={completedDateTime} aria-labelledby="completed-label" > {safeFormatDate(completedAt)} </time> </div> )}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/send/`[id]/page.tsx around lines 264 - 288, The time elements use new Date(...).toISOString() which throws for invalid timestamps; guard these by parsing createdAt and completedAt first (e.g., via Date.parse or new Date(...) and checking isNaN(date.getTime())) and only pass dateTime when the parsed Date is valid, otherwise omit the dateTime attribute (or pass undefined) so the UI degrades gracefully; update the code around the createdAt/completedAt time elements in page.tsx (references: createdAt, completedAt, safeFormatDate) to compute and reuse the validated Date before rendering.app/[locale]/layout.tsx-36-40 (1)
36-40:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAccessibility issue:
maximumScale: 1prevents users from zooming.Setting
maximumScale: 1restricts users from zooming in, which violates WCAG 1.4.4 (Resize Text). This is particularly important for users with low vision. Notably,app/layout.tsxexplicitly removes this restriction with a comment "FIXED: Removed maximumScale to allow zooming."🔧 Proposed fix
export const viewport: Viewport = { width: 'device-width', initialScale: 1, - maximumScale: 1, + userScalable: true, }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/`[locale]/layout.tsx around lines 36 - 40, The viewport config exported as viewport currently sets maximumScale: 1 which blocks user zoom; remove the maximumScale property from the viewport object in the file (the exported constant named viewport in layout.tsx) so the browser can allow zooming, leaving width and initialScale as-is (or set maximumScale to undefined/omit it entirely) to satisfy accessibility requirements.app/api/support/route.ts-79-83 (1)
79-83:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRedact the ticket body before logging misconfiguration failures.
This logs the full support payload, including
name,message, into server logs. That creates an avoidable PII retention path for a config error. Log onlyticketId/pageor a redacted shape here.🔒 Proposed fix
if (!intakeUrl) { console.warn( '[Support] No support intake configured. Ticket not forwarded.', - JSON.stringify(payload, null, 2), + JSON.stringify({ + ticketId: payload.ticketId, + page: payload.page, + createdAt: payload.createdAt, + }), );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/support/route.ts` around lines 79 - 83, The current intakeUrl check logs the full support payload (payload) including PII; change the logging in the if (!intakeUrl) branch to avoid printing name/email/message by constructing and logging a redacted shape (e.g., { ticketId, page } or { ticketId, page, message: '[REDACTED]' }) instead of payload; update the console.warn call in route.ts (the block referencing intakeUrl and payload) to only include those non-PII fields.app/api/support/route.ts-94-108 (1)
94-108:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle upstream network failures and timeouts explicitly.
If the intake host times out, has a DNS issue, or resets the connection,
fetchwill reject and this route falls through as an uncaught 500. That leaves the help form failing unpredictably instead of returning a controlled support error.🛠️ Proposed fix
- const forwardResponse = await fetch(intakeUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload), - }); + let forwardResponse: Response; + try { + forwardResponse = await fetch(intakeUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + signal: AbortSignal.timeout(10_000), + }); + } catch { + return NextResponse.json( + { error: 'Support intake is temporarily unreachable.' }, + { status: 502 }, + ); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/api/support/route.ts` around lines 94 - 108, The fetch to intakeUrl can throw on network failures/timeouts; update the logic around forwardResponse to use an AbortController with a configurable timeout and wrap the fetch call in a try/catch so any thrown errors (DNS, ECONNRESET, timeout) are caught and result in a controlled NextResponse.json error (e.g., status 502 and a concise message). Specifically, modify the block that calls fetch(intakeUrl, ...) and references forwardResponse to create an AbortController, pass its signal into fetch, implement a timeout that aborts the controller, and in the catch branch return the same structured JSON error as the existing non-ok path; keep the existing non-ok handling for HTTP error responses.app/savings/deposit/page.tsx-44-57 (1)
44-57:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSurface receive-address lookup failures to the form.
This
catchonly logs. WhengetReceive()fails, the account field stays blank and the submit button remains disabled, but the user never gets told why they cannot continue.💡 Proposed fix
useEffect(() => { let cancelled = false; userApi.getReceive(opts).then(async (data) => { const uri = (data.pay_uri ?? data.alias) as string | undefined; if (!uri || typeof uri !== 'string') return; const resolved = await resolveUserUri(uri, opts); if (!cancelled) setUser(resolved); }).catch((e) => { logger.error(e instanceof Error ? e.message : 'Failed to load receive address'); + if (!cancelled) { + setError(e instanceof Error ? e.message : 'Failed to load receive address'); + } }).finally(() => { if (cancelled) return; });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/savings/deposit/page.tsx` around lines 44 - 57, The effect that calls userApi.getReceive only logs failures; add a piece of state (e.g., receiveError / setReceiveError) in this component and in the catch handler setReceiveError(e instanceof Error ? e.message : 'Failed to load receive address') so the form can display the message; also clear the error when a successful response sets the user (call setReceiveError(undefined) alongside setUser(resolved)), and update the form/submit enablement logic to treat a non-empty receiveError as a blocking error so the message is shown and the submit remains disabled with feedback.
🟡 Minor comments (3)
lib/stellar/burning.ts-84-88 (1)
84-88:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove unnecessary unsafe cast on
sendRes.errorResult(lib/stellar/burning.ts:84-88)
rpcServer.sendTransaction()returnserrorResultas anxdr.TransactionResult(SDK-parsed), so it already providestoXDR("base64"). The current(sendRes.errorResult as unknown as { toXDR: ... })cast is unnecessary; callsendRes.errorResult.toXDR("base64")directly for proper type safety. TheString(status.resultXdr)usage in the FAILED path is aligned withgetTransaction’s base64resultXdr.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/stellar/burning.ts` around lines 84 - 88, The code is using an unnecessary unsafe cast on sendRes.errorResult; update the ERROR branch in lib/stellar/burning.ts to call sendRes.errorResult.toXDR("base64") directly (no (unknown as ...) cast) when sendRes.errorResult is present, preserve the existing "unknown" fallback, and pass that base64 XDR into formatSorobanError (ensure references: sendRes, errorResult, formatSorobanError).i18n/messages/en-NG.json-2-14 (1)
2-14:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd the new
home.approx_usdkey here as well.
i18n/messages/en.jsonalready defineshome.approx_usd, buten-NGdoes not. That leaves this locale with a missing message anywhere the new label is used.➕ Proposed fix
"some_currencies_missing_rate": "Some currencies missing a rate", - "usd": "USD" + "usd": "USD", + "approx_usd": "≈ USD"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@i18n/messages/en-NG.json` around lines 2 - 14, Add the missing translation key home.approx_usd to the en-NG locale by copying the value used for home.approx_usd from the primary en locale; locate the home object in i18n/messages/en-NG.json and insert the same "approx_usd" entry so the en-NG file matches the en.json key set.test-results/.last-run.json-1-4 (1)
1-4:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTest results file should not be committed.
This transient test output file will change with every test run and creates unnecessary noise in version control. Add
test-results/to.gitignoreand remove this file from the PR.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test-results/.last-run.json` around lines 1 - 4, Remove the committed transient test output file test-results/.last-run.json from the PR and add the test-results/ directory to .gitignore so it isn't tracked going forward; specifically, delete test-results/.last-run.json from the branch (or revert the add), then open .gitignore and append a line "test-results/" (or ensure that entry exists) and commit those changes so future test runs won't create VCS noise.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: afb47689-36e6-489d-97ac-4f6709e30198
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (73)
.gitignoreapp/[locale]/activity/layout.tsxapp/[locale]/activity/page.tsxapp/[locale]/auth/2fa/page.tsxapp/[locale]/auth/error.tsxapp/[locale]/auth/layout.tsxapp/[locale]/auth/signin/page.tsxapp/[locale]/auth/signup/page.tsxapp/[locale]/auth/wallet-setup/page.tsxapp/[locale]/layout.tsxapp/[locale]/page.tsxapp/api/support/route.tsapp/bills/layout.tsxapp/burn/layout.tsxapp/burn/page.tsxapp/business/layout.tsxapp/business/page.tsxapp/currency/layout.tsxapp/currency/page.tsxapp/error.tsxapp/fiat/layout.tsxapp/fiat/page.tsxapp/globals.cssapp/help/layout.tsxapp/help/page.tsxapp/layout.tsxapp/lending/layout.tsxapp/me/layout.tsxapp/me/page.tsxapp/me/settings/layout.tsxapp/mint/layout.tsxapp/mint/mint.test.tsxapp/mint/page.tsxapp/rates/layout.tsxapp/rates/page.tsxapp/recovery/layout.tsxapp/reserves/layout.tsxapp/reserves/page.tsxapp/savings/deposit/page.tsxapp/savings/layout.tsxapp/savings/page.tsxapp/savings/withdraw/page.tsxapp/send/[id]/page.tsxapp/send/layout.tsxapp/send/page.tsxapp/send/send.test.tsxapp/test-locale/layout.tsxapp/test-locale/page.tsxapp/transactions/layout.tsxapp/wallet/layout.tsxapp/wallet/page.tsxcomponents/error-boundary.tsxcontexts/auth-context.tsxglobal.d.tshooks/__tests__/use-api-error.test.tshooks/use-api.tshooks/use-debounce.tsi18n/messages/en-KE.jsoni18n/messages/en-NG.jsoni18n/messages/en.jsoni18n/request.tslib/__tests__/api-error.test.tslib/__tests__/clipboard.test.tslib/api/client.tslib/stellar/burning.tslib/wallet-storage.tsmiddleware.tsnext.config.mjspackage.jsonplaywright-report/index.htmlplaywright.config.tstest-results/.last-run.jsontests/accessibility.spec.ts
💤 Files with no reviewable changes (1)
- app/send/send.test.tsx
| <<<<<<< HEAD | ||
| .pnpm-store/ | ||
| .history/* | ||
| .env | ||
| ======= | ||
| package-lock.json | ||
| pnpm-lock.yaml | ||
|
|
||
| # prevent documentation files from being accidentally served as static assets | ||
| /public/**/*.md | ||
|
|
||
| >>>>>>> origin/dev |
There was a problem hiding this comment.
Critical: Unresolved merge conflict in .gitignore.
The file contains unresolved Git merge conflict markers. Additionally:
- Line 32 (
.env) is redundant since line 20 already covers.env* - Caution: The
origin/devside ignorespackage-lock.jsonandpnpm-lock.yaml. Ignoring lock files prevents reproducible builds and is generally not recommended. Consider keeping lock files in version control.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.gitignore around lines 29 - 40, Remove the unresolved Git conflict markers
(<<<<<<< HEAD, =======, >>>>>>> origin/dev) and merge the two sides into a clean
.gitignore: keep .pnpm-store/ and .history/*, remove the redundant `.env` entry
(since `.env*` is already present), and do not ignore lock files—remove
`package-lock.json` and `pnpm-lock.yaml` from the ignore list so locks remain
committed; also retain the `/public/**/*.md` rule that prevents docs from being
served as static assets.
| <<<<<<< HEAD:app/[locale]/page.tsx | ||
| ======= | ||
| import { BalanceSkeleton } from '@/components/ui/balance-skeleton'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { RetryErrorBlock } from '@/components/ui/retry-error-block'; | ||
| >>>>>>> upstream/dev:app/page.tsx |
There was a problem hiding this comment.
Critical: Unresolved merge conflict in imports.
The file contains unresolved Git merge conflict markers in the import section, which prevents the code from parsing. The conflict is between keeping/removing BalanceSkeleton, Button, and RetryErrorBlock imports.
🧰 Tools
🪛 Biome (2.4.15)
[error] 27-28: Expected a statement but instead found '<<<<<<< HEAD:app/[locale]/page.tsx
======='.
(parse)
[error] 32-32: Expected a statement but instead found '>>>>>>> upstream/dev:app/page.tsx'.
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`[locale]/page.tsx around lines 27 - 32, Remove the unresolved Git
conflict markers in the import block and restore a single valid import section:
remove the <<<<<<<, =======, and >>>>>>> lines, then either (a) keep and combine
the imports for BalanceSkeleton, Button, and RetryErrorBlock from their
respective module paths (e.g., import { BalanceSkeleton } from
'`@/components/ui/balance-skeleton`'; import { Button } from
'`@/components/ui/button`'; import { RetryErrorBlock } from
'`@/components/ui/retry-error-block`';) if those symbols are used in the file, or
(b) delete the unused imports (BalanceSkeleton, Button, RetryErrorBlock) if they
are not referenced; ensure the final file parses without conflict markers and
only contains valid import statements referencing those symbols where needed.
| <<<<<<< HEAD:app/[locale]/page.tsx | ||
| const t = useTranslations('home'); | ||
| const format = useFormatter(); | ||
|
|
||
| const features = [ | ||
| { title: t('features.send.title'), description: t('features.send.description'), icon: Send, href: '/send', color: 'bg-blue-100 dark:bg-blue-900/30', iconColor: 'text-blue-600 dark:text-blue-400' }, | ||
| { title: t('features.mint.title'), description: t('features.mint.description'), icon: Coins, href: '/mint', color: 'bg-purple-100 dark:bg-purple-900/30', iconColor: 'text-purple-600 dark:text-purple-400' }, | ||
| { title: t('features.simulated_bank.title'), description: t('features.simulated_bank.description'), icon: Building2, href: '/fiat', color: 'bg-green-100 dark:bg-green-900/30', iconColor: 'text-green-600 dark:text-green-400' }, | ||
| { title: t('features.rates.title'), description: t('features.rates.description'), icon: TrendingUp, href: '/rates', color: 'bg-amber-100 dark:bg-amber-900/30', iconColor: 'text-amber-600 dark:text-amber-400' }, | ||
| ]; | ||
| ======= | ||
| useScrollRestoration('/', !loading); | ||
| >>>>>>> origin/dev:app/page.tsx |
There was a problem hiding this comment.
Critical: Unresolved merge conflict in features array and scroll restoration.
Another unresolved merge conflict exists between:
- HEAD: i18n-driven features array using
useTranslations - upstream/dev:
useScrollRestorationhook call
Both pieces of logic may be needed - resolve by keeping both the translations setup AND the scroll restoration hook.
🧰 Tools
🪛 Biome (2.4.15)
[error] 137-137: Expected a statement but instead found '<<<<<<< HEAD:app/[locale]/page.tsx'.
(parse)
[error] 147-148: Expected a statement but instead found '=======
useScrollRestoration('/', !loading)'.
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/`[locale]/page.tsx around lines 137 - 149, Resolve the merge conflict by
keeping both the i18n-driven features setup and the scroll restoration call:
restore the declarations that call useTranslations('home') and useFormatter()
and the features array (references: useTranslations, useFormatter, features) and
also add the scroll restoration hook call useScrollRestoration('/', !loading)
(ensure a valid loading variable/prop is in scope); remove the conflict markers
(<<<<<<<, =======, >>>>>>>) so both pieces coexist in the component.
| account_number: data.accountNumber.trim(), | ||
| bank_code: data.bankCode.trim(), | ||
| account_name: data.accountName.trim(), |
There was a problem hiding this comment.
Using undefined data variable instead of values parameter.
The onSubmit handler receives values as its parameter, but lines 207-209, 225-226, 293-294, and 301-302 reference data which is undefined. This will cause ReferenceError at runtime.
🐛 Proposed fix
Replace all occurrences of data. with values.:
const recipientAccount: BurnRecipientAccount = {
- account_number: data.accountNumber.trim(),
- bank_code: data.bankCode.trim(),
- account_name: data.accountName.trim(),
+ account_number: values.accountNumber.trim(),
+ bank_code: values.bankCode.trim(),
+ account_name: values.accountName.trim(),
type: "bank",
};Apply the same fix at lines 225-226, 293-294, and 301-302.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| account_number: data.accountNumber.trim(), | |
| bank_code: data.bankCode.trim(), | |
| account_name: data.accountName.trim(), | |
| account_number: values.accountNumber.trim(), | |
| bank_code: values.bankCode.trim(), | |
| account_name: values.accountName.trim(), |
🧰 Tools
🪛 Biome (2.4.15)
[error] 207-207: expected } but instead found :
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/burn/page.tsx` around lines 207 - 209, The onSubmit handler is using an
undefined identifier `data` instead of the incoming `values` parameter; locate
the onSubmit function (the submit handler that accepts `values`) and replace all
references of `data.` with `values.` for fields such as `accountNumber`,
`bankCode`, `accountName`, and the other occurrences mentioned (the later uses
around the same submit flow). Ensure you update each instance (e.g., where
`data.accountNumber`, `data.bankCode`, `data.accountName` and the other two
`data.` usages are referenced) so they read `values.accountNumber`,
`values.bankCode`, `values.accountName`, etc., to avoid the ReferenceError at
runtime.
| <<<<<<< HEAD | ||
| { id: 'sme', title: 'SME Services', description: 'Business accounts, transfers & statements', icon: Briefcase, badge: 'Pro', href: '/sme' }, | ||
| { id: 'salary', title: 'Payroll', description: 'Disburse salaries and manage batches', icon: Users, badge: 'New', href: '/salary' }, | ||
| { id: 'campaigns', title: 'Crowdfunding', description: 'Raise funds for projects via Trivela', icon: Zap, badge: 'Alpha', href: '/campaigns/1' }, | ||
| { id: 'enterprise', title: 'Enterprise', description: 'Bulk transfers and treasury management', icon: PiggyBank, href: '/enterprise' }, | ||
| { id: 'gateway', title: 'Payment Gateway', description: 'Create charges and manage escrow', icon: CreditCard, href: '/gateway' }, | ||
| ======= | ||
| { id: 'sme', titleKey: 'sme', descKey: 'smeDesc', icon: Briefcase, badge: 'Pro', href: '/business/sme' }, | ||
| { id: 'salary', titleKey: 'payroll', descKey: 'payrollDesc', icon: Users, badge: 'New', href: '/salary' }, | ||
| { id: 'campaigns', titleKey: 'crowdfunding', descKey: 'crowdfundingDesc', icon: Zap, badge: 'Alpha', href: '/campaigns/1' }, | ||
| { id: 'enterprise', titleKey: 'enterprise', descKey: 'enterpriseDesc', icon: PiggyBank, href: '/enterprise' }, | ||
| { id: 'gateway', titleKey: 'gateway', descKey: 'gatewayDesc', icon: CreditCard, href: '/gateway' }, | ||
| >>>>>>> origin/dev |
There was a problem hiding this comment.
Critical: Unresolved merge conflict blocks compilation.
This file contains Git merge conflict markers (<<<<<<<, =======, >>>>>>>) that must be resolved before the code can compile. The businessServices array has two conflicting implementations:
- HEAD: Uses hardcoded
title/descriptionstrings - origin/dev: Uses i18n keys
titleKey/descKey
The rendering code at lines 103 and 106 uses titleKey/descKey, so if you choose the HEAD version, the app will crash at runtime.
🧰 Tools
🪛 Biome (2.4.15)
[error] 24-24: Expected an expression for the left hand side of the << operator.
(parse)
[error] 24-24: Expected an expression but instead found '<<'.
(parse)
[error] 24-24: Expected an expression but instead found '<<'.
(parse)
[error] 25-25: expected ... but instead found id
(parse)
[error] 25-25: expected } but instead found :
(parse)
[error] 25-25: Expected an identifier but instead found ''SME Services''.
(parse)
[error] 25-25: Expected an identifier but instead found ''Business accounts, transfers & statements''.
(parse)
[error] 25-25: Expected a JSX attribute but instead found ','.
(parse)
[error] 25-25: Expected an identifier but instead found ''Pro''.
(parse)
[error] 25-25: Expected an identifier but instead found ''/sme''.
(parse)
[error] 25-25: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 26-26: expected } but instead found :
(parse)
[error] 26-26: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 27-27: expected } but instead found :
(parse)
[error] 27-27: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 28-28: expected } but instead found :
(parse)
[error] 28-28: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 29-29: expected } but instead found :
(parse)
[error] 29-29: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 31-31: expected } but instead found :
(parse)
[error] 31-31: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 32-32: expected } but instead found :
(parse)
[error] 32-32: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 33-33: expected } but instead found :
(parse)
[error] 33-33: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 34-34: expected } but instead found :
(parse)
[error] 34-34: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 35-35: expected } but instead found :
(parse)
[error] 35-35: Unexpected token. Did you mean {'}'} or }?
(parse)
[error] 36-36: Unexpected token. Did you mean {'>'} or >?
(parse)
[error] 36-36: Unexpected token. Did you mean {'>'} or >?
(parse)
[error] 36-36: Unexpected token. Did you mean {'>'} or >?
(parse)
[error] 36-36: Unexpected token. Did you mean {'>'} or >?
(parse)
[error] 36-36: Unexpected token. Did you mean {'>'} or >?
(parse)
[error] 36-36: Unexpected token. Did you mean {'>'} or >?
(parse)
[error] 36-36: Unexpected token. Did you mean {'>'} or >?
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/business/page.tsx` around lines 24 - 36, The file contains unresolved Git
conflict markers in the businessServices array causing compilation failure;
remove the conflict markers and keep the origin/dev variant (entries using
titleKey and descKey) so they match the renderer that expects titleKey/descKey
(references: businessServices, titleKey, descKey), update any hrefs if needed to
the intended routes (e.g., '/business/sme') and ensure all service objects
consistently include titleKey and descKey fields before saving.
| <<<<<<< HEAD | ||
|
|
||
| ======= | ||
| >>>>>>> origin/dev |
There was a problem hiding this comment.
Unresolved merge conflict (empty conflict block).
Lines 35-38 contain empty merge conflict markers that must be resolved.
🧰 Tools
🪛 Biome (2.4.15)
[error] 35-38: Expected a statement but instead found '<<<<<<< HEAD
=======
origin/dev'.
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contexts/auth-context.tsx` around lines 35 - 38, There is an unresolved merge
conflict marker block in contexts/auth-context.tsx (the leftover <<<<<<< HEAD /
======= / >>>>>>> origin/dev lines) — remove the conflict markers and resolve
the conflict by keeping the correct implementation for the
AuthContext/AuthProvider exports (or merge the differing changes if both sides
had edits), then ensure the file compiles and passes linting; search for symbols
AuthContext and AuthProvider to verify you preserved the intended code and
remove any stray markers.
| if (apiKey) { | ||
| setToken(apiKey); | ||
| } |
There was a problem hiding this comment.
setToken is called but not imported - this will cause a ReferenceError.
Line 50 calls setToken(apiKey) but setToken is never imported. It should be imported from @/lib/api/client. Similarly, line 163 also calls setToken.
🐛 Proposed fix
import * as authApi from '`@/lib/api/auth`';
-import { onAuthError } from '`@/lib/api/client`';
+import { onAuthError, setToken } from '`@/lib/api/client`';
import { clearPasscode } from '`@/lib/passcode-manager`';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contexts/auth-context.tsx` around lines 49 - 51, The code calls
setToken(apiKey) in the auth context (and again later) but never imports
setToken, causing a ReferenceError; import setToken from '`@/lib/api/client`' at
the top of the module and ensure the imported symbol is used for both calls (the
call sites are the setToken(apiKey) occurrences) so the function is defined when
invoked.
| <<<<<<< HEAD | ||
| export { useApiError } from '@/hooks/use-api-error'; | ||
| ======= | ||
| import { getApiErrorMessage } from '@/lib/api/client'; | ||
| >>>>>>> origin/dev |
There was a problem hiding this comment.
Resolve the conflict in the shared API hook module.
hooks/use-api.ts still contains merge markers, so every consumer of this module is at risk of a hard build failure until the export strategy is reconciled.
Also applies to: 18-45
🧰 Tools
🪛 Biome (2.4.15)
[error] 3-3: Expected a statement but instead found '<<<<<<< HEAD'.
(parse)
[error] 5-5: Expected a statement but instead found '======='.
(parse)
[error] 7-7: Expected a statement but instead found '>>>>>>> origin/dev'.
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@hooks/use-api.ts` around lines 3 - 7, The file contains unresolved merge
markers; remove the conflict markers and reconcile the export/import strategy so
the module exports the intended API: either re-export useApiError (export {
useApiError } from '`@/hooks/use-api-error`') or import and
re-export/getApiErrorMessage (import { getApiErrorMessage } from
'`@/lib/api/client`' and export it) — ensure you keep only valid ES module syntax,
include both symbols if consumers need them (useApiError and
getApiErrorMessage), and run the build/tests to verify no remaining conflict
markers in the file (also check the nearby block that spans the affected
region).
| <<<<<<< HEAD | ||
| "start": "next start", | ||
| "test:a11y": "playwright test accessibility.spec.ts", | ||
| "test:a11y:ci": "playwright test accessibility.spec.ts --reporter=github", | ||
| "test:a11y:report": "playwright show-report", | ||
| "a11y": "playwright test" | ||
| ======= | ||
| "typecheck": "tsc --noEmit", | ||
| "generate:icons": "node scripts/generate-pwa-icons.mjs", | ||
| "test": "vitest run", | ||
| "test:watch": "vitest", | ||
| "test:coverage": "vitest run --coverage", | ||
| "test:e2e": "playwright test", | ||
| "start": "next start" | ||
| >>>>>>> upstream/dev |
There was a problem hiding this comment.
Unresolved merge conflicts make package.json invalid.
The merge conflicts at lines 10-24 (scripts), 57-61 (dependencies), and 88-146 (devDependencies) produce invalid JSON. Package managers cannot parse this file, so npm install or pnpm install will fail.
Additionally, line 147-148 has duplicate closing braces that would remain even after conflict resolution.
Resolve all conflicts and validate the JSON structure before committing.
🧰 Tools
🪛 Biome (2.4.15)
[error] 10-10: unexpected character <
(parse)
[error] 10-10: unexpected character <
(parse)
[error] 10-10: unexpected character <
(parse)
[error] 10-10: unexpected character <
(parse)
[error] 10-10: unexpected character <
(parse)
[error] 10-10: unexpected character <
(parse)
[error] 10-10: unexpected character <
(parse)
[error] 10-10: String values must be double quoted.
(parse)
[error] 11-11: End of file expected
(parse)
[error] 11-11: End of file expected
(parse)
[error] 11-11: End of file expected
(parse)
[error] 11-11: End of file expected
(parse)
[error] 12-12: End of file expected
(parse)
[error] 12-12: End of file expected
(parse)
[error] 12-12: End of file expected
(parse)
[error] 12-12: End of file expected
(parse)
[error] 13-13: End of file expected
(parse)
[error] 13-13: End of file expected
(parse)
[error] 13-13: End of file expected
(parse)
[error] 13-13: End of file expected
(parse)
[error] 14-14: End of file expected
(parse)
[error] 14-14: End of file expected
(parse)
[error] 14-14: End of file expected
(parse)
[error] 14-14: End of file expected
(parse)
[error] 15-15: End of file expected
(parse)
[error] 15-15: End of file expected
(parse)
[error] 15-15: End of file expected
(parse)
[error] 16-16: unexpected character =
(parse)
[error] 16-16: unexpected character =
(parse)
[error] 16-16: unexpected character =
(parse)
[error] 16-16: unexpected character =
(parse)
[error] 16-16: unexpected character =
(parse)
[error] 16-16: unexpected character =
(parse)
[error] 16-16: unexpected character =
(parse)
[error] 17-17: End of file expected
(parse)
[error] 17-17: End of file expected
(parse)
[error] 17-17: End of file expected
(parse)
[error] 17-17: End of file expected
(parse)
[error] 18-18: End of file expected
(parse)
[error] 18-18: End of file expected
(parse)
[error] 18-18: End of file expected
(parse)
[error] 18-18: End of file expected
(parse)
[error] 19-19: End of file expected
(parse)
[error] 19-19: End of file expected
(parse)
[error] 19-19: End of file expected
(parse)
[error] 19-19: End of file expected
(parse)
[error] 20-20: End of file expected
(parse)
[error] 20-20: End of file expected
(parse)
[error] 20-20: End of file expected
(parse)
[error] 20-20: End of file expected
(parse)
[error] 21-21: End of file expected
(parse)
[error] 21-21: End of file expected
(parse)
[error] 21-21: End of file expected
(parse)
[error] 21-21: End of file expected
(parse)
[error] 22-22: End of file expected
(parse)
[error] 22-22: End of file expected
(parse)
[error] 22-22: End of file expected
(parse)
[error] 22-22: End of file expected
(parse)
[error] 23-23: End of file expected
(parse)
[error] 23-23: End of file expected
(parse)
[error] 23-23: End of file expected
(parse)
[error] 24-24: unexpected character >
(parse)
[error] 24-24: unexpected character >
(parse)
[error] 24-24: unexpected character >
(parse)
[error] 24-24: unexpected character >
(parse)
[error] 24-24: unexpected character >
(parse)
[error] 24-24: unexpected character >
(parse)
[error] 24-24: unexpected character >
(parse)
[error] 24-24: String values must be double quoted.
(parse)
[error] 24-24: unexpected character /
(parse)
[error] 24-24: String values must be double quoted.
(parse)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` around lines 10 - 24, The package.json contains unresolved git
merge markers in the "scripts", "dependencies", and "devDependencies" sections
and duplicate closing braces; remove the conflict markers (<<<<<<<, =======,
>>>>>>>), pick or merge the intended entries for the "scripts" object (e.g.,
combine needed scripts like "start", "typecheck", test scripts), reconcile and
deduplicate entries in "dependencies" and "devDependencies" so there are no
repeated keys, remove the extra trailing braces, and validate the resulting JSON
(e.g., via a JSON linter or `node -e "JSON.parse(...)"`) before committing.
Closes #347
Help page form submits to nowhere - Fixed Issue #347
Summary by CodeRabbit
New Features
Bug Fixes
Improvements
Testing