Skip to content

[READY] filter out Nova Wallet option if not in the Nova app #635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
100 changes: 64 additions & 36 deletions frontend/src/components/BrlaComponents/BrlaExtendedForm.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,49 @@
import { useTranslation } from 'react-i18next';
import { useCallback } from 'react';

import { useKYCProcess } from '../../hooks/brla/useBRLAKYCProcess';
import { useKYCForm } from '../../hooks/brla/useKYCForm';
import { isValidCnpj } from '../../hooks/ramp/schema';
import { useRampKycLevel2Started } from '../../stores/rampStore';

import { VerificationStatus } from './VerificationStatus';
import { useBrlaKycTaxIdLocalStorage } from './useBrlaKycTaxIdLocalStorage';
import { BrlaFieldProps, ExtendedBrlaFieldOptions } from './BrlaField';
import { KYCForm } from './KYCForm';
import { useRampKycLevel2Started, useRampKycStarted } from '../../stores/rampStore';
import { useCallback } from 'react';
import { VerificationStatus } from './VerificationStatus';
import { DocumentUpload } from './KYCLevel2Form';
import { useTaxId } from '../../stores/ramp/useRampFormStore';
import { isValidCnpj } from '../../hooks/ramp/schema';
import { KYCForm } from './KYCForm';
import { useKYCFormLocalStorage } from './KYCForm/useKYCFormLocalStorage';

