Skip to content

[READY] Fix 🇧🇷 Avenia KYB Flow (and improve visual feedback)#1116

Open
Sharqiewicz wants to merge 8 commits intostagingfrom
fix/improve-avenia-kyb-flow
Open

[READY] Fix 🇧🇷 Avenia KYB Flow (and improve visual feedback)#1116
Sharqiewicz wants to merge 8 commits intostagingfrom
fix/improve-avenia-kyb-flow

Conversation

@Sharqiewicz
Copy link
Copy Markdown
Member

Summary

  • KYB form routing fixwidget/index.tsx now checks XState's nested state ("KYBFlow" in stateValue) instead of the presence of kybUrls, preventing the KYB UI from rendering prematurely
  • Dedicated useKYBForm hook — extracted from useKYCForm, scoped to the two KYB-only fields (taxId, fullName); avoids initialising the full 12-field KYC form for company users
  • AveniaVerificationForm decoupled — replaced aveniaKycActor prop with a generic onSubmit: SubmitHandler<T> prop, making the form component reusable across KYC and KYB flows
  • AveniaKYCForm scoping fixuseKYCForm (and its two store-sync useEffects) now only mounts in the AveniaKYCFormStep sub-component, so subscriptions are idle during VerificationStatus, DocumentUpload, and AveniaLivenessStep states
  • Back button on KYB flowStepBackButton added to AveniaKYBFlow, using the existing useStepBackNavigation handler which already covers the KYBFlow XState state
  • brlaApiServiceinitiateKybLevel1 now sends { redirectUrl: "" } in the POST body; Avenia requires the field present even though it ignores the value for the Web SDK flow
  • SparkleButton component — reusable animated button with randomised particle burst on click and on a periodic interval; supports primary and success themes via CSS custom properties (var(--color-primary), var(--color-success))
  • Success-state SVGs — green variants of business-check-business.svg and business-check-representative.svg generated with the exact oklch(0.448 0.119 151.328) success color; shown in AveniaKYBVerifyStep when isVerificationStarted is true, alongside a success-coloured title
  • brlaKyc.machine.tsFORM_SUBMIT event now accepts KYCFormData | KYBFormData, removing the as unknown as cast in AveniaKYBForm

Test plan

  • CPF user: KYC form renders, submits, and transitions through Verifying → DocumentUpload → LivenessCheck states correctly
  • CNPJ user: AveniaKYBForm (company name + CNPJ) renders before KYB URLs are fetched; AveniaKYBFlow renders once in KYBFlow XState state
  • Back button visible on all KYB flow screens; pressing it returns to the KYB form
  • Verify company step: clicking "Verify" opens Avenia URL; isVerificationStarted flips to true; image and title turn green; SparkleButton replaces the primary button
  • SparkleButton: particles burst on click and automatically every second; particles appear behind the button
  • No TypeScript errors: bun typecheck

@Sharqiewicz Sharqiewicz requested a review from gianfra-t April 8, 2026 19:14
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 8, 2026

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit d568315
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/69d7770eceb55d00084fe258
😎 Deploy Preview https://deploy-preview-1116--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 8, 2026

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit d568315
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/69d7770e377e1400085f6e5c
😎 Deploy Preview https://deploy-preview-1116--vortexfi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@ebma ebma requested review from Copilot and ebma April 9, 2026 09:30
@ebma
Copy link
Copy Markdown
Member

ebma commented Apr 9, 2026

@Sharqiewicz there are some merge conflicts, let's try to resolve them.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes the Avenia KYB flow rendering/routing in the widget, reduces unnecessary form initialization/subscriptions by splitting KYC vs KYB hooks/components, and adds improved KYB visual feedback (success styling + new animated SparkleButton). It also migrates several frontend form validators from Yup to Zod v4 via @hookform/resolvers/standard-schema, and updates the BRLA KYB initiation request to include the required redirectUrl field.

Changes:

  • Fix KYB flow gating by checking nested XState compound state (KYBFlow) rather than presence of fetched URLs; add KYB back navigation.
  • Refactor Avenia form handling: new useKYBForm, generic AveniaVerificationForm submission, and KYC form scoping to reduce idle subscriptions.
  • Migrate validation (Yup → Zod v4 + standard-schema resolver) and add new UI feedback components/assets (SparkleButton + success SVG variants).

