Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
default: (props: any) => {
// next/image uses 'fill' boolean; strip non-DOM props
const { priority, layout, objectFit, fill, ...rest } = props
return <img {...rest} />

Check warning on line 51 in src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx

View workflow job for this annotation

GitHub Actions / eslint

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element

Check warning on line 51 in src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx

View workflow job for this annotation

GitHub Actions / eslint

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
},
}))

Expand Down Expand Up @@ -866,8 +866,8 @@
})

test('Provider maintenance shows maintenance banner', async () => {
const maintenanceConfig = require('@/config/underMaintenance.config').default
maintenanceConfig.disabledPaymentProviders = ['MANTECA']
const underMaintenanceConfig = require('@/config/underMaintenance.config').default
underMaintenanceConfig.disabledPaymentProviders = ['MANTECA']
Comment on lines +869 to +870

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.

📐 Maintainability & Code Quality | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify test structure around the config mutation lines
sed -n '860,890p' src/app/\(mobile-ui\)/qr-pay/__tests__/qr-pay-states.test.tsx | cat -n

Repository: peanutprotocol/peanut-ui

Length of output: 1497


🏁 Script executed:

# Find the describe block containing this test to see if other tests need the same config
sed -n '850,920p' 'src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx' | cat -n

Repository: peanutprotocol/peanut-ui

Length of output: 3140


Convert require() to ES6 import to comply with ESLint rules.

The dynamic require() call on line 869 violates the project's @typescript-eslint/no-require-imports rule. Since the setup and cleanup are already properly scoped within the test function, add an ES6 import at the top of the file and keep the mutation and cleanup within the test:

🔧 Suggested refactor

At the top of the test file, add:

import underMaintenanceConfig from '`@/config/underMaintenance.config`'

Then replace line 869 with direct usage:

- const underMaintenanceConfig = require('`@/config/underMaintenance.config`').default
  underMaintenanceConfig.disabledPaymentProviders = ['MANTECA']

Keep the cleanup at the end of the test as-is (line 881).

🧰 Tools
🪛 ESLint

[error] 869-869: A require() style import is forbidden.

(@typescript-eslint/no-require-imports)

🤖 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 `@src/app/`(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx around lines 869
- 870, Convert the dynamic require() statement for the underMaintenance.config
to an ES6 import statement at the top of the qr-pay-states.test.tsx file to
comply with the `@typescript-eslint/no-require-imports` ESLint rule. Add the
import statement at the file level, then replace the require() call on line 869
with direct usage of the imported underMaintenanceConfig object, keeping the
mutation of the disabledPaymentProviders property and its cleanup within the
test function scope as it is currently structured.

Source: Linters/SAST tools


setupMantecaPayment()

Expand All @@ -878,7 +878,7 @@
})

// Clean up
maintenanceConfig.disabledPaymentProviders = []
underMaintenanceConfig.disabledPaymentProviders = []
})
})

Expand Down
4 changes: 2 additions & 2 deletions src/app/(mobile-ui)/qr-pay/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
import { usePointsConfetti } from '@/hooks/usePointsConfetti'
import { usePointsCalculation } from '@/hooks/usePointsCalculation'
import { useModalsContext } from '@/context/ModalsContext'
import maintenanceConfig from '@/config/underMaintenance.config'
import underMaintenanceConfig from '@/config/underMaintenance.config'
import PointsCard from '@/components/Common/PointsCard'
import { TRANSACTIONS } from '@/constants/query.consts'
import { useLimitsValidation } from '@/features/limits/hooks/useLimitsValidation'
Expand Down Expand Up @@ -141,7 +141,7 @@

// Check if this payment provider is under maintenance
const isProviderDisabled = useMemo(() => {
return paymentProcessor ? maintenanceConfig.disabledPaymentProviders.includes(paymentProcessor) : false
return paymentProcessor ? underMaintenanceConfig.disabledPaymentProviders.includes(paymentProcessor) : false
}, [paymentProcessor])

// MIGRATION-REVIEW: QR-pay KYC gate, formerly useQrKycGate + useKycStatus.
Expand Down Expand Up @@ -241,7 +241,7 @@
if (sumsubFlow.showWrapper || sumsubFlow.isModalOpen) {
sumsubFlow.completeFlow()
}
}, [kycGateState, sumsubFlow.showWrapper, sumsubFlow.isModalOpen, sumsubFlow.completeFlow])

Check warning on line 244 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'sumsubFlow'. Either include it or remove the dependency array

Check warning on line 244 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'sumsubFlow'. Either include it or remove the dependency array

