Skip to content

Help page form submits to nowhere - Fixed Issue #347#427

Merged
Junman140 merged 48 commits into
Pi-Defi-world:devfrom
Jujukoder:page-form
Jun 4, 2026
Merged

Help page form submits to nowhere - Fixed Issue #347#427
Junman140 merged 48 commits into
Pi-Defi-world:devfrom
Jujukoder:page-form

Conversation

@Jujukoder

@Jujukoder Jujukoder commented May 28, 2026

Copy link
Copy Markdown
Contributor

Closes #347


Help page form submits to nowhere - Fixed Issue #347

Summary by CodeRabbit

  • New Features

    • Added multi-language support with localized UI strings for English, Nigerian English, and Kenyan English
    • Implemented encrypted wallet storage for enhanced security
    • Added support ticket submission system
    • Introduced debouncing for better form input handling
  • Bug Fixes

    • Improved authentication flow handling and error messaging
    • Enhanced form validation with better error feedback
    • Fixed date formatting utilities
  • Improvements

    • Significantly improved accessibility across all pages
    • Better error handling and user-facing error messages
    • Enhanced UI responsiveness and styling consistency
  • Testing

    • Added comprehensive accessibility testing suite

dubemoyibe-star and others added 30 commits April 23, 2026 08:50
- 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…"
Junman140 and others added 15 commits April 28, 2026 03:13
Enhance Multi-Currency Bank Validation and Burn Page Form Logic
…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)
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR integrates next-intl i18n support, refactors authentication to remove client-side API keys, implements real wallet encryption with AES-GCM, adds a support ticket submission API, and improves form validation and error handling across multiple pages. However, it contains numerous unresolved merge conflicts that require resolution before merge.

Changes

i18n and Localization Framework

Layer / File(s) Summary
i18n infrastructure setup
i18n/request.ts, i18n/messages/en.json, i18n/messages/en-NG.json, i18n/messages/en-KE.json, middleware.ts, next.config.mjs
next-intl is wired into the app via createMiddleware in middleware, locale message catalogs for English, Nigerian English, and Kenyan English are provided, and Next.js config wraps with withNextIntl plugin.
Root layout with provider tree
app/[locale]/layout.tsx
Async root layout imports locale from params, fetches i18n messages, and composes NextIntlClientProvider, ErrorBoundary, AuthProvider, AuthGuard, AppLayout, WalletSetupModal, and Analytics around children. Exports metadata and viewport.
Localized dashboard and route layouts
app/[locale]/page.tsx, app/[locale]/activity/layout.tsx, app/[locale]/auth/layout.tsx, app/bills/layout.tsx, app/burn/layout.tsx, app/currency/layout.tsx, app/fiat/layout.tsx, app/help/layout.tsx, app/lending/layout.tsx, app/me/layout.tsx, app/me/settings/layout.tsx, app/mint/layout.tsx, app/rates/layout.tsx, app/recovery/layout.tsx, app/reserves/layout.tsx, app/savings/layout.tsx, app/send/layout.tsx, app/test-locale/layout.tsx, app/transactions/layout.tsx, app/wallet/layout.tsx
Dashboard integrates useTranslations and useFormatter to localize UI strings, format numbers, and render feature cards from translated keys. All route sections now have dedicated layout files exporting metadata and default components.

Authentication and API Infrastructure

Layer / File(s) Summary
Auth context refactoring
contexts/auth-context.tsx, app/[locale]/auth/2fa/page.tsx
Auth context stores and hydrates apiKey from sessionStorage, sets API client tokens during init, computes isAuthenticated from both apiKey and userId. Login and setAuth methods accept apiKey parameter. 2FA page now calls login without api_key, passing only user identifier and stellar address.
API error handling refactoring
hooks/use-api.ts, hooks/use-api-error.test.ts, hooks/__tests__/use-api-error.test.ts, lib/__tests__/api-error.test.ts, lib/api/client.ts
useApiError is re-exported from dedicated @/hooks/use-api-error module instead of being implemented in use-api. setToken becomes deprecated no-op (auth via httpOnly cookies). Error state uses uiError?.message instead of error field. Tests updated to validate new state shape and message mappings.