Reviewed changes

Copilot reviewed 24 out of 27 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/shared/src/services/brla/mappings.ts Updates endpoint typing so KYB Web SDK POST expects { redirectUrl } body.
packages/shared/src/services/brla/brlaApiService.ts Sends { redirectUrl: "" } payload for KYB initiation as required by Avenia.
bun.lock Locks dependency updates (notably Zod v4, resolvers v4) and catalog move for cobe.
apps/frontend/package.json Bumps @hookform/resolvers, migrates to zod@^4, removes yup.
apps/frontend/src/pages/widget/index.tsx Fixes KYB flow rendering by checking compound XState state.
apps/frontend/src/machines/types.ts Adds isInCompoundState helper for XState v5 nested state detection.
apps/frontend/src/hooks/useStepBackNavigation.ts Uses isInCompoundState to detect KYB flow for back navigation.
apps/frontend/src/machines/brlaKyc.machine.ts Broadens FORM_SUBMIT typing to accept KYC or KYB form payloads.
apps/frontend/src/hooks/brla/useKYCForm/index.tsx Migrates KYC validation to Zod + standard-schema; consolidates store sync watch.
apps/frontend/src/hooks/brla/useKYBForm/index.tsx Adds a dedicated KYB-only hook for company flow.
apps/frontend/src/components/Avenia/AveniaVerificationForm/index.tsx Makes verification form reusable via generic onSubmit prop.
apps/frontend/src/components/Avenia/AveniaKYCForm.tsx Scopes KYC form mounting to the form step; swaps layout components.
apps/frontend/src/components/Avenia/AveniaKYBForm.tsx Switches to useKYBForm and uses generic submit handler.
apps/frontend/src/components/Avenia/AveniaKYBFlow/index.tsx Adds menu buttons and consolidates KYB step rendering.
apps/frontend/src/components/Avenia/AveniaKYBFlow/AveniaKYBVerifyStep.tsx Adds success-state visuals and swaps in SparkleButton after verification starts.
apps/frontend/src/components/Avenia/AveniaKYBFlow/AveniaKYBVerifyCompany*.tsx Supplies verified-state SVGs to the KYB verification step.
apps/frontend/src/components/SparkleButton/index.tsx Introduces animated button with periodic/click sparkle bursts.
apps/frontend/src/assets/*-success.svg Adds success-colored variants for KYB verification images.
apps/frontend/src/hooks/useContactForm.ts Migrates contact form validation to Zod + standard-schema.
apps/frontend/src/hooks/ramp/useRampForm.ts Switches resolver to standard-schema.
apps/frontend/src/hooks/ramp/schema.ts Migrates ramp schema to Zod with custom refinements.
apps/frontend/src/hooks/quote/useQuoteForm.ts Switches resolver to standard-schema.
apps/frontend/src/hooks/quote/schema.ts Migrates quote schema to Zod with custom refinements.
apps/frontend/src/pages/alfredpay/FiatAccountRegistration/RegisterFiatAccountScreen.tsx Switches resolver to standard-schema and adjusts Zod types.
apps/frontend/src/components/widget-steps/AuthEmailStep/index.tsx Replaces Yup email validation with Zod v4 z.email() parsing.
Comments suppressed due to low confidence (1)

apps/frontend/src/pages/alfredpay/FiatAccountRegistration/RegisterFiatAccountScreen.tsx:48

  • In buildZodSchema, the later format-specific branches overwrite the earlier required schema (e.g., accountNumber for SPEI/ACH/WIRE and routingNumber for ACH/WIRE). This drops the fieldRequired message, so an empty value will show a format error like “CLABE must be exactly 18 digits” instead of “{{field}} is required”. Consider chaining the regex onto the existing schema (preserving required/optional) rather than replacing it.
    if (f.required) {
      schema = z.string().min(1, t("components.fiatAccountRegistration.validation.fieldRequired", { field: f.label }));
    } else {
      schema = z.string().optional();
    }

    if (f.field === "accountNumber" && accountType === "SPEI") {
      schema = z.string().regex(/^\d{18}$/, t("components.fiatAccountRegistration.validation.clabe"));
    }
    if (f.field === "routingNumber" && (accountType === "ACH" || accountType === "WIRE")) {
      schema = z.string().regex(/^\d{9}$/, t("components.fiatAccountRegistration.validation.routing"));
    }
    if (f.field === "accountNumber" && accountType === "ACH") {
      schema = z.string().regex(/^\d{4,17}$/, t("components.fiatAccountRegistration.validation.accountNumber"));
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +11 to +13
.string({
error: issue => (issue.input === undefined ? undefined : undefined)
})
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The email field no longer uses the existing “required” message and will report emailFormat even when the value is empty. Also, the error callback passed to z.string({ error: ... }) always returns undefined, which is effectively a no-op and makes the schema harder to understand. Consider using an explicit non-empty constraint (e.g., min(1) with pages.contact.validation.emailRequired) and then applying the format check (regex/email) so empty input shows the required message.

Suggested change
.string({
error: issue => (issue.input === undefined ? undefined : undefined)
})
.string()
.min(1, t("pages.contact.validation.emailRequired"))

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +31
[ExtendedAveniaFieldOptions.FULL_NAME]: z
.string()
.min(3, t("components.brlaExtendedForm.validation.fullName.minLength"))
.regex(/^[a-zA-Z\s]*$/, t("components.brlaExtendedForm.validation.fullName.format")),

[ExtendedAveniaFieldOptions.STATE]: yup
.string()
.required(t("components.brlaExtendedForm.validation.state.required"))
.max(2, t("components.brlaExtendedForm.validation.state.maxLength")),
[ExtendedAveniaFieldOptions.CEP]: z.string().min(3, t("components.brlaExtendedForm.validation.cep.minLength")),

[ExtendedAveniaFieldOptions.STREET]: yup
.string()
.required(t("components.brlaExtendedForm.validation.street.required"))
.min(5, t("components.brlaExtendedForm.validation.street.minLength")),
[ExtendedAveniaFieldOptions.CITY]: z.string().min(5, t("components.brlaExtendedForm.validation.city.minLength")),
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Several required fields no longer use their dedicated *.required i18n messages (e.g., fullName.required, cep.required, city.required, street.required). With the current Zod schema, empty input will surface the min-length message (or a generic type error) instead of the intended required message. If these fields are still required, add an explicit non-empty constraint before the min-length/format constraints so empty values show the correct translation.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +73
<div className={cn("relative", className)}>
<button
className={cn("btn relative z-10 flex w-full items-center justify-center gap-2", buttonClass)}
onClick={handleClick}
>
{icon}
{label}
</button>
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

<button> defaults to type="submit". Since SparkleButton is meant to be a reusable action button and may be placed inside a <form> in the future, it should explicitly set type="button" (or accept a type prop with a safe default) to avoid accidental form submissions.

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +51
return z
.object({
fiatToken: z.string().optional() as z.ZodType<FiatToken | undefined>,
moneriumWalletAddress: z.string().optional(),
pixId: z.string().optional(),
taxId: z.string().optional(),
walletAddress: z.string().optional()
})
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fiatToken is currently modeled as z.string().optional() as z.ZodType<FiatToken | undefined>, which bypasses runtime validation and will accept any string. Using z.nativeEnum(FiatToken) (and .optional() if needed) would keep the schema aligned with the actual enum and prevent invalid values from URL params / deserialization from silently passing validation.

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +47
.object({
deadline: z.number().optional(),
fiatToken: z.string() as z.ZodType<FiatToken>,
inputAmount: z.string().min(1, t("components.swap.validation.inputAmount.required")),
onChainToken: z.string() as z.ZodType<OnChainTokenSymbol>,
outputAmount: z.string().optional(),
pixId: z.string().optional(),
slippage: z.number().optional(),
taxId: z.string().optional()
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

fiatToken and onChainToken are typed via as z.ZodType<...> but are only validated as generic strings at runtime. That means invalid token strings can pass schema validation and then skip the superRefine branches (e.g., BRL-specific requirements). Prefer z.nativeEnum(FiatToken) / a z.enum([...]) for OnChainTokenSymbol to enforce valid values at runtime.

Copilot uses AI. Check for mistakes.
@Sharqiewicz Sharqiewicz changed the title [READY] Fix Avenia KYB Flow (and improve visual feedback) [READY] Fix 🇧🇷 Avenia KYB Flow (and improve visual feedback) Apr 9, 2026
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.

3 participants