@airxpay/sdk-ui 1.0.6 → 1.0.8

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.
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Frontend SDK - API Client
3
+ * Axios instance with interceptors for token management and refresh
4
+ */
5
+ import { AxiosInstance } from 'axios';
6
+ export declare const createApiClient: (publicKey: string) => AxiosInstance;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * Frontend SDK - API Client
4
+ * Axios instance with interceptors for token management and refresh
5
+ */
6
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
+ return new (P || (P = Promise))(function (resolve, reject) {
9
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
13
+ });
14
+ };
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.createApiClient = void 0;
20
+ const axios_1 = __importDefault(require("axios"));
21
+ const tokenStorage_1 = require("../utils/tokenStorage");
22
+ const BASE_URL = 'https://api.airxpay.com/api/merchant';
23
+ let isRefreshing = false;
24
+ let failedQueue = [];
25
+ const processQueue = (error, token = null) => {
26
+ failedQueue.forEach(prom => {
27
+ if (error) {
28
+ prom.reject(error);
29
+ }
30
+ else {
31
+ prom.resolve(token);
32
+ }
33
+ });
34
+ failedQueue = [];
35
+ };
36
+ const createApiClient = (publicKey) => {
37
+ const client = axios_1.default.create({
38
+ baseURL: BASE_URL,
39
+ timeout: 30000,
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ 'X-Public-Key': publicKey,
43
+ },
44
+ });
45
+ // Request interceptor - automatically attach token
46
+ client.interceptors.request.use((config) => __awaiter(void 0, void 0, void 0, function* () {
47
+ const token = yield (0, tokenStorage_1.getStoredToken)();
48
+ if (token) {
49
+ config.headers.Authorization = `Bearer ${token}`;
50
+ }
51
+ return config;
52
+ }), (error) => Promise.reject(error));
53
+ // Response interceptor - handle 401 and token refresh
54
+ client.interceptors.response.use((response) => response, (error) => __awaiter(void 0, void 0, void 0, function* () {
55
+ var _a;
56
+ const originalRequest = error.config;
57
+ if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) !== 401 || originalRequest._retry) {
58
+ return Promise.reject(error);
59
+ }
60
+ if (isRefreshing) {
61
+ // Queue failed requests while refreshing
62
+ return new Promise((resolve, reject) => {
63
+ failedQueue.push({ resolve, reject });
64
+ })
65
+ .then(token => {
66
+ originalRequest.headers.Authorization = `Bearer ${token}`;
67
+ return client(originalRequest);
68
+ })
69
+ .catch(err => Promise.reject(err));
70
+ }
71
+ originalRequest._retry = true;
72
+ isRefreshing = true;
73
+ try {
74
+ const currentToken = yield (0, tokenStorage_1.getStoredToken)();
75
+ if (!currentToken) {
76
+ throw new Error('No token to refresh');
77
+ }
78
+ // Call refresh token endpoint
79
+ const response = yield axios_1.default.post(`${BASE_URL}/merchant/refresh-token`, null, {
80
+ headers: { Authorization: `Bearer ${currentToken}` },
81
+ });
82
+ const { token: newToken } = response.data;
83
+ if (!newToken) {
84
+ throw new Error('No token in refresh response');
85
+ }
86
+ yield (0, tokenStorage_1.setStoredToken)(newToken);
87
+ // Update auth header
88
+ originalRequest.headers.Authorization = `Bearer ${newToken}`;
89
+ processQueue(null, newToken);
90
+ return client(originalRequest);
91
+ }
92
+ catch (refreshError) {
93
+ processQueue(refreshError, null);
94
+ // Clear invalid token
95
+ yield (0, tokenStorage_1.clearStoredToken)();
96
+ return Promise.reject(refreshError);
97
+ }
98
+ finally {
99
+ isRefreshing = false;
100
+ }
101
+ }));
102
+ return client;
103
+ };
104
+ exports.createApiClient = createApiClient;
@@ -1,11 +1,32 @@
1
- export interface MerchantInitResponse {
2
- merchantId: string;
3
- airxpayMerchantId: string;
4
- walletId: string;
5
- mode: "live" | "test";
1
+ /**
2
+ * Frontend SDK - Merchant API
3
+ * All merchant-related API calls
4
+ */
5
+ import { AxiosInstance } from 'axios';
6
+ import { CreateMerchantPayload, MerchantCreateResponse, MerchantStatus, KycStatus } from '../types/merchantTypes';
7
+ export declare const initializeApi: (publicKey: string) => void;
8
+ export declare const getApiClient: () => AxiosInstance;
9
+ /**
10
+ * Verify public key during initialization
11
+ */
12
+ export declare const verifyPublicKey: (publicKey: string) => Promise<{
13
+ valid: boolean;
14
+ merchantData?: any;
15
+ }>;
16
+ /**
17
+ * Create new merchant
18
+ * Automatically stores returned token
19
+ */
20
+ export declare const createMerchant: (payload: CreateMerchantPayload) => Promise<MerchantCreateResponse>;
21
+ /**
22
+ * Get merchant status
23
+ */
24
+ export declare const getMerchantStatus: () => Promise<{
25
+ success: boolean;
26
+ status: MerchantStatus;
27
+ mode: "test" | "live";
6
28
  isKycCompleted: boolean;
7
29
  isBankDetailsCompleted: boolean;
8
- kycStatus: "not_submitted" | "pending" | "verified" | "rejected";
9
- status: "active" | "suspended" | "blocked";
10
- }
11
- export declare const verifyPublicKey: (publicKey: string) => Promise<MerchantInitResponse>;
30
+ kycStatus: KycStatus;
31
+ bankDetails?: any;
32
+ }>;
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
- // api/merchant.ts
2
+ /**
3
+ * Frontend SDK - Merchant API
4
+ * All merchant-related API calls
5
+ */
3
6
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
7
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
8
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -10,26 +13,57 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
13
  });