Support System Integration

Layer / File(s) Summary
Support API endpoint and help page
app/api/support/route.ts, app/help/page.tsx
New POST /api/support endpoint validates contact form submissions, generates unique ticket IDs, and forwards to backend intake service. Help page integrates fetch submission, tracks ticketId and error state, and renders conditional success/error banners with ticket identifier.

Form Validation and Input Handling

Layer / File(s) Summary
Burn page currency-aware validation
app/burn/page.tsx
Replaces basic Zod schema with superRefine for currency-specific validation (NGN digit lengths, KES patterns). Error handler maps backend 400 validation errors to form fields. Submit button uses form.formState.isValid. Locale selection uses navigator.language. UI includes conditional currency-specific placeholders wrapped in Suspense with skeleton fallback.
Mint, currency, savings, and send form refactoring
app/mint/page.tsx, app/currency/page.tsx, app/savings/deposit/page.tsx, app/savings/withdraw/page.tsx, app/send/page.tsx, app/savings/page.tsx
Mint introduces local mintError/burnError state with debounced inputs. Currency page refactors balance hook and restricts mint source. Savings forms resolve recipient URIs and remove intermediate "resolving" UI state. Send page adds virtualized contact dropdown, session pre-flight checks, and inline error/success handling. Savings page adds SAVINGS_ACCOUNT_TYPES constant.

UI and Error Handling

Layer / File(s) Summary
Error boundary and error page
components/error-boundary.tsx, app/error.tsx
Error boundary removes errorInfo from state and level-based logic. Error page adds centered layout and single "Try again" button. Both files include unresolved merge-conflict markers.
Transfer details and reserves metrics
app/send/[id]/page.tsx, app/reserves/page.tsx
Transfer detail page adds safeFormatDate helper, derives local-currency fields, and wraps status badges with screen-reader descriptions. Reserves page updates metric tooltips and uses MetricLabel helpers for per-currency grid.
Global styling and theme
app/globals.css
Tailwind import switched to source(none) with explicit @source directives. New CSS custom properties for --status-success, --status-warning, --status-neutral (plus variants) added to :root and .dark. Exposed as --color-status-* theme colors.

Infrastructure and Security

Layer / File(s) Summary
Wallet storage encryption
lib/wallet-storage.ts
Replaces mock base64 encoding with real AES-GCM encryption: secrets encrypted with key derived from passcode via PBKDF2 (SHA-256). Encrypted payload stored as JSON with version, salt, IV, ciphertext. Includes unresolved merge-conflict markers in best-effort lookup helper.
API client and Stellar error handling
lib/api/client.ts, lib/stellar/burning.ts
API client derives bearer token from opts.token only, removes fallback logic. Stellar error handling converts errorResult to base64 via toXDR() before formatting.
Testing and configuration
package.json, playwright.config.ts, tests/accessibility.spec.ts, app/mint/mint.test.tsx, app/send/send.test.tsx
Package renamed, scripts updated with typecheck/vitest tests, next-intl and stellar-sdk added. Playwright config switches to ./tests directory, chromium-only, with failure screenshots. Accessibility test suite added using @axe-core/playwright. Test mocks remove apiKey and token fields.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Pi-Defi-world/acbu-frontend#242: Main PR's next-intl i18n integration directly overlaps with this PR's core i18n wiring across app/[locale]/layout.tsx, message catalogs, and useTranslations usage.
  • Pi-Defi-world/acbu-frontend#245: Both PRs heavily refactor app/burn/page.tsx with overlapping schema validation, form error handling, and UI updates.
  • Pi-Defi-world/acbu-frontend#294: Both PRs modify contexts/auth-context.tsx and 2FA flow around apiKey handling, so authentication changes conflict/overlap.

🐰 A PR hops through the locales with care,
Auth keys dance away—no tokens to share,
Forms now speak many tongues with grace,
While merge conflicts mark'd scatter the place! 🌍✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

⚠️ This pull request might be slop. It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@drips-wave

