@djb25/digit-ui-module-ekyc 1.0.0 → 1.0.1

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,451 @@
1
+ import React, { useState, useRef, Fragment } from "react";
2
+ import { Header, Card, LabelFieldPair, CardLabel, TextInput, SubmitBar, CardHeader, RadioButtons, ActionBar, InfoBannerIcon, PropertyHouse, LocationIcon, RemoveableTag, HomeIcon, ConnectingCheckPoints, CheckPoint } from "@djb25/digit-ui-react-components";
3
+ import { useTranslation } from "react-i18next";
4
+ import { useHistory, useLocation } from "react-router-dom";
5
+
6
+ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
7
+ const { t } = useTranslation();
8
+ const history = useHistory();
9
+ const location = useLocation();
10
+
11
+ // Use parent state if provided, otherwise fallback to location state or defaults
12
+ const flowState = parentState || location.state || {
13
+ kNumber: "EKYC-1234567890",
14
+ selectedOption: { code: "SELF", name: "EKYC_SELF" },
15
+ connectionDetails: null
16
+ };
17
+
18
+ const [addressType, setAddressType] = useState({ code: "AADHAAR", name: "EKYC_AADHAAR_ADDRESS" });
19
+ const [correctAddress, setCorrectAddress] = useState({ code: "NO", name: "CORE_COMMON_NO" });
20
+ const [fullAddress, setFullAddress] = useState("");
21
+ const [flatNo, setFlatNo] = useState("");
22
+ const [building, setBuilding] = useState("");
23
+ const [landmark, setLandmark] = useState("");
24
+ const [pincode, setPincode] = useState("");
25
+ const [doorPhoto, setDoorPhoto] = useState(null);
26
+ const [isLocationFetching, setIsLocationFetching] = useState(false);
27
+ const fileInputRef = useRef(null);
28
+
29
+ const addressOptions = [
30
+ { code: "AADHAAR", name: "EKYC_AADHAAR_ADDRESS" },
31
+ { code: "OLD", name: "EKYC_OLD_ADDRESS" }
32
+ ];
33
+
34
+ const yesNoOptions = [
35
+ { code: "YES", name: "CORE_COMMON_YES" },
36
+ { code: "NO", name: "CORE_COMMON_NO" }
37
+ ];
38
+
39
+ const handleCompleteVerification = () => {
40
+ if (onComplete) {
41
+ onComplete({ addressType, fullAddress, flatNo, building, landmark, pincode, doorPhoto });
42
+ } else {
43
+ const { kNumber, selectedOption, connectionDetails } = flowState;
44
+ history.push("/digit-ui/employee/ekyc/property-info", {
45
+ kNumber,
46
+ selectedOption,
47
+ connectionDetails,
48
+ addressDetails: { addressType, fullAddress, flatNo, building, landmark, pincode, doorPhoto }
49
+ });
50
+ }
51
+ };
52
+
53
+ const handleCapture = (e) => {
54
+ const file = e.target.files[0];
55
+ if (file) {
56
+ const reader = new FileReader();
57
+ reader.onloadend = () => {
58
+ setDoorPhoto(reader.result);
59
+ };
60
+ reader.readAsDataURL(file);
61
+ }
62
+ };
63
+
64
+ const openGallery = () => {
65
+ fileInputRef.current.click();
66
+ };
67
+
68
+ const removePhoto = () => {
69
+ setDoorPhoto(null);
70
+ if (fileInputRef.current) fileInputRef.current.value = "";
71
+ };
72
+
73
+ const handleUseCurrentLocation = () => {
74
+ if (!("geolocation" in navigator)) {
75
+ alert(t("GEOLOCATION_NOT_SUPPORTED") || "Geolocation is not supported by your browser");
76
+ return;
77
+ }
78
+
79
+ setIsLocationFetching(true);
80
+ navigator.geolocation.getCurrentPosition(
81
+ async (position) => {
82
+ const { latitude, longitude } = position.coords;
83
+ try {
84
+ const response = await fetch(
85
+ `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&zoom=18&addressdetails=1`
86
+ );
87
+ if (!response.ok) throw new Error("Failed to fetch address");
88
+ const data = await response.json();
89
+
90
+ if (data && data.address) {
91
+ const addr = [
92
+ data.address?.amenity,
93
+ data.address?.road,
94
+ data.address?.neighbourhood,
95
+ data.address?.suburb,
96
+ data.address?.city,
97
+ data.address?.state,
98
+ data.address?.postcode
99
+ ].filter(Boolean).join(", ");
100
+
101
+ setFullAddress(addr || "");
102
+ setPincode(data.address?.postcode || "");
103
+ setLandmark(data.address?.suburb || data.address?.neighbourhood || "");
104
+ setFlatNo(data.address?.amenity || "");
105
+ }
106
+ } catch (error) {
107
+ console.error("Error reverse geocoding:", error);
108
+ } finally {
109
+ setIsLocationFetching(false);
110
+ }
111
+ },
112
+ (error) => {
113
+ console.error("Error getting location:", error);
114
+ setIsLocationFetching(false);
115
+ alert(t("LOCATION_FETCH_FAILED") || "Failed to fetch your current location. Please ensure location permissions are granted.");
116
+ },
117
+ { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
118
+ );
119
+ };
120
+
121
+ const FlagIcon = () => (
122
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
123
+ <path d="M14.4 6L13.6 4H5V21H7V14H12.6L13.4 16H20V6H14.4Z" fill="#00703C" />
124
+ </svg>
125
+ );
126
+
127
+ const IdCardIcon = () => (
128
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
129
+ <path d="M2 7V17C2 18.1 2.9 19 4 19H20C21.1 19 22 18.1 22 17V7C22 5.9 21.1 5 20 5H4C2.9 5 2 5.9 2 7ZM12 11H14V13H12V11ZM12 7H14V9H12V7ZM16 11H20V13H16V11ZM16 7H20V9H16V7ZM4 7H10V15H4V7ZM20 17H4V16H20V17Z" fill="#3D51B0" />
130
+ </svg>
131
+ );
132
+
133
+ const CameraIcon = () => (
134
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
135
+ <path d="M9 2L7.17 4H4C2.9 4 2 4.9 2 6V18C2 19.1 2.9 20 4 20H20C21.1 20 22 19.1 22 18V6C22 4.9 21.1 4 20 4H16.83L15 2H9ZM12 17C9.24 17 7 14.76 7 12C7 9.24 9.24 7 12 7C14.76 7 17 9.24 17 12C17 14.76 14.76 17 12 17ZM12 9C10.34 9 9 10.34 9 12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12C15 10.34 13.66 9 12 9Z" fill="#0068faff" />
136
+ </svg>
137
+ );
138
+
139
+ const TargetIcon = () => (
140
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
141
+ <path d="M12 8C9.79 8 8 9.79 8 12C8 14.21 9.79 16 12 16C14.21 16 16 14.21 16 12C16 9.79 14.21 8 12 8ZM20.94 11C20.48 6.83 17.17 3.52 13 3.06V1H11V3.06C6.83 3.52 3.52 6.83 3.06 11H1V13H3.06C3.52 17.17 6.83 20.48 11 20.94V23H13V20.94C17.17 20.48 20.48 17.17 20.94 13H23V11H20.94ZM12 19C8.13 19 5 15.87 5 12C5 8.13 8.13 5 12 5C15.87 5 19 8.13 19 12C19 15.87 15.87 19 12 19Z" fill="#0068faff" />
142
+ </svg>
143
+ );
144
+
145
+ const PincodeIcon = () => (
146
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
147
+ <path d="M13 13V11H15V13H13ZM13 9V7H15V9H13ZM17 13V11H19V13H17ZM17 9V7H19V9H17ZM11 13V11H9V13H11ZM11 9V7H9V9H11ZM7 13V11H5V13H7ZM7 9V7H5V9H7ZM21 3H3C1.9 3 1 3.9 1 5V19C1 20.1 1.9 21 3 21H21C22.1 21 23 20.1 23 19V5C23 3.9 22.1 3 21 3ZM21 19H3V5H21V19Z" fill="#0068faff" />
148
+ </svg>
149
+ );
150
+
151
+ const renderContent = () => (
152
+ <div style={{ animation: "fadeSlideIn 0.3s ease" }}>
153
+ {isSection && <hr style={{ margin: "40px 0", border: "0", borderTop: "2px solid #EAECF0" }} />}
154
+ <Header style={{ marginBottom: "24px" }}>{t("EKYC_ADDRESS_DETAILS_HEADER") || "Address Details"}</Header>
155
+ <div style={{ marginBottom: "32px" }}>
156
+ <RadioButtons
157
+ options={addressOptions}
158
+ optionsKey="name"
159
+ selectedOption={addressType}
160
+ onSelect={setAddressType}
161
+ t={t}
162
+ innerStyles={{ display: "flex", alignItems: "center" }}
163
+ style={{ display: "flex", gap: "50px", justifyContent: "flex-start" }}
164
+ />
165
+ </div>
166
+
167
+ {addressType.code === "AADHAAR" && (
168
+ <div style={{ backgroundColor: "#F9FAFB", padding: "16px", borderRadius: "12px", marginBottom: "24px", border: "1px solid #EAECF0", display: "flex", alignItems: "flex-start", gap: "12px", animation: "fadeSlideIn 0.3s ease" }}>
169
+ <div style={{ backgroundColor: "#E7F4EE", padding: "8px", borderRadius: "8px" }}>
170
+ <LocationIcon className="icon" styles={{ fill: "#00703C", width: "20px", height: "20px" }} />
171
+ </div>
172
+ <div style={{ fontSize: "16px", lineHeight: "1.6", color: "#344054", fontWeight: "500" }}>
173
+ H.No. 123, Sector 15, Rohini<br />
174
+ Delhi - 110085
175
+ </div>
176
+ </div>
177
+ )}
178
+
179
+ {addressType.code === "OLD" && (
180
+ <div style={{ animation: "fadeSlideIn 0.3s ease" }}>
181
+ <div style={{ marginBottom: "24px" }}>
182
+ <CardLabel style={{ marginBottom: "12px", fontWeight: "600", color: "#344054" }}>{t("EKYC_ADDRESS_CORRECTION_PROMPT") || "Do you want to correct the address?"}</CardLabel>
183
+ <RadioButtons
184
+ options={yesNoOptions}
185
+ optionsKey="name"
186
+ selectedOption={correctAddress}
187
+ onSelect={setCorrectAddress}
188
+ t={t}
189
+ innerStyles={{ display: "flex", alignItems: "center" }}
190
+ style={{ display: "flex", gap: "50px", justifyContent: "flex-start" }}
191
+ />
192
+ </div>
193
+
194
+ <div
195
+ style={{
196
+ border: "1px solid #D0D5DD",
197
+ borderRadius: "12px",
198
+ padding: "14px 16px",
199
+ display: "flex",
200
+ alignItems: "center",
201
+ justifyContent: "space-between",
202
+ marginBottom: "24px",
203
+ cursor: isLocationFetching ? "not-allowed" : "pointer",
204
+ backgroundColor: isLocationFetching ? "#F2F4F7" : "#FFFFFF",
205
+ transition: "all 0.2s ease",
206
+ opacity: isLocationFetching ? 0.7 : 1,
207
+ boxShadow: "0px 1px 2px rgba(16, 24, 40, 0.05)"
208
+ }}
209
+ onClick={!isLocationFetching ? handleUseCurrentLocation : undefined}
210
+ onMouseOver={(e) => !isLocationFetching ? (e.currentTarget.style.backgroundColor = "#F9FAFB") : null}
211
+ onMouseOut={(e) => !isLocationFetching ? (e.currentTarget.style.backgroundColor = "#FFFFFF") : null}
212
+ >
213
+ <div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
214
+ <div style={{ backgroundColor: "#EEF4FF", padding: "8px", borderRadius: "8px" }}>
215
+ {isLocationFetching ? (
216
+ <div className="location-loader" style={{ width: "20px", height: "20px", border: "2px solid #0068faff", borderTopColor: "transparent", borderRadius: "50%", animation: "spin 1s linear infinite" }}></div>
217
+ ) : (
218
+ <TargetIcon />
219
+ )}
220
+ </div>
221
+ <span style={{ fontWeight: "600", color: "#344054" }}>
222
+ {isLocationFetching ? (t("EKYC_FETCHING_LOCATION") || "Fetching Location...") : (t("EKYC_USE_CURRENT_LOCATION") || "Use Current Location")}
223
+ </span>
224
+ </div>
225
+ {!isLocationFetching && <span style={{ fontSize: "20px", color: "#98A2B3" }}>›</span>}
226
+ </div>
227
+
228
+ <LabelFieldPair>
229
+ <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_FULL_ADDRESS") || "Full Address"}</CardLabel>
230
+ <div className="field" style={{ position: "relative" }}>
231
+ <div style={{ position: "absolute", left: "12px", top: "16px", zIndex: 1, opacity: 0.6 }}>
232
+ <PropertyHouse styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
233
+ </div>
234
+ <TextInput
235
+ value={fullAddress}
236
+ onChange={(e) => setFullAddress(e.target.value)}
237
+ placeholder={t("EKYC_ENTER_FULL_ADDRESS") || "Enter Full Address"}
238
+ textInputStyle={{ paddingLeft: "40px", minHeight: "80px" }}
239
+ />
240
+ </div>
241
+ </LabelFieldPair>
242
+
243
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "24px" }}>
244
+ <LabelFieldPair>
245
+ <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_FLAT_HOUSE_NUMBER") || "Flat/House Number"}</CardLabel>
246
+ <div className="field" style={{ position: "relative" }}>
247
+ <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
248
+ <PropertyHouse styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
249
+ </div>
250
+ <TextInput
251
+ value={flatNo}
252
+ onChange={(e) => setFlatNo(e.target.value)}
253
+ placeholder={t("EKYC_ENTER_FLAT_NO") || "e.g. 45-B"}
254
+ textInputStyle={{ paddingLeft: "40px" }}
255
+ />
256
+ </div>
257
+ </LabelFieldPair>
258
+ <LabelFieldPair>
259
+ <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_BUILDING_TOWER") || "Building/Tower"}</CardLabel>
260
+ <div className="field" style={{ position: "relative" }}>
261
+ <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
262
+ <PropertyHouse styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
263
+ </div>
264
+ <TextInput
265
+ value={building}
266
+ onChange={(e) => setBuilding(e.target.value)}
267
+ placeholder={t("EKYC_ENTER_BUILDING") || "e.g. Tower 4"}
268
+ textInputStyle={{ paddingLeft: "40px" }}
269
+ />
270
+ </div>
271
+ </LabelFieldPair>
272
+ </div>
273
+
274
+ <LabelFieldPair>
275
+ <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_LANDMARK") || "Landmark"}</CardLabel>
276
+ <div className="field" style={{ position: "relative" }}>
277
+ <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
278
+ <LocationIcon className="icon" styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
279
+ </div>
280
+ <TextInput
281
+ value={landmark}
282
+ onChange={(e) => setLandmark(e.target.value)}
283
+ placeholder={t("EKYC_ENTER_LANDMARK") || "Near Central Park"}
284
+ textInputStyle={{ paddingLeft: "40px" }}
285
+ />
286
+ </div>
287
+ </LabelFieldPair>
288
+
289
+ <LabelFieldPair>
290
+ <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_PINCODE") || "Pincode"}</CardLabel>
291
+ <div className="field" style={{ position: "relative" }}>
292
+ <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
293
+ <PincodeIcon />
294
+ </div>
295
+ <TextInput
296
+ value={pincode}
297
+ onChange={(e) => setPincode(e.target.value)}
298
+ placeholder={t("EKYC_ENTER_PINCODE") || "6-digit pincode"}
299
+ textInputStyle={{ paddingLeft: "40px" }}
300
+ maxLength={6}
301
+ />
302
+ </div>
303
+ </LabelFieldPair>
304
+ </div>
305
+ )}
306
+
307
+ <hr style={{ margin: "32px 0", border: "0", borderTop: "1px solid #EAECF0" }} />
308
+
309
+ <div style={{ display: "flex", alignItems: "center", gap: "10px", marginBottom: "20px" }}>
310
+ <div style={{ backgroundColor: "#EEF4FF", padding: "8px", borderRadius: "8px" }}>
311
+ <PropertyHouse styles={{ fill: "#0068faff", width: "24px", height: "24px" }} />
312
+ </div>
313
+ <CardHeader style={{ margin: 0, fontSize: "20px" }}>{t("EKYC_ADMINISTRATIVE_DIVISION") || "Administrative Division"}</CardHeader>
314
+ </div>
315
+
316
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "24px", marginBottom: "32px" }}>
317
+ <div style={{ backgroundColor: "#F9FAFB", padding: "16px", borderRadius: "12px", display: "flex", alignItems: "center", gap: "16px", border: "1px solid #EAECF0" }}>
318
+ <div style={{ backgroundColor: "#E7F4EE", padding: "10px", borderRadius: "10px", display: "flex" }}>
319
+ <FlagIcon />
320
+ </div>
321
+ <div>
322
+ <div style={{ color: "#00703C", fontSize: "12px", fontWeight: "700", textTransform: "uppercase", letterSpacing: "0.5px" }}>{t("EKYC_ASSEMBLY") || "ASSEMBLY"}</div>
323
+ <div style={{ fontSize: "15px", fontWeight: "700", color: "#101828", marginTop: "2px" }}>AC-12 Chandni Chowk</div>
324
+ </div>
325
+ </div>
326
+ <div style={{ backgroundColor: "#F9FAFB", padding: "16px", borderRadius: "12px", display: "flex", alignItems: "center", gap: "16px", border: "1px solid #EAECF0" }}>
327
+ <div style={{ backgroundColor: "#EEF4FF", padding: "10px", borderRadius: "10px", display: "flex" }}>
328
+ <IdCardIcon />
329
+ </div>
330
+ <div>
331
+ <div style={{ color: "#0068faff", fontSize: "12px", fontWeight: "700", textTransform: "uppercase", letterSpacing: "0.5px" }}>{t("EKYC_WARD") || "WARD"}</div>
332
+ <div style={{ fontSize: "15px", fontWeight: "700", color: "#101828", marginTop: "2px" }}>WARD-45 Civil Lines</div>
333
+ </div>
334
+ </div>
335
+ </div>
336
+
337
+ <CardHeader style={{ fontSize: "18px", color: "#101828", marginBottom: "4px" }}>{t("EKYC_DOOR_PHOTO_HEADER") || "Door Photo with GPS Stamp"}</CardHeader>
338
+ <div style={{ color: "#667085", fontSize: "14px", marginBottom: "16px" }}>{t("EKYC_REQUIRED_FOR_VERIFICATION") || "Required for verification"}</div>
339
+
340
+ <div style={{ backgroundColor: "#FFFAEB", padding: "14px", borderRadius: "12px", display: "flex", alignItems: "center", gap: "12px", marginBottom: "20px", border: "1px solid #FEDF89" }}>
341
+ <InfoBannerIcon fill="#B54708" />
342
+ <div>
343
+ <div style={{ fontWeight: "700", color: "#B54708", fontSize: "14px" }}>{t("EKYC_IMPORTANT") || "Important"}</div>
344
+ <div style={{ fontSize: "13px", color: "#B54708", marginTop: "2px" }}>{t("EKYC_CAPTURE_LIVE_CAMERA") || "Capture with live camera for GPS metadata"}</div>
345
+ </div>
346
+ </div>
347
+
348
+ <div
349
+ style={{
350
+ border: "2px dashed #D0D5DD",
351
+ borderRadius: "16px",
352
+ padding: doorPhoto ? "12px" : "40px 24px",
353
+ textAlign: "center",
354
+ cursor: "pointer",
355
+ position: "relative",
356
+ overflow: "hidden",
357
+ minHeight: "180px",
358
+ display: "flex",
359
+ flexDirection: "column",
360
+ alignItems: "center",
361
+ justifyContent: "center",
362
+ backgroundColor: "#F9FAFB",
363
+ transition: "all 0.2s ease",
364
+ boxShadow: "inset 0px 2px 4px rgba(0, 0, 0, 0.02)"
365
+ }}
366
+ onClick={!doorPhoto ? openGallery : undefined}
367
+ onMouseOver={(e) => !doorPhoto ? e.currentTarget.style.borderColor = "#0068faff" : null}
368
+ onMouseOut={(e) => !doorPhoto ? e.currentTarget.style.borderColor = "#D0D5DD" : null}
369
+ >
370
+ <input
371
+ type="file"
372
+ ref={fileInputRef}
373
+ onChange={handleCapture}
374
+ accept="image/*"
375
+ style={{ display: "none" }}
376
+ />
377
+ {!doorPhoto ? (
378
+ <>
379
+ <div style={{ backgroundColor: "#FFFFFF", width: "64px", height: "64px", borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 16px", boxShadow: "0px 1px 3px rgba(16, 24, 40, 0.1)" }}>
380
+ <CameraIcon />
381
+ </div>
382
+ <div style={{ fontWeight: "700", fontSize: "16px", marginBottom: "4px", color: "#101828" }}>{t("EKYC_TAP_TO_CAPTURE") || "Tap to Capture"}</div>
383
+ <div style={{ color: "#667085", fontSize: "14px" }}>{t("EKYC_CAPTURE_DOOR_IMAGE") || "Capture Door Image"}</div>
384
+ </>
385
+ ) : (
386
+ <div style={{ position: "relative", width: "100%", height: "100%", display: "flex", justifyContent: "center" }}>
387
+ <img src={doorPhoto} alt="Door" style={{ width: "100%", maxHeight: "300px", objectFit: "cover", borderRadius: "12px", display: "block" }} />
388
+ <div style={{ position: "absolute", top: "12px", right: "12px" }}>
389
+ <button
390
+ onClick={(e) => { e.stopPropagation(); removePhoto(); }}
391
+ style={{ background: "#FFFFFF", border: "1px solid #EAECF0", borderRadius: "8px", padding: "8px", display: "flex", boxShadow: "0px 1px 2px rgba(16, 24, 40, 0.05)", cursor: "pointer" }}
392
+ >
393
+ <RemoveableTag text="" onClick={() => { }} extraStyles={{ tagStyles: { margin: 0, padding: 0 } }} />
394
+ </button>
395
+ </div>
396
+ </div>
397
+ )}
398
+ </div>
399
+
400
+ <ActionBar>
401
+ <SubmitBar label={isSection ? t("EKYC_COMPLETE_VERIFICATION_AND_PROCEED") || "Complete & Proceed" : t("EKYC_COMPLETE_VERIFICATION") || "Complete Verification"} onSubmit={handleCompleteVerification} />
402
+ </ActionBar>
403
+ </div>
404
+ );
405
+
406
+ if (isSection) return renderContent();
407
+
408
+ return (
409
+ <div className="inbox-container">
410
+ <style>{`
411
+ @keyframes spin { to { transform: rotate(360deg); } }
412
+ @keyframes fadeSlideIn { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
413
+ `}</style>
414
+
415
+ <div className="filters-container">
416
+ {/* Sidebar Title Card */}
417
+ <Card className="sidebar-title-card" style={{ display: "flex", alignItems: "center", padding: "16px", marginBottom: "16px", borderRadius: "4px" }}>
418
+ <div className="icon-container" style={{ color: "#0068faff", marginRight: "12px" }}>
419
+ <HomeIcon style={{ width: "24px", height: "24px" }} />
420
+ </div>
421
+ <div style={{ fontWeight: "700", fontSize: "18px", color: "#0B0C0C" }}>
422
+ {t("EKYC_PROCESS")}
423
+ </div>
424
+ </Card>
425
+
426
+ {/* Progress Steps Sidebar */}
427
+ <div style={{ padding: "8px 16px" }}>
428
+ <ConnectingCheckPoints>
429
+ <CheckPoint label={t("EKYC_STEP_AADHAAR") || "Aadhaar"} isCompleted={true} />
430
+ <CheckPoint label={t("EKYC_STEP_ADDRESS") || "Address"} isCompleted={true} />
431
+ <CheckPoint label={t("EKYC_STEP_REVIEW") || "Review"} />
432
+ </ConnectingCheckPoints>
433
+ </div>
434
+ </div>
435
+
436
+ <div style={{ flex: 1, marginLeft: "16px" }}>
437
+ <Card>
438
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "24px" }}>
439
+ <Header>{t("EKYC_ADDRESS_DETAILS_HEADER") || "Address Details"}</Header>
440
+ <div style={{ fontSize: "14px", fontWeight: "700", color: "#505A5F" }}>
441
+ {t("EKYC_K_NUMBER")}: <span style={{ color: "#0B0C0C" }}>{flowState?.kNumber}</span>
442
+ </div>
443
+ </div>
444
+ {renderContent()}
445
+ </Card>
446
+ </div>
447
+ </div>
448
+ );
449
+ };
450
+
451
+ export default AddressDetails;
@@ -0,0 +1,94 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Card, HomeIcon, Toast } from "@djb25/digit-ui-react-components";
3
+ import { useTranslation } from "react-i18next";
4
+ import SearchConsumer from "../../components/SearchConsumer";
5
+ import ConnectionDetailsView from "../../components/ConnectionDetailsView";
6
+
7
+ const Create = () => {
8
+ const { t } = useTranslation();
9
+ const [searchParams, setSearchParams] = useState(() => {
10
+ const saved = sessionStorage.getItem("EKYC_CREATE_SEARCH_PARAMS");
11
+ return saved ? JSON.parse(saved) : { kNumber: "", kName: "" };
12
+ });
13
+ const [searchPerformed, setSearchPerformed] = useState(() => {
14
+ return sessionStorage.getItem("EKYC_CREATE_SEARCH_PERFORMED") === "true";
15
+ });
16
+ const [showToast, setShowToast] = useState(null);
17
+
18
+ const tenantId = Digit.ULBService.getCurrentTenantId();
19
+
20
+ const { isLoading: isSearching, data: connectionDetails, error, revalidate } = Digit.Hooks.ekyc.useGetConnection({
21
+ tenantId,
22
+ details: { kno: searchParams.kNumber }
23
+ }, {
24
+ enabled: searchPerformed && !!searchParams.kNumber
25
+ });
26
+
27
+ const { mutate: validateUser, isLoading: isValidating } = Digit.Hooks.ekyc.useValidateUser(tenantId, {
28
+ onSuccess: (data) => {
29
+ if (data?.responseInfo?.status === "successful") {
30
+ // Keep the flow: first validate, then search details is implied by the new UI
31
+ } else {
32
+ setShowToast({ error: true, label: data?.message || t("EKYC_VALIDATION_FAILED") });
33
+ setSearchPerformed(false);
34
+ sessionStorage.removeItem("EKYC_CREATE_SEARCH_PERFORMED");
35
+ }
36
+ },
37
+ onError: (error) => {
38
+ setShowToast({ error: true, label: error?.response?.data?.Errors?.[0]?.message || t("EKYC_VALIDATION_ERROR_PLEASE_ENTER_THE_CORRECT_CREDENTIALS") });
39
+ setSearchPerformed(false);
40
+ sessionStorage.removeItem("EKYC_CREATE_SEARCH_PERFORMED");
41
+ }
42
+ });
43
+
44
+ const handleSearch = (params) => {
45
+ if (!params.kNumber && !params.kName) {
46
+ // This is the "Clear" case
47
+ setSearchParams({ kNumber: "", kName: "" });
48
+ setSearchPerformed(false);
49
+ sessionStorage.removeItem("EKYC_CREATE_SEARCH_PARAMS");
50
+ sessionStorage.removeItem("EKYC_CREATE_SEARCH_PERFORMED");
51
+ return;
52
+ }
53
+
54
+ if (!params.kNumber || !params.kName) {
55
+ setShowToast({ error: true, label: t("EKYC_FILL_ALL_FIELDS") });
56
+ return;
57
+ }
58
+ setSearchParams(params);
59
+ setSearchPerformed(true);
60
+ sessionStorage.setItem("EKYC_CREATE_SEARCH_PARAMS", JSON.stringify(params));
61
+ sessionStorage.setItem("EKYC_CREATE_SEARCH_PERFORMED", "true");
62
+ // We validate first as per original logic, then the hook useGetConnection will fetch details
63
+ validateUser({ kno: params.kNumber, name: params.kName });
64
+ };
65
+
66
+ const closeToast = () => {
67
+ setShowToast(null);
68
+ };
69
+
70
+ return (
71
+ <SearchConsumer
72
+ onSearch={handleSearch}
73
+ searchParams={searchParams}
74
+ >
75
+ {searchPerformed && (
76
+ <ConnectionDetailsView
77
+ kNumber={searchParams.kNumber}
78
+ kName={searchParams.kName}
79
+ connectionDetails={connectionDetails}
80
+ isLoading={isSearching || isValidating}
81
+ />
82
+ )}
83
+
84
+ {!searchPerformed && !isSearching && (
85
+ <Card style={{ textAlign: "center", padding: "40px" }}>
86
+ <div style={{ color: "#667085" }}>{t("EKYC_SEARCH_TO_VIEW_DETAILS")}</div>
87
+ </Card>
88
+ )}
89
+ {showToast && <Toast error={showToast.error} label={showToast.label} onClose={closeToast} isDsc={true} />}
90
+ </SearchConsumer>
91
+ );
92
+ };
93
+
94
+ export default Create;
@@ -1,6 +1,8 @@
1
1
  import React, { useCallback, useEffect, useState, useMemo } from "react";