const queryClient = useQueryClient()
const [isShaking, setIsShaking] = useState(false)
Expand Down Expand Up @@ -354,7 +354,7 @@
if (isSuccess || !!errorMessage) {
setLoadingState('Idle')
}
}, [isSuccess, errorMessage])

Check warning on line 357 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'setLoadingState'. Either include it or remove the dependency array

Check warning on line 357 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'setLoadingState'. Either include it or remove the dependency array

// First fetch for qrcode info — only after KYC gating allows proceeding
useEffect(() => {
Expand All @@ -366,7 +366,7 @@
}

setIsFirstLoad(false)
}, [timestamp, paymentProcessor, qrCode])

Check warning on line 369 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'resetState'. Either include it or remove the dependency array

Check warning on line 369 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'resetState'. Either include it or remove the dependency array

// Get amount from payment lock (Manteca)
useEffect(() => {
Expand All @@ -381,7 +381,7 @@
setAmount(paymentLock.paymentAgainstAmount)
setCurrencyAmount(paymentLock.paymentAssetAmount)
}
}, [paymentLock?.code, paymentProcessor])

Check warning on line 384 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'paymentLock'. Either include it or remove the dependency array

Check warning on line 384 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'paymentLock'. Either include it or remove the dependency array

// Get currency object from payment lock (Manteca)
useEffect(() => {
Expand All @@ -403,7 +403,7 @@
}
}
getCurrencyObject().then(setCurrency)
}, [paymentLock?.code, paymentProcessor])

Check warning on line 406 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'paymentLock'. Either include it or remove the dependency array

Check warning on line 406 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useEffect has a missing dependency: 'paymentLock'. Either include it or remove the dependency array

const isBlockingError = useMemo(() => {
return !!errorMessage && errorMessage !== 'Please confirm the transaction.'
Expand All @@ -419,7 +419,7 @@
// For dynamic QR codes, backend provides the USD amount
return paymentLock.paymentAgainstAmount
}
}, [paymentLock?.code, paymentLock?.paymentAgainstAmount, amount])

Check warning on line 422 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useMemo has a missing dependency: 'paymentLock'. Either include it or remove the dependency array

Check warning on line 422 in src/app/(mobile-ui)/qr-pay/page.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useMemo has a missing dependency: 'paymentLock'. Either include it or remove the dependency array

// Live card-vs-local-rail markup, driven by Manteca's rate + (for ARS)
// BCRA's official rate. Used by both the confirm-screen "Save vs card"
Expand Down
13 changes: 7 additions & 6 deletions src/components/AddMoney/components/MantecaAddMoney.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { useLimitsValidation } from '@/features/limits/hooks/useLimitsValidation
import posthog from 'posthog-js'
import { ANALYTICS_EVENTS } from '@/constants/analytics.consts'
import InfoCard from '@/components/Global/InfoCard'
import underMaintenanceConfig, { PIX_BRAZIL_ONRAMP_MAINTENANCE } from '@/config/underMaintenance.config'
import underMaintenanceConfig, { PIX_ONRAMP_MAINTENANCE_COPY } from '@/config/underMaintenance.config'