drips-wave Bot commented May 28, 2026

Copy link
Copy Markdown

@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! 🚀

Learn more about application limits

@Junman140

Copy link
Copy Markdown
Member

@Jujukoder RESOLVE CONFLICTS

@Jujukoder

Copy link
Copy Markdown
Contributor Author

Okayy

@Jujukoder

Copy link
Copy Markdown
Contributor Author

Done, thank you! Kindly merge the PR

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Associate 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 win

Fix app/help/page.tsx App Router metadata boundary, resolve merge conflicts, and correct label accessibility

  • [Critical] app/help/page.tsx is marked "use client" but exports export const metadata; route metadata is Server Component–only in the App Router—move metadata to the server page.tsx/layout.tsx and keep this file as a Client Component only for interactive UI.
  • [Critical] Unresolved merge-conflict markers remain in the FAQ accordion (<<<<<<< HEAD / ======= / >>>>>>> origin/dev around lines ~186-190); resolve/remove them.
  • [Major] Contact form <label> elements don’t have htmlFor and the associated Input/Textarea controls don’t define matching id (no aria-labelledby/aria-label either); add proper id + htmlFor (or an aria association) 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 win

Variable shadowing: t used for both i18n function and loop variable.

The transfersList memo uses t as the loop variable (transfers.map((t: TransferItem) => ...) which shadows the t translation function from useI18n. This causes the translation calls like t('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 lift

Critical: 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 win

Critical: Multiple unresolved merge conflicts throughout the file.

This file has merge conflicts in three locations:

  1. Lines 1-9: Import section (next-intl plugin vs env-safety validation)
  2. Lines 13-37: Config object (images.unoptimized vs typescript.ignoreBuildErrors and experimental settings)
  3. Lines 51-55: Default export (withNextIntl vs withBundleAnalyzer)

Important consideration: The origin/dev side sets ignoreBuildErrors: true, while upstream/dev has it as false with comment "F-001: TypeScript errors must fail the build to prevent shipping broken code". The false setting is the safer choice for production quality.

When resolving, you likely need to combine:

  • next-intl plugin wrapper
  • env validation
  • ignoreBuildErrors: false for 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 win

Move metadata out of this "use client" page (app/[locale]/auth/2fa/page.tsx).
Next.js App Router disallows export const metadata / generateMetadata from files marked with "use client"; keep the route metadata in a server page.tsx or layout.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

searchParams is undefined.

The form's defaultValues references searchParams?.get("amount") and searchParams?.get("currency"), but searchParams is never declared or imported. This appears to be a remnant from a removed useSearchParams() hook import.

This will cause a ReferenceError at 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 win

Multiple 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 win

Unresolved 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 win

Duplicate 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 win

Fix unsupported metadata export in client component (app/test-locale/page.tsx).

app/test-locale/page.tsx is marked with "use client" but exports export const metadata, which Next.js App Router does not support from Client Components (it will error or be ignored). Since app/test-locale/layout.tsx already exports metadata for this route, remove the metadata export from page.tsx and keep the page as a client component for the useState UI.

🤖 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 lift

Critical: Unresolved merge conflict between two session hydration strategies.

This conflict is between:

  • HEAD branch (lines 73-76): Simple useEffect that calls getStoredAuth() on mount
  • upstream/dev branch (lines 78-141): Complex validateSession that makes an API call to getMe() to verify the httpOnly cookie is still valid

Given 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

validateSession is referenced but may not exist after conflict resolution.

Line 221 includes validateSession in the useMemo dependencies, and line 223 exposes it as refetchSession. However, validateSession is 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

metadata export 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 separate layout.tsx or 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 win

Critical: 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
  • BalanceSkeleton vs simple ... loading states

The 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.level is referenced but level is not defined in Props.

The componentDidCatch method references this.props.level (line 30), but the Props interface (lines 8-11) no longer includes level. This will always be undefined, causing the error reporter to receive 'component' as the fallback.

If the level prop 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 win

Critical: Unresolved merge conflict in RootLayout function.

The RootLayout function has unresolved merge conflict markers that break the entire application. One branch returns only children (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/dev branch 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 lift

Fix unresolved merge conflicts in middleware.ts (compose next-intl + CSP logic into a single middleware)

  • middleware.ts still contains <<<<<<</>>>>>>> conflict markers, leaving two different middleware implementations (next-intl createMiddleware(...) vs a custom middleware(request: NextRequest) with .md 404 + CSP nonce directives) and two conflicting export const config.matcher definitions.
  • Resolve the conflicts by keeping only one exported middleware + one export 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 win

Resolve 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 account vs. 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 win

Show an error when the receive address cannot be loaded.

Right now this path only logs. If getReceive() fails, user stays 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 win

Resolve 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 win

Fail 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 win

Re-enable color-contrast in 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 accessible and send form interactions should be accessible tests, 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 win

Guard the dateTime attribute against malformed timestamps.

safeFormatDate avoids bad display text, but new Date(createdAt).toISOString() and new 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 win

Accessibility issue: maximumScale: 1 prevents users from zooming.

Setting maximumScale: 1 restricts users from zooming in, which violates WCAG 1.4.4 (Resize Text). This is particularly important for users with low vision. Notably, app/layout.tsx explicitly 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 win

Redact the ticket body before logging misconfiguration failures.

This logs the full support payload, including name, email, and message, into server logs. That creates an avoidable PII retention path for a config error. Log only ticketId/page or 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 win

Handle upstream network failures and timeouts explicitly.

If the intake host times out, has a DNS issue, or resets the connection, fetch will 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 win

Surface receive-address lookup failures to the form.

This catch only logs. When getReceive() 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 win

Remove unnecessary unsafe cast on sendRes.errorResult (lib/stellar/burning.ts:84-88)

rpcServer.sendTransaction() returns errorResult as an xdr.TransactionResult (SDK-parsed), so it already provides toXDR("base64"). The current (sendRes.errorResult as unknown as { toXDR: ... }) cast is unnecessary; call sendRes.errorResult.toXDR("base64") directly for proper type safety. The String(status.resultXdr) usage in the FAILED path is aligned with getTransaction’s base64 resultXdr.

🤖 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 win

Add the new home.approx_usd key here as well.

i18n/messages/en.json already defines home.approx_usd, but en-NG does 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 win

Test 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 .gitignore and 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5041fcb and 94cadc2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (73)
  • .gitignore
  • app/[locale]/activity/layout.tsx
  • app/[locale]/activity/page.tsx
  • app/[locale]/auth/2fa/page.tsx
  • app/[locale]/auth/error.tsx
  • app/[locale]/auth/layout.tsx
  • app/[locale]/auth/signin/page.tsx
  • app/[locale]/auth/signup/page.tsx
  • app/[locale]/auth/wallet-setup/page.tsx
  • app/[locale]/layout.tsx
  • app/[locale]/page.tsx
  • app/api/support/route.ts
  • app/bills/layout.tsx
  • app/burn/layout.tsx
  • app/burn/page.tsx
  • app/business/layout.tsx
  • app/business/page.tsx
  • app/currency/layout.tsx
  • app/currency/page.tsx
  • app/error.tsx
  • app/fiat/layout.tsx
  • app/fiat/page.tsx
  • app/globals.css
  • app/help/layout.tsx
  • app/help/page.tsx
  • app/layout.tsx
  • app/lending/layout.tsx
  • app/me/layout.tsx
  • app/me/page.tsx
  • app/me/settings/layout.tsx
  • app/mint/layout.tsx
  • app/mint/mint.test.tsx
  • app/mint/page.tsx
  • app/rates/layout.tsx
  • app/rates/page.tsx
  • app/recovery/layout.tsx
  • app/reserves/layout.tsx
  • app/reserves/page.tsx
  • app/savings/deposit/page.tsx
  • app/savings/layout.tsx
  • app/savings/page.tsx
  • app/savings/withdraw/page.tsx
  • app/send/[id]/page.tsx
  • app/send/layout.tsx
  • app/send/page.tsx
  • app/send/send.test.tsx
  • app/test-locale/layout.tsx
  • app/test-locale/page.tsx
  • app/transactions/layout.tsx
  • app/wallet/layout.tsx
  • app/wallet/page.tsx
  • components/error-boundary.tsx
  • contexts/auth-context.tsx
  • global.d.ts
  • hooks/__tests__/use-api-error.test.ts
  • hooks/use-api.ts
  • hooks/use-debounce.ts
  • i18n/messages/en-KE.json
  • i18n/messages/en-NG.json
  • i18n/messages/en.json
  • i18n/request.ts
  • lib/__tests__/api-error.test.ts
  • lib/__tests__/clipboard.test.ts
  • lib/api/client.ts
  • lib/stellar/burning.ts
  • lib/wallet-storage.ts
  • middleware.ts
  • next.config.mjs
  • package.json
  • playwright-report/index.html
  • playwright.config.ts
  • test-results/.last-run.json
  • tests/accessibility.spec.ts
💤 Files with no reviewable changes (1)
  • app/send/send.test.tsx

Comment thread .gitignore
Comment on lines +29 to +40
<<<<<<< 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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/dev side ignores package-lock.json and pnpm-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.

Comment thread app/[locale]/page.tsx
Comment on lines +27 to +32
<<<<<<< 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

Comment thread app/[locale]/page.tsx
Comment on lines +137 to +149
<<<<<<< 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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: useScrollRestoration hook 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.

Comment thread app/burn/page.tsx
Comment on lines +207 to +209
account_number: data.accountNumber.trim(),
bank_code: data.bankCode.trim(),
account_name: data.accountName.trim(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

Suggested change
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.

Comment thread app/business/page.tsx
Comment on lines +24 to +36
<<<<<<< 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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/description strings
  • 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 &rbrace;?

(parse)


[error] 26-26: expected } but instead found :

(parse)


[error] 26-26: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 27-27: expected } but instead found :

(parse)


[error] 27-27: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 28-28: expected } but instead found :

(parse)


[error] 28-28: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 29-29: expected } but instead found :