export const PIXKYCForm = () => {
const { verificationStatus, statusMessage, handleFormSubmit, handleBackClick, setIsSubmitted, setCpf, isSubmitted } = useKYCProcess();
const offrampKycLevel2Started = useRampKycLevel2Started();
const offrampKycStarted = useRampKycStarted();
const {
verificationStatus,
statusMessage,
handleFormSubmit: handleKYCFormSubmit,
handleBackClick,
setIsSubmitted,
setCpf,
isSubmitted,
} = useKYCProcess();

const rampKycLevel2Started = useRampKycLevel2Started();
const { kycForm } = useKYCForm();
const { clearStorage } = useKYCFormLocalStorage(kycForm);

const { t } = useTranslation();

const taxId = useTaxId();
const { taxId, clearTaxId } = useBrlaKycTaxIdLocalStorage();

const handleDocumentUploadSubmit = useCallback(() => {
if (!taxId) {
return;
}


const handleDocumentSubmit = useCallback(() => {
setIsSubmitted(true);
const taxIdToSet = taxId || null
setCpf(taxIdToSet);
setCpf(taxId);
}, [setIsSubmitted, setCpf, taxId]);

if (!taxId) {
return null;
}

const pixformFields: BrlaFieldProps[] = [
{
Expand Down Expand Up @@ -104,10 +119,6 @@ export const PIXKYCForm = () => {
},
];

if (!taxId) {
return null;
}

if (isValidCnpj(taxId)) {
pixformFields.push({
id: ExtendedBrlaFieldOptions.COMPANY_NAME,
Expand All @@ -133,27 +144,44 @@ export const PIXKYCForm = () => {
});
}

return (
<div className="relative">
{isSubmitted ? (
<VerificationStatus
status={verificationStatus}
message={statusMessage}
isLevel2={offrampKycLevel2Started}
/>
) : offrampKycLevel2Started ? (
if (isSubmitted) {
return (
<div className="relative">
<VerificationStatus status={verificationStatus} message={statusMessage} isLevel2={rampKycLevel2Started} />
</div>
);
}

if (rampKycLevel2Started) {
return (
<div className="relative">
<DocumentUpload
onSubmitHandler={handleDocumentSubmit}
onBackClick={handleBackClick}
/>
) : offrampKycStarted ? (
<KYCForm
fields={pixformFields}
form={kycForm}
onSubmit={handleFormSubmit}
onSubmitHandler={() => {
handleDocumentUploadSubmit();
clearTaxId();
}}
onBackClick={handleBackClick}
taxId={taxId}
/>
) : null}
</div>
);
}

return (
<div className="relative">
<KYCForm
fields={pixformFields}
form={kycForm}
onSubmit={async (formData) => {
await handleKYCFormSubmit(formData);
clearStorage();
}}
onBackClick={() => {
handleBackClick();
clearTaxId();
clearStorage();
}}
/>
</div>
);
};
6 changes: 2 additions & 4 deletions frontend/src/components/BrlaComponents/KYCForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useCallback } from 'react';
import { motion } from 'motion/react';
import { FormProvider, UseFormReturn } from 'react-hook-form';
import { useTranslation, Trans } from 'react-i18next';

import { BrlaField, BrlaFieldProps, ExtendedBrlaFieldOptions } from '../BrlaField';
import { KYCFormData } from '../../../hooks/brla/useKYCForm';
import { useWidgetMode } from '../../../hooks/useWidgetMode';

import { useKYCFormLocalStorage } from './useKYCFormLocalStorage';
interface KYCFormProps {
fields: BrlaFieldProps[];
form: UseFormReturn<KYCFormData>;
Expand All @@ -17,7 +15,7 @@ interface KYCFormProps {
export const KYCForm = ({ form, onSubmit, onBackClick, fields }: KYCFormProps) => {
const { handleSubmit } = form;
const { t } = useTranslation();
const isWidgetMode = useWidgetMode();


return (
<FormProvider {...form}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useEffect } from 'react';
import { Path, PathValue, UseFormReturn } from 'react-hook-form';
import { debounce } from '../../../hooks/useLocalStorage';

export const BRLA_KYC_FORM_STORAGE_KEY = 'brla_kyc_form_data';

export const useKYCFormLocalStorage = <T extends object>(form: UseFormReturn<T>) => {
const { watch, setValue } = form;

const saveToStorage = debounce((data: T) => {
localStorage.setItem(BRLA_KYC_FORM_STORAGE_KEY, JSON.stringify(data));
}, 500);

useEffect(() => {
const savedData = localStorage.getItem(BRLA_KYC_FORM_STORAGE_KEY);

if (!savedData) {
return;
}

try {
const parsedData = JSON.parse(savedData);
Object.entries(parsedData).forEach(([key, value]) => {
setValue(key as Path<T>, value as PathValue<T, Path<T>>, { shouldValidate: true });
});
} catch (error) {
console.error('Error loading form data from localStorage:', error);
}
}, [setValue]);

useEffect(() => {
const subscription = watch((data) => {
saveToStorage(data as T);
});
return () => subscription.unsubscribe();
}, [watch, saveToStorage]);

const clearStorage = () => {
localStorage.removeItem(BRLA_KYC_FORM_STORAGE_KEY);
};

return {
clearStorage,
};
};
67 changes: 21 additions & 46 deletions frontend/src/components/BrlaComponents/KYCLevel2Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@ import React, { useState } from 'react';
import { motion } from 'motion/react';
import { useTranslation } from 'react-i18next';
import { KycLevel2Toggle } from '../../KycLevel2Toggle';
import {
CameraIcon,
DocumentTextIcon,
CheckCircleIcon,
} from '@heroicons/react/24/outline';
import { CameraIcon, DocumentTextIcon, CheckCircleIcon } from '@heroicons/react/24/outline';
import { BrlaService, KYCDocType } from '../../../services/api';
import { useTaxId } from '../../../stores/ramp/useRampFormStore';

const MAX_FILE_SIZE = 15 * 1024 * 1024; // 15 MB
const ALLOWED_TYPES = ['image/png', 'image/jpeg', 'application/pdf'];

interface DocumentUploadProps {
onSubmitHandler: () => void;
onBackClick: () => void;
taxId: string;
}

async function uploadFileAsBuffer(file: File, url: string) {
Expand All @@ -37,12 +33,8 @@ async function uploadFileAsBuffer(file: File, url: string) {
}
}

export const DocumentUpload: React.FC<DocumentUploadProps> = ({
onSubmitHandler,
onBackClick,
}) => {
export const DocumentUpload: React.FC<DocumentUploadProps> = ({ onSubmitHandler, onBackClick, taxId }) => {
const { t } = useTranslation();
const taxId = useTaxId();

const [docType, setDocType] = useState<KYCDocType>(KYCDocType.RG);
const [selfie, setSelfie] = useState<File | null>(null);
Expand All @@ -64,7 +56,7 @@ export const DocumentUpload: React.FC<DocumentUploadProps> = ({
const validateAndSetFile = (
file: File | null,
setter: React.Dispatch<React.SetStateAction<File | null>>,
validSetter: React.Dispatch<React.SetStateAction<boolean>>
validSetter: React.Dispatch<React.SetStateAction<boolean>>,
) => {
if (!file) {
setter(null);
Expand All @@ -91,16 +83,14 @@ export const DocumentUpload: React.FC<DocumentUploadProps> = ({
const handleFileChange = (
e: React.ChangeEvent<HTMLInputElement>,
setter: React.Dispatch<React.SetStateAction<File | null>>,
validSetter: React.Dispatch<React.SetStateAction<boolean>>
validSetter: React.Dispatch<React.SetStateAction<boolean>>,
) => {
const file = e.target.files?.[0] || null;
validateAndSetFile(file, setter, validSetter);
};

const isSubmitDisabled =
loading ||
!selfieValid ||
(docType === KYCDocType.RG ? !frontValid || !backValid : !frontValid);
loading || !selfieValid || (docType === KYCDocType.RG ? !frontValid || !backValid : !frontValid);

const handleSubmit = async () => {
setError('');
Expand Down Expand Up @@ -129,7 +119,7 @@ export const DocumentUpload: React.FC<DocumentUploadProps> = ({
uploads.push(
uploadFileAsBuffer(selfie, response.uploadUrls.selfieUploadUrl),
uploadFileAsBuffer(front, response.uploadUrls.RGFrontUploadUrl),
uploadFileAsBuffer(back, response.uploadUrls.RGBackUploadUrl)
uploadFileAsBuffer(back, response.uploadUrls.RGBackUploadUrl),
);
} else {
if (!selfie || !front) {
Expand All @@ -139,7 +129,7 @@ export const DocumentUpload: React.FC<DocumentUploadProps> = ({
}
uploads.push(
uploadFileAsBuffer(selfie, response.uploadUrls.selfieUploadUrl),
uploadFileAsBuffer(front, response.uploadUrls.CNHUploadUrl)
uploadFileAsBuffer(front, response.uploadUrls.CNHUploadUrl),
);
}

Expand All @@ -156,20 +146,13 @@ export const DocumentUpload: React.FC<DocumentUploadProps> = ({
label: string,
onChange: React.ChangeEventHandler<HTMLInputElement> | undefined,
valid: boolean,
Icon: React.ComponentType<React.SVGProps<SVGSVGElement>>
Icon: React.ComponentType<React.SVGProps<SVGSVGElement>>,
) => (
<label className="relative flex flex-col items-center justify-center p-6 border-2 border-dashed rounded-lg cursor-pointer hover:border-blue-500">
<Icon className="w-12 h-12 text-gray-400 mb-2" />
<span className="text-gray-600 mb-1">{label}</span>
<input
type="file"
accept=".png,.jpeg,.jpg,.pdf"
onChange={onChange}
className="hidden"
/>
{valid && (
<CheckCircleIcon className="absolute top-2 right-2 w-6 h-6 text-green-500" />
)}
<input type="file" accept=".png,.jpeg,.jpg,.pdf" onChange={onChange} className="hidden" />
{valid && <CheckCircleIcon className="absolute top-2 right-2 w-6 h-6 text-green-500" />}
</label>
);

Expand All @@ -180,49 +163,41 @@ export const DocumentUpload: React.FC<DocumentUploadProps> = ({
transition={{ duration: 0.3 }}
className="px-4 pt-6 pb-8 mx-4 mt-8 mb-4 rounded-lg shadow-custom md:mx-auto md:w-96 bg-white"
>
<h2 className="text-2xl font-semibold text-center text-blue-700 mb-6">
{t('components.documentUpload.title')}
</h2>
<p className="text-center text-gray-600 mb-4">
{t('components.documentUpload.description')}
</p>

<KycLevel2Toggle
activeDocType={docType}
onToggle={setDocType}
/>
<h2 className="text-2xl font-semibold text-center text-blue-700 mb-6">{t('components.documentUpload.title')}</h2>
<p className="text-center text-gray-600 mb-4">{t('components.documentUpload.description')}</p>

<KycLevel2Toggle activeDocType={docType} onToggle={setDocType} />

<div className="grid grid-cols-1 gap-4">
{renderField(
t('components.documentUpload.fields.uploadSelfie'),
(e) => handleFileChange(e, setSelfie, setSelfieValid),
selfieValid,
CameraIcon
CameraIcon,
)}
{docType === KYCDocType.RG && (
<>
{renderField(
t('components.documentUpload.fields.rgFront'),
(e) => handleFileChange(e, setFront, setFrontValid),
frontValid,
DocumentTextIcon
DocumentTextIcon,
)}
{renderField(
t('components.documentUpload.fields.rgBack'),
(e) => handleFileChange(e, setBack, setBackValid),
backValid,
DocumentTextIcon
DocumentTextIcon,
)}
</>
)}
{docType === KYCDocType.CNH && (
{docType === KYCDocType.CNH &&
renderField(
t('components.documentUpload.fields.cnhDocument'),
(e) => handleFileChange(e, setFront, setFrontValid),
frontValid,
DocumentTextIcon
)
)}
DocumentTextIcon,
)}
</div>

{error && <p className="text-red-500 text-center mt-4">{error}</p>}
Expand Down
Loading
Loading