@myazahq/kyc-sdk-react-native 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +35 -3
- package/android/src/main/java/com/margelo/nitro/myazakyc/HybridMyazaFaceDetector.kt +75 -0
- package/ios/HybridMyazaFaceDetector.swift +8 -0
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.cpp +23 -0
- package/nitrogen/generated/android/c++/JHybridMyazaFaceDetectorSpec.hpp +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/myazakyc/HybridMyazaFaceDetectorSpec.kt +9 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/KycSdkReactNative-Swift-Cxx-Bridge.hpp +43 -0
- package/nitrogen/generated/ios/c++/HybridMyazaFaceDetectorSpecSwift.hpp +17 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridMyazaFaceDetectorSpec_cxx.swift +31 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridMyazaFaceDetectorSpec.hpp +3 -0
- package/package.json +1 -2
- package/src/MyazaKYC.tsx +12 -0
- package/src/components/BrandBar.tsx +137 -0
- package/src/components/DashedBorder.tsx +55 -0
- package/src/components/DialCodePicker.tsx +25 -24
- package/src/components/DocumentReviewSide.tsx +34 -14
- package/src/components/Icon.tsx +14 -0
- package/src/components/KycSheet.tsx +39 -140
- package/src/components/MediaSourceSheet.tsx +7 -37
- package/src/components/MultiIdProgress.tsx +107 -0
- package/src/components/MyazaDateField.tsx +6 -20
- package/src/components/MyazaInput.tsx +23 -4
- package/src/components/MyazaSelect.tsx +17 -40
- package/src/components/OwnershipSlider.tsx +102 -0
- package/src/components/PhoneNumberInput.tsx +37 -4
- package/src/components/SandboxBanner.tsx +92 -0
- package/src/components/StaggerIn.tsx +63 -0
- package/src/components/StepHeader.tsx +15 -2
- package/src/components/StepView.tsx +26 -0
- package/src/components/glass/ChromeGlass.tsx +65 -0
- package/src/components/glass/FloatingSheet.tsx +242 -0
- package/src/components/glass/GlassSheet.tsx +50 -0
- package/src/components/glass/GlassSurface.tsx +31 -3
- package/src/components/stepHeaderMeta.tsx +3 -1
- package/src/components/viewfinder/ImmersiveBottomBar.tsx +28 -17
- package/src/components/viewfinder/ImmersiveControls.tsx +26 -14
- package/src/components/viewfinder/ViewfinderControls.tsx +29 -7
- package/src/config/business.ts +6 -6
- package/src/config/businessDetailsValidity.ts +52 -0
- package/src/config/businessPrefill.ts +72 -0
- package/src/config/businessSteps.ts +14 -3
- package/src/config/keyPeople.ts +111 -7
- package/src/config/keyPeopleCorporate.ts +19 -0
- package/src/config/keyPeopleOwnerTypes.ts +12 -0
- package/src/config/keyPeopleOwners.ts +26 -0
- package/src/config/keyPeoplePrefill.ts +138 -0
- package/src/config/keyPeopleSectionDefs.ts +65 -0
- package/src/config/keyPeopleSections.ts +167 -0
- package/src/config/registrationHint.ts +1 -1
- package/src/config/stepOrder.ts +24 -4
- package/src/config/theme.ts +8 -2
- package/src/config/website.ts +36 -0
- package/src/config/workflowMerge.ts +5 -0
- package/src/emrtd/activeAuth.ts +100 -0
- package/src/emrtd/extras.ts +76 -0
- package/src/emrtd/files.ts +5 -0
- package/src/emrtd/index.ts +1 -0
- package/src/emrtd/read.ts +8 -1
- package/src/emrtd/session.ts +41 -0
- package/src/emrtd/stages.ts +11 -0
- package/src/lib/contact-recovery.ts +44 -0
- package/src/lib/multi-id.ts +210 -0
- package/src/lib/resubmit.ts +130 -0
- package/src/lib/screen-corners.ts +50 -0
- package/src/lib/step-log.ts +24 -3
- package/src/lib/use-multi-id-plan.ts +29 -0
- package/src/liveness/useModelReady.ts +65 -0
- package/src/liveness/visionCameraFaceDetector.ts +35 -0
- package/src/screens/ApplicantRoleStep.tsx +9 -5
- package/src/screens/BusinessCheckPanel.tsx +89 -0
- package/src/screens/BusinessDetailsFields.tsx +109 -0
- package/src/screens/BusinessDetailsStep.tsx +131 -145
- package/src/screens/BusinessDocumentSlot.tsx +2 -3
- package/src/screens/BusinessDocumentsStep.tsx +1 -1
- package/src/screens/BusinessKeyPeopleStep.tsx +103 -71
- package/src/screens/BusinessPickedCard.tsx +86 -0
- package/src/screens/BusinessPickedSection.tsx +51 -0
- package/src/screens/BusinessRegistryPickers.tsx +67 -0
- package/src/screens/BusinessSandboxToggle.tsx +114 -0
- package/src/screens/BusinessSearch.tsx +199 -0
- package/src/screens/BusinessSearchResults.tsx +115 -0
- package/src/screens/CompanyInfoFields.tsx +86 -51
- package/src/screens/ConsentStep.tsx +31 -0
- package/src/screens/ContactDestinationField.tsx +1 -0
- package/src/screens/ContactVerificationStep.tsx +34 -3
- package/src/screens/IdTypeStep.tsx +12 -5
- package/src/screens/KeepLinksSheet.tsx +88 -0
- package/src/screens/KeyPeopleAwaitCard.tsx +204 -0
- package/src/screens/KeyPeopleAwaitList.tsx +126 -0
- package/src/screens/KeyPeoplePending.tsx +154 -0
- package/src/screens/KeyPeopleSection.tsx +162 -0
- package/src/screens/KeyPeopleSectionsList.tsx +103 -0
- package/src/screens/KeyPeopleUboExemption.tsx +72 -0
- package/src/screens/KeyPersonCard.tsx +36 -4
- package/src/screens/KeyPersonForm.tsx +195 -36
- package/src/screens/KeyPersonKindToggle.tsx +68 -0
- package/src/screens/KeyPersonOwners.tsx +109 -0
- package/src/screens/KeyPersonRoleChips.tsx +68 -0
- package/src/screens/KeyPersonSheet.tsx +92 -108
- package/src/screens/LivenessStep.tsx +18 -0
- package/src/screens/NfcStep.tsx +14 -0
- package/src/screens/ProofOfAddressStep.tsx +6 -3
- package/src/screens/SubmittedStep.tsx +66 -10
- package/src/screens/nfc/NfcReadProgress.tsx +1 -0
- package/src/screens/useAwaitingPeople.ts +90 -0
- package/src/services/api-types.ts +158 -2
- package/src/services/api-verify-types.ts +53 -1
- package/src/services/api.ts +104 -0
- package/src/services/deviceMetadata.ts +1 -1
- package/src/services/errors.ts +10 -2
- package/src/services/workflowGate.ts +1 -0
- package/src/specs/MyazaFaceDetector.nitro.ts +29 -0
- package/src/store/businessCheck.ts +113 -0
- package/src/store/derive.ts +15 -0
- package/src/store/kycStore.ts +241 -14
- package/src/store/serverConfig.ts +2 -0
- package/src/store/session.ts +208 -0
- package/src/store/state.ts +155 -2
- package/src/store/submit.ts +63 -13
- package/src/store/submitApplicant.ts +5 -11
- package/src/types/business.ts +23 -2
- package/src/types/config.ts +29 -3
- package/src/types/verification.ts +1 -1
- package/src/types/workflow.ts +15 -0
- package/src/screens/KeyPeopleInviteLinks.tsx +0 -239
|
@@ -2,34 +2,33 @@ import React, { useMemo, useState } from 'react';
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { spacing } from '../config/theme';
|
|
5
|
-
import { useKyc, useKycConfig, useKycStore
|
|
6
|
-
import { MyazaText } from '../components/Typography';
|
|
5
|
+
import { useKyc, useKycConfig, useKycStore } from '../components/runtime';
|
|
7
6
|
import { MyazaButton } from '../components/MyazaButton';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
7
|
+
import { BusinessRegistryPickers } from './BusinessRegistryPickers';
|
|
8
|
+
import { BusinessSearch } from './BusinessSearch';
|
|
9
|
+
import { BusinessPickedSection } from './BusinessPickedSection';
|
|
10
|
+
import { BusinessDetailsFields } from './BusinessDetailsFields';
|
|
11
|
+
import { BusinessCheckPanel } from './BusinessCheckPanel';
|
|
12
12
|
import {
|
|
13
13
|
businessCountriesFor,
|
|
14
|
-
businessCountryName,
|
|
15
14
|
businessProductsForCountry,
|
|
16
15
|
companyInfoFieldModes,
|
|
17
16
|
getBusinessProductDef,
|
|
18
17
|
keyPeopleNeedsContactEmail,
|
|
19
|
-
missingRequiredCompanyInfo,
|
|
20
18
|
} from '../config/business';
|
|
21
19
|
import { registrationNumberHint } from '../config/registrationHint';
|
|
22
|
-
import {
|
|
23
|
-
import
|
|
20
|
+
import { registerPrefillPatch } from '../config/businessPrefill';
|
|
21
|
+
import { businessDetailsValid, checkPanelVisible } from '../config/businessDetailsValidity';
|
|
24
22
|
|
|
25
23
|
// ---------------------------------------------------------------------------
|
|
26
|
-
// Business (KYB) registry details.
|
|
24
|
+
// Business (KYB) registry details — two screens in one step.
|
|
27
25
|
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
26
|
+
// 'pick' is choosing WHICH company. 'details' is confirming what the register
|
|
27
|
+
// then said about it. They are separate because the register has not been
|
|
28
|
+
// asked yet while you are still picking, so showing the detail fields there
|
|
29
|
+
// would invite somebody to fill in answers we are about to overwrite.
|
|
30
|
+
// Continue is what runs the paid check and moves between them. Mirrors the
|
|
31
|
+
// web SDK's BusinessDetailsStep — keep the two in lockstep.
|
|
33
32
|
// ---------------------------------------------------------------------------
|
|
34
33
|
|
|
35
34
|
export const businessDetailsMeta = {
|
|
@@ -41,172 +40,159 @@ export const businessDetailsMeta = {
|
|
|
41
40
|
export function BusinessDetailsStep(): React.ReactElement {
|
|
42
41
|
const config = useKycConfig();
|
|
43
42
|
const store = useKycStore();
|
|
44
|
-
const { colors } = useTheme();
|
|
45
43
|
const business = useKyc((s) => s.business);
|
|
46
|
-
const
|
|
44
|
+
const check = useKyc((s) => s.businessCheck);
|
|
45
|
+
const [phase, setPhase] = useState<'pick' | 'details'>('pick');
|
|
46
|
+
// Production never shows the test-result toggle, and never honours a pin.
|
|
47
|
+
const isSandbox = useKyc((s) => s.serverConfig.environment) !== 'PRODUCTION';
|
|
47
48
|
|
|
48
49
|
const workflowBusiness = config.business;
|
|
49
50
|
const countries = useMemo(() => businessCountriesFor(workflowBusiness), [workflowBusiness]);
|
|
50
|
-
// Precedence mirrors the web SDK
|
|
51
|
-
//
|
|
52
|
-
// the offered list but not necessarily FIRST — preferring `countries[0]`
|
|
53
|
-
// defaulted the picker to the wrong registry whenever the primary sat later.
|
|
51
|
+
// Precedence mirrors the web SDK: the visitor's pick, then the workflow's
|
|
52
|
+
// PRIMARY country, then the list head.
|
|
54
53
|
const country = business.country ?? workflowBusiness?.country ?? countries[0] ?? '';
|
|
55
54
|
const products = useMemo(
|
|
56
55
|
() => businessProductsForCountry(workflowBusiness, country),
|
|
57
56
|
[workflowBusiness, country],
|
|
58
57
|
);
|
|
59
|
-
// Re-derived per country rather than remembered: a product the picked country
|
|
60
|
-
// does not offer would be rejected at submit.
|
|
61
58
|
const product = products.includes(business.product ?? '') ? business.product! : products[0]!;
|
|
62
59
|
const productDef = getBusinessProductDef(product);
|
|
63
60
|
|
|
64
61
|
const modes = companyInfoFieldModes(workflowBusiness);
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
);
|
|
68
|
-
const needsContactEmail = keyPeopleNeedsContactEmail(workflowBusiness);
|
|
62
|
+
const showCompanyInfo = Object.values(modes).some((m) => m !== 'off');
|
|
63
|
+
const showContactEmail = keyPeopleNeedsContactEmail(workflowBusiness);
|
|
69
64
|
|
|
70
65
|
const set = <K extends keyof typeof business>(key: K, value: (typeof business)[K]): void =>
|
|
71
66
|
store.getState().setBusinessField(key, value);
|
|
72
67
|
|
|
73
|
-
const missingCompanyInfo = missingRequiredCompanyInfo(workflowBusiness, {
|
|
74
|
-
address: business.address,
|
|
75
|
-
email: business.email,
|
|
76
|
-
phone: business.phone,
|
|
77
|
-
website: business.website,
|
|
78
|
-
});
|
|
79
|
-
const contactEmailInvalid =
|
|
80
|
-
needsContactEmail && business.contactEmail.trim() !== '' && !isValidContactEmail(business.contactEmail);
|
|
81
|
-
const nameMissing =
|
|
82
|
-
workflowBusiness?.requireRegistrationName === true && business.registrationName.trim() === '';
|
|
83
|
-
|
|
84
|
-
// Country-aware registration-number guidance (NG: CAC prefix rules + format
|
|
85
|
-
// validation; elsewhere: placeholder + registry tip). Mirrors the web SDK.
|
|
86
68
|
const regHint = registrationNumberHint(country, productDef);
|
|
87
69
|
const regNumber = business.registrationNumber;
|
|
70
|
+
// A company has been named, so the card replaces the search. The NUMBER is
|
|
71
|
+
// what names it (a prefilled session sends one without a name; the register
|
|
72
|
+
// supplies the name on Continue, so an unnamed card is momentary).
|
|
73
|
+
const picked = regNumber.trim() !== '';
|
|
74
|
+
const nameRequired = workflowBusiness?.requireRegistrationName === true;
|
|
88
75
|
const formatOk =
|
|
89
76
|
!regHint.isValidFormat || regNumber.trim() === '' || regHint.isValidFormat(regNumber);
|
|
90
77
|
const numberValid = regNumber.trim().length >= 2 && formatOk;
|
|
91
|
-
const canContinue =
|
|
92
|
-
numberValid && !nameMissing && missingCompanyInfo.length === 0 && !contactEmailInvalid;
|
|
93
78
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
79
|
+
const isFormValid = businessDetailsValid({
|
|
80
|
+
phase,
|
|
81
|
+
business,
|
|
82
|
+
modes,
|
|
83
|
+
product,
|
|
84
|
+
numberValid,
|
|
85
|
+
nameRequired,
|
|
86
|
+
showContactEmail,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const checking = check.status === 'checking';
|
|
90
|
+
|
|
91
|
+
const handleContinue = async (): Promise<void> => {
|
|
92
|
+
if (!isFormValid || checking) return;
|
|
97
93
|
// Persist the resolved country/product so the submission uses exactly what
|
|
98
|
-
// was on screen
|
|
99
|
-
|
|
100
|
-
set('
|
|
94
|
+
// was on screen. Only when different: these are identity keys, and writing
|
|
95
|
+
// an unchanged value would needlessly reset the check we just ran.
|
|
96
|
+
if (business.country !== country) set('country', country);
|
|
97
|
+
if (business.product !== product) set('product', product);
|
|
98
|
+
|
|
99
|
+
// The paid registry check — awaited, so the details screen opens already
|
|
100
|
+
// holding what the register said. Only a definitive "not on the register"
|
|
101
|
+
// stops the flow: everything else (a short balance, an outage) continues
|
|
102
|
+
// and is checked at submission, as it was before.
|
|
103
|
+
const { canContinue, company } = await store.getState().checkBusiness();
|
|
104
|
+
if (!canContinue) return;
|
|
105
|
+
|
|
106
|
+
// First Continue ends at the details screen rather than the next step: the
|
|
107
|
+
// register has only just answered, and this is where what it said gets put
|
|
108
|
+
// in front of them to confirm or correct.
|
|
109
|
+
if (phase === 'pick') {
|
|
110
|
+
const { patch, prefilled } = registerPrefillPatch(company, store.getState().business);
|
|
111
|
+
if (prefilled.length > 0) store.getState().applyBusinessPrefill(patch, prefilled);
|
|
112
|
+
setPhase('details');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
101
115
|
store.getState().nextStep();
|
|
102
116
|
};
|
|
103
117
|
|
|
104
118
|
return (
|
|
105
119
|
<View>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// The product list narrows per country, so a stale pick is
|
|
119
|
-
// cleared rather than carried into a refused submission.
|
|
120
|
-
set('product', null);
|
|
121
|
-
}}
|
|
122
|
-
/>
|
|
123
|
-
<View style={{ height: spacing.md }} />
|
|
124
|
-
</>
|
|
125
|
-
) : null}
|
|
126
|
-
|
|
127
|
-
{products.length > 1 ? (
|
|
128
|
-
<>
|
|
129
|
-
<MyazaText variant="bodySmall" style={{ fontWeight: '600', marginBottom: spacing.xs }}>
|
|
130
|
-
Verification type
|
|
131
|
-
</MyazaText>
|
|
132
|
-
<MyazaSelect
|
|
133
|
-
value={product}
|
|
134
|
-
sheetTitle="Verification type"
|
|
135
|
-
options={products.map((key) => ({
|
|
136
|
-
value: key,
|
|
137
|
-
label: getBusinessProductDef(key).label,
|
|
138
|
-
}))}
|
|
139
|
-
onChange={(key) => set('product', key)}
|
|
140
|
-
/>
|
|
141
|
-
<View style={{ height: spacing.md }} />
|
|
142
|
-
</>
|
|
143
|
-
) : null}
|
|
144
|
-
|
|
145
|
-
<MyazaInput
|
|
146
|
-
label={productDef.inputLabel}
|
|
147
|
-
value={business.registrationNumber}
|
|
148
|
-
onChangeText={(text) => set('registrationNumber', text)}
|
|
149
|
-
placeholder={regHint.placeholder}
|
|
150
|
-
autoCapitalize="characters"
|
|
151
|
-
autoFocus
|
|
152
|
-
// Live like the web SDK: a wrong CAC prefix is corrected on the spot,
|
|
153
|
-
// not discovered on Continue. The registry tip fills the same slot
|
|
154
|
-
// until there is an error to show.
|
|
155
|
-
error={
|
|
156
|
-
regNumber !== '' && !numberValid
|
|
157
|
-
? (!formatOk && regHint.formatError) ||
|
|
158
|
-
`Enter a valid ${productDef.inputLabel.toLowerCase()}.`
|
|
159
|
-
: null
|
|
160
|
-
}
|
|
161
|
-
helper={regHint.tip ?? undefined}
|
|
120
|
+
<BusinessRegistryPickers
|
|
121
|
+
country={country}
|
|
122
|
+
countries={countries}
|
|
123
|
+
product={product}
|
|
124
|
+
products={products}
|
|
125
|
+
onCountry={(code) => {
|
|
126
|
+
set('country', code);
|
|
127
|
+
// The product list narrows per country, so a stale pick is cleared
|
|
128
|
+
// rather than carried into a refused submission.
|
|
129
|
+
set('product', null);
|
|
130
|
+
}}
|
|
131
|
+
onProduct={(key) => set('product', key)}
|
|
162
132
|
/>
|
|
163
133
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
134
|
+
{/* HIDDEN, not unmounted, once a company is chosen: unmounting threw
|
|
135
|
+
away the query and results, so "Change" dropped somebody back to an
|
|
136
|
+
empty box. Hiding keeps the picker alive, which makes Change cheap. */}
|
|
137
|
+
<View style={{ display: phase === 'pick' && country && !picked ? 'flex' : 'none' }}>
|
|
138
|
+
<BusinessSearch
|
|
139
|
+
country={country}
|
|
140
|
+
onPicked={(hit) => {
|
|
141
|
+
// Names the company and nothing more: the register is asked on
|
|
142
|
+
// Continue, and the fields it fills live on the screen after.
|
|
143
|
+
set('registrationNumber', hit.registrationNumber);
|
|
144
|
+
set('registrationName', hit.name);
|
|
145
|
+
}}
|
|
146
|
+
onManualEntry={() => setPhase('details')}
|
|
147
|
+
/>
|
|
148
|
+
</View>
|
|
149
|
+
|
|
150
|
+
{phase === 'pick' && picked ? (
|
|
151
|
+
<BusinessPickedSection
|
|
152
|
+
country={country}
|
|
153
|
+
name={business.registrationName}
|
|
154
|
+
registrationNumber={regNumber}
|
|
155
|
+
isSandbox={isSandbox}
|
|
156
|
+
onChange={() => {
|
|
157
|
+
// Back to the search rather than an undo: they are changing WHICH
|
|
158
|
+
// company this is about, and the fields belong to the old one.
|
|
159
|
+
set('registrationNumber', '');
|
|
160
|
+
set('registrationName', '');
|
|
161
|
+
}}
|
|
162
|
+
/>
|
|
163
|
+
) : null}
|
|
172
164
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
165
|
+
{phase === 'details' ? (
|
|
166
|
+
<BusinessDetailsFields
|
|
167
|
+
business={business}
|
|
168
|
+
productDef={productDef}
|
|
169
|
+
regHint={regHint}
|
|
170
|
+
numberValid={numberValid}
|
|
171
|
+
formatOk={formatOk}
|
|
172
|
+
requireName={nameRequired}
|
|
173
|
+
country={country}
|
|
174
|
+
modes={modes}
|
|
175
|
+
showCompanyInfo={showCompanyInfo}
|
|
176
|
+
showContactEmail={showContactEmail}
|
|
177
|
+
onChange={set}
|
|
178
|
+
/>
|
|
179
|
+
) : null}
|
|
186
180
|
|
|
187
|
-
{
|
|
181
|
+
{checkPanelVisible(check.status) ? (
|
|
188
182
|
<>
|
|
189
|
-
<View style={{ height: spacing.
|
|
190
|
-
<
|
|
191
|
-
label="Contact email for owner verification (optional)"
|
|
192
|
-
value={business.contactEmail}
|
|
193
|
-
onChangeText={(text) => set('contactEmail', text)}
|
|
194
|
-
placeholder="admin@company.com"
|
|
195
|
-
keyboardType="email-address"
|
|
196
|
-
autoCapitalize="none"
|
|
197
|
-
helper="We'll email this address a link for your directors and owners to verify their identity."
|
|
198
|
-
error={touched && contactEmailInvalid ? 'Enter a valid email address.' : null}
|
|
199
|
-
/>
|
|
183
|
+
<View style={{ height: spacing.lg }} />
|
|
184
|
+
<BusinessCheckPanel check={check} />
|
|
200
185
|
</>
|
|
201
186
|
) : null}
|
|
202
187
|
|
|
203
|
-
<View style={{ height: spacing.
|
|
204
|
-
<MyazaButton
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
188
|
+
<View style={{ height: spacing.lg }} />
|
|
189
|
+
<MyazaButton
|
|
190
|
+
label={checking ? 'Checking…' : phase === 'details' ? 'Confirm details & continue' : 'Continue'}
|
|
191
|
+
onPress={() => void handleContinue()}
|
|
192
|
+
loading={checking}
|
|
193
|
+
disabled={!isFormValid || checking}
|
|
194
|
+
/>
|
|
210
195
|
</View>
|
|
211
196
|
);
|
|
212
197
|
}
|
|
198
|
+
|
|
@@ -5,6 +5,7 @@ import { radius, spacing } from '../config/theme';
|
|
|
5
5
|
import { useTheme } from '../components/runtime';
|
|
6
6
|
import { MyazaText } from '../components/Typography';
|
|
7
7
|
import { Icon } from '../components/Icon';
|
|
8
|
+
import { DashedBorder } from '../components/DashedBorder';
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// One business-document upload slot — mirrors the web SDK's
|
|
@@ -134,15 +135,13 @@ export function BusinessDocumentSlot({
|
|
|
134
135
|
style={{
|
|
135
136
|
flexDirection: 'row',
|
|
136
137
|
alignItems: 'center',
|
|
137
|
-
borderWidth: 2,
|
|
138
|
-
borderStyle: 'dashed',
|
|
139
|
-
borderColor: colors.border,
|
|
140
138
|
borderRadius: radius.sm,
|
|
141
139
|
padding: spacing.md,
|
|
142
140
|
marginBottom: spacing.sm + 4,
|
|
143
141
|
opacity: uploading ? 0.7 : 1,
|
|
144
142
|
}}
|
|
145
143
|
>
|
|
144
|
+
<DashedBorder color={colors.border} radius={radius.sm} />
|
|
146
145
|
{uploading ? (
|
|
147
146
|
<ActivityIndicator size="small" color={colors.primary} />
|
|
148
147
|
) : (
|
|
@@ -32,7 +32,7 @@ import { loadDocumentPicker } from '../services/documentPicker';
|
|
|
32
32
|
export const businessDocumentsMeta = {
|
|
33
33
|
title: 'Business documents',
|
|
34
34
|
description:
|
|
35
|
-
'Upload the supporting documents for your business. Required documents are marked with *.',
|
|
35
|
+
'Upload the supporting documents for your business. Each one must clearly show the registered business name and registration number. Required documents are marked with *.',
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
export function BusinessDocumentsStep(): React.ReactElement {
|