2
2
  import { useTranslation } from "react-i18next";
3
3
  import DesktopInbox from "../../components/DesktopInbox";
4
+ import MobileInbox from "../../components/MobileInbox";
5
+ import Filter from "../../components/Filter";
4
6
 
5
7
  const MOCK_DATA_ITEMS = [
6
8
  { applicationNumber: "EKYC-2024-001", citizenName: "Rahul Sharma", mobileNumber: "9876543210", status: "COMPLETED" },
@@ -82,7 +84,7 @@ const Inbox = ({
82
84
  name: "status",
83
85
  type: "dropdown",
84
86
  options: [
85
- { label: t("EKYC_STATUS_ALL"), value: "" },
87
+ { label: t("CHOOSE_STATUS"), value: "" },
86
88
  { label: t("EKYC_STATUS_COMPLETED"), value: "COMPLETED" },
87
89
  { label: t("EKYC_STATUS_PENDING"), value: "PENDING" },
88
90
  { label: t("EKYC_STATUS_REJECTED"), value: "REJECTED" },
@@ -92,25 +94,40 @@ const Inbox = ({
92
94
  ], [t]);
93
95
 
94
96
  return (
95
- <div className="inbox-main-container">
96
- <DesktopInbox
97
- businessService={businessService}
98
- data={{ items: filteredStaticData, totalCount: filteredStaticData.length }}
99
- isLoading={false}
100
- searchFields={searchFields}
101
- onSearch={handleSearch}
102
- onSort={handleSort}
103
- onNextPage={fetchNextPage}
104
- onPrevPage={fetchPrevPage}
105
- currentPage={Math.floor(pageOffset / pageSize)}
106
- pageSizeLimit={pageSize}
107
- onPageSizeChange={handlePageSizeChange}
108
- parentRoute={parentRoute}
109
- searchParams={searchParams}
110
- sortParams={sortParams}
111
- totalRecords={filteredStaticData.length}
112
- countData={staticCountData}
113
- />
97
+ <div className="ekyc-employee-container">
98
+ <div className="inbox-main-container">
99
+ {Digit.Utils.browser.isMobile() ? (
100
+ <MobileInbox
101
+ data={{ items: filteredStaticData, totalCount: filteredStaticData.length }}
102
+ isLoading={false}
103
+ onSearch={handleSearch}
104
+ searchFields={searchFields}
105
+ searchParams={searchParams}
106
+ parentRoute={parentRoute}
107
+ countData={staticCountData}
108
+ />
109
+ ) : (
110
+ <DesktopInbox
111
+ businessService={businessService}
112
+ data={{ items: filteredStaticData, totalCount: filteredStaticData.length }}
113
+ isLoading={false}
114
+ searchFields={searchFields}
115
+ onSearch={handleSearch}
116
+ onSort={handleSort}
117
+ onNextPage={fetchNextPage}
118
+ onPrevPage={fetchPrevPage}
119
+ currentPage={Math.floor(pageOffset / pageSize)}
120
+ pageSizeLimit={pageSize}
121
+ onPageSizeChange={handlePageSizeChange}
122
+ parentRoute={parentRoute}
123
+ searchParams={searchParams}
124
+ sortParams={sortParams}
125
+ totalRecords={filteredStaticData.length}
126
+ countData={staticCountData}
127
+ filterComponent="EKYC_INBOX_FILTER"
128
+ />
129
+ )}
130
+ </div>
114
131
  </div>
115
132
  );
116
133
  };