(parse)


[error] 29-29: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 31-31: expected } but instead found :

(parse)


[error] 31-31: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 32-32: expected } but instead found :

(parse)


[error] 32-32: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 33-33: expected } but instead found :

(parse)


[error] 33-33: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 34-34: expected } but instead found :

(parse)


[error] 34-34: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 35-35: expected } but instead found :

(parse)


[error] 35-35: Unexpected token. Did you mean {'}'} or &rbrace;?

(parse)


[error] 36-36: Unexpected token. Did you mean {'>'} or &gt;?

(parse)


[error] 36-36: Unexpected token. Did you mean {'>'} or &gt;?

(parse)


[error] 36-36: Unexpected token. Did you mean {'>'} or &gt;?

(parse)


[error] 36-36: Unexpected token. Did you mean {'>'} or &gt;?

(parse)


[error] 36-36: Unexpected token. Did you mean {'>'} or &gt;?

(parse)


[error] 36-36: Unexpected token. Did you mean {'>'} or &gt;?

(parse)


[error] 36-36: Unexpected token. Did you mean {'>'} or &gt;?

(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.

Comment thread contexts/auth-context.tsx
Comment on lines +35 to +38
<<<<<<< HEAD

=======
>>>>>>> origin/dev

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

Comment thread contexts/auth-context.tsx
Comment on lines +49 to +51
if (apiKey) {
setToken(apiKey);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

Comment thread hooks/use-api.ts
Comment on lines +3 to +7
<<<<<<< HEAD
export { useApiError } from '@/hooks/use-api-error';
=======
import { getApiErrorMessage } from '@/lib/api/client';
>>>>>>> origin/dev

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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).

Comment thread middleware.ts
Comment thread package.json
Comment on lines +10 to +24
<<<<<<< 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

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.

@Junman140 Junman140 merged commit 9ce63b5 into Pi-Defi-world:dev Jun 4, 2026
1 of 2 checks passed
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.

Help page form submits to nowhere – L