// Step type for URL state
type MantecaStep = 'inputAmount' | 'depositDetails'
Expand Down Expand Up @@ -72,9 +72,10 @@ const MantecaAddMoney: FC = () => {
return countryData.find((country) => country.type === 'country' && country.path === selectedCountryPath)
}, [selectedCountryPath])
const onBack = useSafeBack(addMoneyCountryUrl(selectedCountryPath))
// BRL-via-PIX onramp warn-only maintenance flag (see underMaintenance.config.ts).
// BRL-via-PIX onramp warn-only maintenance banner (see underMaintenance.config.ts).
// Brazil-scoped so the Argentina/ARS Manteca onramp is unaffected.
const showPixMaintenance = selectedCountry?.id === 'BR' && underMaintenanceConfig.pixBrazilOnrampMaintenance
const showPixMaintenanceBanner =
selectedCountry?.id === 'BR' && underMaintenanceConfig.enablePixOnrampMaintenanceWarning
// The pool→full upgrade gate asks "did the user clear ID verification?",
// not "do they have an enabled rail elsewhere?" — read the identity
// signal directly (Sumsub-cleared the human) instead of the old
Expand Down Expand Up @@ -286,12 +287,12 @@ const MantecaAddMoney: FC = () => {
limitsCurrency={limitsValidation.currency}
onBack={onBack}
maintenanceBanner={
showPixMaintenance ? (
showPixMaintenanceBanner ? (
<InfoCard
variant="warning"
icon="alert"
title={PIX_BRAZIL_ONRAMP_MAINTENANCE.title}
description={PIX_BRAZIL_ONRAMP_MAINTENANCE.description}
title={PIX_ONRAMP_MAINTENANCE_COPY.title}
description={PIX_ONRAMP_MAINTENANCE_COPY.description}
/>
) : undefined
}
Expand Down
13 changes: 7 additions & 6 deletions src/components/AddWithdraw/AddWithdrawCountriesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { getRegionIntent } from '@/utils/regions.utils'
import { useTosGuard } from '@/hooks/useTosGuard'
import { BridgeTosStep } from '@/components/Kyc/BridgeTosStep'
import { useModalsContext } from '@/context/ModalsContext'
import underMaintenanceConfig, { PIX_BRAZIL_ONRAMP_MAINTENANCE } from '@/config/underMaintenance.config'
import underMaintenanceConfig, { PIX_ONRAMP_MAINTENANCE_COPY } from '@/config/underMaintenance.config'

interface AddWithdrawCountriesListProps {
flow: 'add' | 'withdraw'
Expand Down Expand Up @@ -429,11 +429,12 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => {
<div className="flex flex-col">
{paymentMethods.map((method, index) => {
// BRL-via-PIX onramp is warn-only under maintenance: tag the Pix option but
// keep it clickable (do not set isDisabled).
const isPixOnrampUnderMaintenance =
// keep it clickable (do not set isDisabled). The `pix-add` method is itself
// Brazil-only (consts filter it to countryCode === 'BR').
const showPixMaintenanceTag =
flow === 'add' &&
method.id === 'pix-add' &&
underMaintenanceConfig.pixBrazilOnrampMaintenance
underMaintenanceConfig.enablePixOnrampMaintenanceWarning
return (
<ActionListCard
key={method.id}
Expand Down Expand Up @@ -470,10 +471,10 @@ const AddWithdrawCountriesList = ({ flow }: AddWithdrawCountriesListProps) => {
rightContent={
method.isSoon ? (
<StatusBadge status="soon" size="small" />
) : isPixOnrampUnderMaintenance ? (
) : showPixMaintenanceTag ? (
<StatusBadge
status="pending"
customText={PIX_BRAZIL_ONRAMP_MAINTENANCE.badge}
customText={PIX_ONRAMP_MAINTENANCE_COPY.badge}
size="small"
/>
) : null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,21 +239,26 @@ describe('AddWithdrawCountriesList — bank gate', () => {

/**
* BRL-via-PIX onramp is unstable, so the Pix option is flagged "under maintenance"
* (config: pixBrazilOnrampMaintenance) — warn-only: it stays visible and clickable.
* (config: enablePixOnrampMaintenanceWarning) — warn-only: it stays visible and clickable.
*/
describe('AddWithdrawCountriesList — PIX onramp maintenance tag', () => {
// snapshot/restore the shipped flag so each test can flip it without leaking state —
// and without coupling the restore to whatever the committed default happens to be
let originalPixMaintenance: boolean

beforeEach(() => {
mockPush.mockClear()
// a ready gate so a click can navigate — proving the option is not blocked
setCapabilities('ready', [{ status: 'enabled', channel: 'bank', country: 'US' }])
originalPixMaintenance = underMaintenanceConfig.enablePixOnrampMaintenanceWarning
})

afterEach(() => {
underMaintenanceConfig.pixBrazilOnrampMaintenance = true
underMaintenanceConfig.enablePixOnrampMaintenanceWarning = originalPixMaintenance
})

it('tags the Pix option "Maintenance" but keeps it clickable (warn-only)', () => {
underMaintenanceConfig.pixBrazilOnrampMaintenance = true
underMaintenanceConfig.enablePixOnrampMaintenanceWarning = true

render(<AddWithdrawCountriesList flow="add" />)

Expand All @@ -266,7 +271,7 @@ describe('AddWithdrawCountriesList — PIX onramp maintenance tag', () => {
})

it('shows no maintenance tag when the flag is off, and never tags non-Pix methods', () => {
underMaintenanceConfig.pixBrazilOnrampMaintenance = false
underMaintenanceConfig.enablePixOnrampMaintenanceWarning = false

render(<AddWithdrawCountriesList flow="add" />)

Expand Down
4 changes: 2 additions & 2 deletions src/components/Global/Banner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useEffect } from 'react'
import { usePathname } from 'next/navigation'
import { MaintenanceBanner } from './MaintenanceBanner'
import { MarqueeWrapper } from '../MarqueeWrapper'
import maintenanceConfig from '@/config/underMaintenance.config'
import underMaintenanceConfig from '@/config/underMaintenance.config'
import { HandThumbsUp } from '@/assets'
import Image from 'next/image'
import { useModalsContext } from '@/context/ModalsContext'
Expand All @@ -16,7 +16,7 @@ export function Banner() {
if (!pathname) return null

// check if maintenance banner OR full maintenance is enabled - show on all pages
if (maintenanceConfig.enableMaintenanceBanner || maintenanceConfig.enableFullMaintenance) {
if (underMaintenanceConfig.enableMaintenanceBanner || underMaintenanceConfig.enableFullMaintenance) {
return <MaintenanceBanner />
}

Expand Down
18 changes: 12 additions & 6 deletions src/config/underMaintenance.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
*
* to enable maintenance mode, simply toggle one or both of these keys:
*
* naming convention — read the verb, not just the value:
* - enable<Behavior>: true = turn that maintenance behavior ON (block / redirect / warn)
* - disable<Feature>: true = turn that product feature OFF
* `enableX: true` makes X happen; `disableY: true` makes Y stop. New flags MUST follow
* one of these two shapes so the polarity is always obvious from the name.
*
* 1. enableFullMaintenance: redirects ALL pages to /maintenance page
* - landing page (/) and support page (/support) remain accessible
* - maintenance banner shows on all pages (including landing and support)
Expand Down Expand Up @@ -34,11 +40,11 @@
* - card pioneer modal, carousel cta, and perk rewards hidden from home
* - set to false to enable the feature
*
* 7. pixBrazilOnrampMaintenance: warn-only flag for the BRL-via-PIX onramp (Manteca Brazil deposit)
* 7. enablePixOnrampMaintenanceWarning: warn-only flag for the PIX onramp (Manteca Brazil deposit)
* - shows a "Maintenance" tag on the Pix option in /add-money/brazil
* - shows a warning banner inside the deposit flow (/add-money/brazil/manteca)
* - does NOT block deposits — the option stays usable (warn-only)
* - set to false when PIX deposits are stable again
* - set to true when PIX deposits degrade again
*
* note: if either mode is enabled, the maintenance banner will show everywhere
*
Expand All @@ -55,7 +61,7 @@ interface MaintenanceConfig {
disableXchainWithdraw: boolean
disableXchainSend: boolean
disableCardPioneers: boolean
pixBrazilOnrampMaintenance: boolean
enablePixOnrampMaintenanceWarning: boolean
}

const underMaintenanceConfig: MaintenanceConfig = {
Expand All @@ -65,16 +71,16 @@ const underMaintenanceConfig: MaintenanceConfig = {
disableXchainWithdraw: true, // set to true to disable cross-chain withdrawals (only allows USDC on Arbitrum)
disableXchainSend: true, // set to true to disable cross-chain sends (claim, request payments - only allows USDC on Arbitrum)
disableCardPioneers: true, // set to false to enable the Card Pioneers waitlist feature
pixBrazilOnrampMaintenance: true, // set to false when BRL-via-PIX deposits are stable again
enablePixOnrampMaintenanceWarning: false, // set to true when PIX (Manteca Brazil) deposits degrade again
}

// shared user-facing copy for cross-chain disabled paths — keep wording aligned with TokenSelector banner
export const CROSS_CHAIN_DISABLED_MESSAGE =
'Cross-chain claims are temporarily unavailable. Try claiming to an external wallet on the same chain as the link, or try again later.'

// shared user-facing copy for the BRL-via-PIX onramp maintenance warning — keep the list tag and
// shared user-facing copy for the PIX onramp maintenance warning — keep the list tag and
// the in-flow banner aligned
export const PIX_BRAZIL_ONRAMP_MAINTENANCE = {
export const PIX_ONRAMP_MAINTENANCE_COPY = {
badge: 'Maintenance',
title: 'PIX deposits are under maintenance',
description:
Expand Down
4 changes: 2 additions & 2 deletions src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// https://nextjs.org/docs/messages/middleware-to-proxy
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import maintenanceConfig from '@/config/underMaintenance.config'
import underMaintenanceConfig from '@/config/underMaintenance.config'

export function proxy(request: NextRequest) {
const { pathname } = request.nextUrl
Expand All @@ -15,7 +15,7 @@ export function proxy(request: NextRequest) {
// }

// check if full maintenance mode is enabled
if (maintenanceConfig.enableFullMaintenance) {
if (underMaintenanceConfig.enableFullMaintenance) {
const allowedPaths = ['/', '/maintenance', '/apple-app-site-association', '/support']
if (
!allowedPaths.includes(pathname) &&
Expand Down
Loading