11
14
  };
12
15
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.verifyPublicKey = void 0;
16
+ exports.getMerchantStatus = exports.createMerchant = exports.verifyPublicKey = exports.getApiClient = exports.initializeApi = void 0;
17
+ const client_1 = require("./client");
18
+ const tokenStorage_1 = require("../utils/tokenStorage");
19
+ let apiClient = null;
20
+ const initializeApi = (publicKey) => {
21
+ if (!(publicKey === null || publicKey === void 0 ? void 0 : publicKey.trim())) {
22
+ throw new Error('Public key is required');
23
+ }
24
+ apiClient = (0, client_1.createApiClient)(publicKey);
25
+ };
26
+ exports.initializeApi = initializeApi;
27
+ const getApiClient = () => {
28
+ if (!apiClient) {
29
+ throw new Error('API not initialized. Call initializeApi() first.');
30
+ }
31
+ return apiClient;
32
+ };
33
+ exports.getApiClient = getApiClient;
14
34
  /**
15
- * 🔐 Production AirXPay API Base URL
16
- * Developer ko baseUrl pass karne ki zarurat nahi
35
+ * Verify public key during initialization
17
36
  */
18
- const AIRXPAY_BASE_URL = "http://172.20.10.12:7000";
19
37
  const verifyPublicKey = (publicKey) => __awaiter(void 0, void 0, void 0, function* () {
20
- if (!publicKey) {
21
- throw new Error("Public key is required");
38
+ try {
39
+ const client = (0, exports.getApiClient)();
40
+ const response = yield client.post('/verify', { publicKey });
41
+ return response.data;
22
42
  }
23
- const response = yield fetch(`${AIRXPAY_BASE_URL}/api/verify-public-key`, {
24
- method: "POST",
25
- headers: {
26
- "Content-Type": "application/json",
27
- },
28
- body: JSON.stringify({ publicKey }),
29
- });
30
- if (!response.ok) {
31
- throw new Error("Invalid public key or server error");
43
+ catch (error) {
44
+ throw new Error('Invalid public key');
32
45
  }
33
- return response.json();
34
46
  });
35
47
  exports.verifyPublicKey = verifyPublicKey;
48
+ /**
49
+ * Create new merchant
50
+ * Automatically stores returned token
51
+ */
52
+ const createMerchant = (payload) => __awaiter(void 0, void 0, void 0, function* () {
53
+ const client = (0, exports.getApiClient)();
54
+ const response = yield client.post('/create', payload);
55
+ if (response.data.token) {
56
+ yield (0, tokenStorage_1.setStoredToken)(response.data.token);
57
+ }
58
+ return response.data;
59
+ });
60
+ exports.createMerchant = createMerchant;
61
+ /**
62
+ * Get merchant status
63
+ */
64
+ const getMerchantStatus = () => __awaiter(void 0, void 0, void 0, function* () {
65
+ const client = (0, exports.getApiClient)();
66
+ const response = yield client.get('/status');
67
+ return response.data;
68
+ });
69
+ exports.getMerchantStatus = getMerchantStatus;
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
- import { Merchant, Mode, KYCStatus } from '../../types/merchantTypes';
2
+ import { Merchant, Mode, KycStatus } from '../../types/merchantTypes';
3
3
  interface KYCVerificationProps {
4
4
  initialData: Partial<Merchant>;
5
5
  mode: Mode;
6
- kycStatus: KYCStatus;
6
+ kycStatus: KycStatus;
7
7
  onNext: (data: Partial<Merchant>) => void;
8
8
  onBack: () => void;
9
9
  }
@@ -51,6 +51,73 @@ const react_native_1 = require("react-native");
51
51
  const react_native_paper_1 = require("react-native-paper");
52
52
  const expo_linear_gradient_1 = require("expo-linear-gradient");
53
53
  const FileUploader_1 = __importDefault(require("../common/FileUploader"));
54
+ // Text fields configuration
55
+ const TEXT_FIELDS = [
56
+ {
57
+ key: 'panNumber',
58
+ label: 'PAN Number',
59
+ required: true,
60
+ icon: 'card-account-details',
61
+ placeholder: 'ABCDE1234F',
62
+ validation: (value) => {
63
+ if (!(value === null || value === void 0 ? void 0 : value.trim()))
64
+ return 'PAN number is required';
65
+ const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/;
66
+ if (!panRegex.test(value.toUpperCase())) {
67
+ return 'Invalid PAN format (e.g., ABCDE1234F)';
68
+ }
69
+ return undefined;
70
+ },
71
+ },
72
+ {
73
+ key: 'aadhaarNumber',
74
+ label: 'Aadhaar Number',
75
+ required: true,
76
+ icon: 'card-bulleted',
77
+ placeholder: '1234 5678 9012',
78
+ keyboardType: 'numeric',
79
+ maxLength: 14,
80
+ validation: (value) => {
81
+ if (!(value === null || value === void 0 ? void 0 : value.trim()))
82
+ return 'Aadhaar number is required';
83
+ const cleaned = value.replace(/\s/g, '');
84
+ if (!/^\d{12}$/.test(cleaned)) {
85
+ return 'Aadhaar must be 12 digits';
86
+ }
87
+ return undefined;
88
+ },
89
+ },
90
+ {
91
+ key: 'gstNumber',
92
+ label: 'GST Number',
93
+ required: false,
94
+ icon: 'file-certificate',
95
+ placeholder: '22AAAAA0000A1Z5',
96
+ validation: (value) => {
97
+ if (!(value === null || value === void 0 ? void 0 : value.trim()))
98
+ return undefined;
99
+ const gstRegex = /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[Z]{1}[0-9A-Z]{1}$/;
100
+ if (!gstRegex.test(value.toUpperCase())) {
101
+ return 'Invalid GST format';
102
+ }
103
+ return undefined;
104
+ },
105
+ },
106
+ {
107
+ key: 'registeredBusinessName',
108
+ label: 'Registered Business Name',
109
+ required: false,
110
+ icon: 'store',
111
+ placeholder: 'Your registered business name',
112
+ validation: (value) => {
113
+ if (value && value.length < 3) {
114
+ return 'Business name must be at least 3 characters';
115
+ }
116
+ return undefined;
117
+ },
118
+ },
119
+ ];
120
+ // Document uploads configuration
54
121
  const REQUIRED_DOCUMENTS = [
55
122
  {
56
123
  key: 'panCardUrl',
@@ -58,7 +125,7 @@ const REQUIRED_DOCUMENTS = [
58
125
  required: true,
59
126
  icon: 'card-account-details',
60
127
  description: 'Clear image of PAN card',
61
- acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png']
128
+ acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png', 'application/pdf'],
62
129
  },
63
130
  {
64
131
  key: 'aadhaarUrl',
@@ -66,7 +133,7 @@ const REQUIRED_DOCUMENTS = [
66
133
  required: true,
67
134
  icon: 'card-bulleted',
68
135
  description: 'Both sides of Aadhaar',
69
- acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png']
136
+ acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png', 'application/pdf'],
70
137
  },
71
138
  {
72
139
  key: 'selfieUrl',
@@ -74,44 +141,83 @@ const REQUIRED_DOCUMENTS = [
74
141
  required: true,
75
142
  icon: 'face',
76
143
  description: 'Clear front-facing photo',
77
- acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png']
144
+ acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png'],
78
145
  },
79
146
  {
80
147
  key: 'addressProofUrl',
81
148
  label: 'Address Proof',
82
- required: false,
149
+ required: true,
83
150
  icon: 'home',
84
151
  description: 'Utility bill or rent agreement',
85
- acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png', 'application/pdf']
86
- },
87
- {
88
- key: 'businessRegistrationUrl',
89
- label: 'Business Registration',
90
- required: false,
91
- icon: 'file-document',
92
- description: 'GST, MSME, or company registration',
93
- acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png', 'application/pdf']
94
- },
95
- {
96
- key: 'gstCertificateUrl',
97
- label: 'GST Certificate',
98
- required: false,
99
- icon: 'file-certificate',
100
- description: 'If applicable',
101
- acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png', 'application/pdf']
152
+ acceptedTypes: ['image/jpeg', 'image/jpg', 'image/png', 'application/pdf'],
102
153
  },
103
154
  ];
104
155
  const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
105
- const [documents, setDocuments] = (0, react_1.useState)(initialData.kycDocuments || {});
156
+ // Initialize with new KYCDetails structure
157
+ const [kycDetails, setKycDetails] = (0, react_1.useState)(initialData.kycDetails || {});
106
158
  const [uploadingFor, setUploadingFor] = (0, react_1.useState)(null);
107
159
  const [isVerifying, setIsVerifying] = (0, react_1.useState)(false);
108
160
  const [verificationComplete, setVerificationComplete] = (0, react_1.useState)(false);
161
+ const [touched, setTouched] = (0, react_1.useState)({});
162
+ const [fieldErrors, setFieldErrors] = (0, react_1.useState)({});
109
163
  // Check if KYC is already verified
110
164
  (0, react_1.useEffect)(() => {
111
165
  if (kycStatus === 'verified') {
112
166
  setVerificationComplete(true);
113
167
  }
114
168
  }, [kycStatus]);
169
+ // Validate a specific field
170
+ const validateField = (key, value) => {
171
+ const textField = TEXT_FIELDS.find(f => f.key === key);
172
+ if ((textField === null || textField === void 0 ? void 0 : textField.validation) && value !== undefined) {
173
+ return textField.validation(value);
174
+ }
175
+ return undefined;
176
+ };
177
+ // Handle text input change
178
+ const handleTextChange = (key, value) => {
179
+ // Format Aadhaar number with spaces
180
+ if (key === 'aadhaarNumber') {
181
+ value = value.replace(/\D/g, '');
182
+ if (value.length > 12)
183
+ value = value.slice(0, 12);
184
+ // Add space after every 4 digits
185
+ const parts = value.match(/.{1,4}/g);
186
+ value = parts ? parts.join(' ') : value;
187
+ }
188
+ // Format PAN to uppercase
189
+ if (key === 'panNumber' || key === 'gstNumber') {
190
+ value = value.toUpperCase();
191
+ }
192
+ setKycDetails(prev => (Object.assign(Object.assign({}, prev), { [key]: value })));
193
+ // Validate on change
194
+ const error = validateField(key, value);
195
+ setFieldErrors(prev => {
196
+ const newErrors = Object.assign({}, prev);
197
+ if (error) {
198
+ newErrors[key] = error;
199
+ }
200
+ else {
201
+ delete newErrors[key];
202
+ }
203
+ return newErrors;
204
+ });
205
+ };
206
+ const handleBlur = (key) => {
207
+ setTouched(prev => (Object.assign(Object.assign({}, prev), { [key]: true })));
208
+ const value = kycDetails[key];
209
+ const error = validateField(key, value);
210
+ setFieldErrors(prev => {
211
+ const newErrors = Object.assign({}, prev);
212
+ if (error) {
213
+ newErrors[key] = error;
214
+ }
215
+ else {
216
+ delete newErrors[key];
217
+ }
218
+ return newErrors;
219
+ });
220
+ };
115
221
  const validateDocumentType = (file, documentKey) => {
116
222
  const document = REQUIRED_DOCUMENTS.find(doc => doc.key === documentKey);
117
223
  if (!document || !document.acceptedTypes)
@@ -131,7 +237,7 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
131
237
  setUploadingFor(documentKey);
132
238
  // Simulate upload delay
133
239
  setTimeout(() => {
134
- setDocuments(prev => (Object.assign(Object.assign({}, prev), { [documentKey]: file.uri || 'uploaded_file.jpg' })));
240
+ setKycDetails(prev => (Object.assign(Object.assign({}, prev), { [documentKey]: file.uri || 'uploaded_file.jpg' })));
135
241
  setUploadingFor(null);
136
242
  if (mode === 'test') {
137
243
  react_native_1.Alert.alert('Test Mode', 'Document would be auto-approved in test mode');
@@ -145,9 +251,9 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
145
251
  text: 'Remove',
146
252
  style: 'destructive',
147
253
  onPress: () => {
148
- const updated = Object.assign({}, documents);
254
+ const updated = Object.assign({}, kycDetails);
149
255
  delete updated[documentKey];
150
- setDocuments(updated);
256
+ setKycDetails(updated);
151
257
  },
152
258
  },
153
259
  ]);
@@ -176,15 +282,24 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
176
282
  </react_native_1.View>);
177
283
  }
178
284
  };
179
- const isRequiredDocumentsUploaded = () => {
180
- return REQUIRED_DOCUMENTS
285
+ const isRequiredFieldsFilled = () => {
286
+ // Check required text fields
287
+ const textFieldsValid = TEXT_FIELDS
288
+ .filter(field => field.required)
289
+ .every(field => {
290
+ const value = kycDetails[field.key];
291
+ return value && value.trim().length > 0;
292
+ });
293
+ // Check required documents
294
+ const documentsValid = REQUIRED_DOCUMENTS
181
295
  .filter(doc => doc.required)
182
- .every(doc => documents[doc.key]);
296
+ .every(doc => kycDetails[doc.key]);
297
+ return textFieldsValid && documentsValid && Object.keys(fieldErrors).length === 0;
183
298
  };
184
299
  const handleSubmit = () => {
185
- // Validate required documents
186
- if (!isRequiredDocumentsUploaded()) {
187
- react_native_1.Alert.alert('Error', 'Please upload all required documents');
300
+ // Validate all required fields
301
+ if (!isRequiredFieldsFilled()) {
302
+ react_native_1.Alert.alert('Error', 'Please fill all required fields and upload required documents');
188
303
  return;
189
304
  }
190
305
  // Show verifying state
@@ -194,14 +309,14 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
194
309
  setIsVerifying(false);
195
310
  if (mode === 'test') {
196
311
  onNext({
197
- kycDocuments: documents,
312
+ kycDetails: kycDetails,
198
313
  isKycCompleted: true,
199
314
  kycStatus: 'verified',
200
315
  });
201
316
  }
202
317
  else {
203
318
  onNext({
204
- kycDocuments: documents,
319
+ kycDetails: kycDetails,
205
320
  isKycCompleted: false,
206
321
  kycStatus: 'pending',
207
322
  });
@@ -211,9 +326,17 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
211
326
  const handleBack = () => {
212
327
  onBack();
213
328
  };
214
- const requiredDocsCount = REQUIRED_DOCUMENTS.filter(doc => doc.required).length;
215
- const uploadedRequiredCount = REQUIRED_DOCUMENTS.filter(doc => doc.required && documents[doc.key]).length;
216
- const progress = (uploadedRequiredCount / requiredDocsCount) * 100;
329
+ const requiredTextCount = TEXT_FIELDS.filter(f => f.required).length;
330
+ const requiredDocCount = REQUIRED_DOCUMENTS.filter(d => d.required).length;
331
+ const totalRequired = requiredTextCount + requiredDocCount;
332
+ const filledTextCount = TEXT_FIELDS
333
+ .filter(f => f.required && kycDetails[f.key])
334
+ .length;
335
+ const filledDocCount = REQUIRED_DOCUMENTS
336
+ .filter(d => d.required && kycDetails[d.key])
337
+ .length;
338
+ const filledTotal = filledTextCount + filledDocCount;
339
+ const progress = (filledTotal / totalRequired) * 100;
217
340
  // If already verified, show success state
218
341
  if (verificationComplete) {
219
342
  return (<react_native_1.View style={styles.container}>
@@ -250,7 +373,7 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
250
373
  <react_native_1.View style={styles.headerText}>
251
374
  <react_native_1.Text style={styles.title}>KYC Verification</react_native_1.Text>
252
375
  <react_native_1.Text style={styles.subtitle}>
253
- Upload your documents for verification
376
+ Enter your details and upload documents
254
377
  </react_native_1.Text>
255
378
  </react_native_1.View>
256
379
  </react_native_paper_1.Surface>
@@ -289,7 +412,7 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
289
412
  <expo_linear_gradient_1.LinearGradient colors={['#0066CC', '#0099FF']} style={[styles.progressFill, { width: `${progress}%` }]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }}/>
290
413
  </react_native_1.View>
291
414
  <react_native_1.Text style={styles.progressText}>
292
- {uploadedRequiredCount}/{requiredDocsCount} Required
415
+ {filledTotal}/{totalRequired} Completed
293
416
  </react_native_1.Text>
294
417
  </react_native_1.View>
295
418
 
@@ -300,19 +423,38 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
300
423
  <react_native_1.View style={styles.rejectionText}>
301
424
  <react_native_1.Text style={styles.rejectionTitle}>KYC Rejected</react_native_1.Text>
302
425
  <react_native_1.Text style={styles.rejectionMessage}>
303
- Please upload clear, valid documents
426
+ Please check and resubmit your details
304
427
  </react_native_1.Text>
305
428
  </react_native_1.View>
306
429
  </react_native_1.View>
307
430
  </react_native_paper_1.Surface>)}
308
431
 
309
- {/* Document Upload Sections */}
310
- <react_native_1.View style={styles.documentsContainer}>
432
+ {/* Text Fields Section */}
433
+ <react_native_1.View style={styles.sectionContainer}>
434
+ <react_native_1.Text style={styles.sectionTitle}>Personal Details</react_native_1.Text>
435
+ <react_native_1.Text style={styles.sectionSubtitle}>Enter your identification numbers</react_native_1.Text>
436
+
437
+ {TEXT_FIELDS.map((field, index) => (<react_native_1.View key={field.key} style={styles.fieldContainer}>
438
+ <react_native_1.Text style={styles.label}>
439
+ {field.label} {field.required && <react_native_1.Text style={styles.requiredStar}>*</react_native_1.Text>}
440
+ </react_native_1.Text>
441
+ <react_native_paper_1.TextInput mode="outlined" value={kycDetails[field.key] || ''} onChangeText={(text) => handleTextChange(field.key, text)} onBlur={() => handleBlur(field.key)} keyboardType={field.keyboardType || 'default'} error={!!fieldErrors[field.key]} style={styles.input} outlineColor="#E5E7EB" activeOutlineColor="#0066CC" left={<react_native_paper_1.TextInput.Icon icon={field.icon} color="#6B7280"/>} placeholder={field.placeholder} placeholderTextColor="#9CA3AF" maxLength={field.maxLength}/>
442
+ {fieldErrors[field.key] && (<react_native_paper_1.HelperText type="error" style={styles.errorText}>
443
+ {fieldErrors[field.key]}
444
+ </react_native_paper_1.HelperText>)}
445
+ </react_native_1.View>))}
446
+ </react_native_1.View>
447
+
448
+ {/* Document Upload Section */}
449
+ <react_native_1.View style={[styles.sectionContainer, styles.documentSection]}>
450
+ <react_native_1.Text style={styles.sectionTitle}>Document Uploads</react_native_1.Text>
451
+ <react_native_1.Text style={styles.sectionSubtitle}>Upload clear images of your documents</react_native_1.Text>
452
+
311
453
  {REQUIRED_DOCUMENTS.map((doc, index) => {
312
454
  var _a;
313
455
  return (<react_native_1.View key={doc.key} style={styles.documentItem}>
314
456
  {index > 0 && <react_native_1.View style={styles.documentDivider}/>}
315
- <FileUploader_1.default label={doc.label} required={doc.required} description={doc.description} icon={doc.icon} value={documents[doc.key]} onUpload={(file) => handleDocumentUpload(doc.key, file)} onRemove={() => handleDocumentRemove(doc.key)} uploading={uploadingFor === doc.key} mode={mode} accept={((_a = doc.acceptedTypes) === null || _a === void 0 ? void 0 : _a.join(',')) || '*'}/>
457
+ <FileUploader_1.default label={doc.label} required={doc.required} description={doc.description} icon={doc.icon} value={kycDetails[doc.key]} onUpload={(file) => handleDocumentUpload(doc.key, file)} onRemove={() => handleDocumentRemove(doc.key)} uploading={uploadingFor === doc.key} mode={mode} accept={((_a = doc.acceptedTypes) === null || _a === void 0 ? void 0 : _a.join(',')) || '*'}/>
316
458
  </react_native_1.View>);
317
459
  })}
318
460
  </react_native_1.View>
@@ -338,9 +480,9 @@ const KYCVerification = ({ initialData, mode, kycStatus, onNext, onBack, }) => {
338
480
 
339
481
  <react_native_1.TouchableOpacity style={[
340
482
  styles.submitButton,
341
- (!isRequiredDocumentsUploaded() || isVerifying) && styles.submitButtonDisabled
342
- ]} onPress={handleSubmit} disabled={!isRequiredDocumentsUploaded() || isVerifying}>
343
- <expo_linear_gradient_1.LinearGradient colors={isRequiredDocumentsUploaded() && !isVerifying ? ['#0066CC', '#0099FF'] : ['#9CA3AF', '#9CA3AF']} style={styles.submitGradient} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }}>
483
+ (!isRequiredFieldsFilled() || isVerifying) && styles.submitButtonDisabled
484
+ ]} onPress={handleSubmit} disabled={!isRequiredFieldsFilled() || isVerifying}>
485
+ <expo_linear_gradient_1.LinearGradient colors={isRequiredFieldsFilled() && !isVerifying ? ['#0066CC', '#0099FF'] : ['#9CA3AF', '#9CA3AF']} style={styles.submitGradient} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }}>
344
486
  {isVerifying ? (<react_native_1.View style={styles.verifyingContent}>
345
487
  <react_native_paper_1.ActivityIndicator size="small" color="#FFFFFF"/>
346
488
  <react_native_1.Text style={[styles.submitButtonText, styles.verifyingText]}>
@@ -559,6 +701,45 @@ const styles = react_native_1.StyleSheet.create({
559
701
  color: '#7F1D1D',
560
702
  marginTop: 1,
561
703
  },
704
+ sectionContainer: {
705
+ marginBottom: 20,
706
+ },
707
+ documentSection: {
708
+ marginTop: 8,
709
+ },
710
+ sectionTitle: {
711
+ fontSize: 16,
712
+ fontWeight: '600',
713
+ color: '#111827',
714
+ marginBottom: 4,
715
+ },
716
+ sectionSubtitle: {
717
+ fontSize: 12,
718
+ color: '#6B7280',
719
+ marginBottom: 12,
720
+ },
721
+ fieldContainer: {
722
+ marginBottom: 16,
723
+ },
724
+ label: {
725
+ fontSize: 13,
726
+ fontWeight: '500',
727
+ color: '#374151',
728
+ marginBottom: 6,
729
+ },
730
+ requiredStar: {
731
+ color: '#EF4444',
732
+ },
733
+ input: {
734
+ backgroundColor: '#FFFFFF',
735
+ fontSize: 14,
736
+ height: 48,
737
+ },
738
+ errorText: {
739
+ fontSize: 11,
740
+ color: '#EF4444',
741
+ marginTop: 2,
742
+ },
562
743
  documentsContainer: {
563
744
  marginBottom: 8,
564
745
  },