@djb25/digit-ui-module-ekyc 1.0.5 → 1.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djb25/digit-ui-module-ekyc",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Digit UI Module for Ekyc",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.modern.js",
@@ -54,7 +54,7 @@ const DesktopInbox = ({ tableConfig, filterComponent, ...props }) => {
54
54
  accessor: "status",
55
55
  Cell: ({ row }) => {
56
56
  const status = row.original?.status || "DEFAULT";
57
- return <span className={`ekyc-status-tag ${status}`}>{t(`EKYC_STATUS_${status}`)}</span>;
57
+ return <span className={`ekyc-status-tag ${status}`}>{t(`${status}`)}</span>;
58
58
  },
59
59
  },
60
60
  ],
@@ -66,7 +66,7 @@ const DesktopInbox = ({ tableConfig, filterComponent, ...props }) => {
66
66
  }, [data]);
67
67
 
68
68
  return (
69
- <div className="inbox-container">
69
+ <div className="inbox-container" style={{ paddingBottom: "16px" }}>
70
70
  <div className="filters-container">
71
71
  {/* Sidebar Title Card */}
72
72
  <Card
@@ -25,7 +25,9 @@ const Filter = ({ searchParams, onFilterChange, defaultSearchParams, statusMap,
25
25
  };
26
26
 
27
27
  const onStatusChange = (value) => {
28
- localParamChange({ status: value });
28
+ const newParams = { ..._searchParams, status: value };
29
+ setSearchParams(newParams);
30
+ onFilterChange(newParams);
29
31
  };
30
32
 
31
33
  return (
@@ -43,9 +45,8 @@ const Filter = ({ searchParams, onFilterChange, defaultSearchParams, statusMap,
43
45
  <Dropdown
44
46
  option={[
45
47
  { label: t("EKYC_STATUS_ALL"), value: "" },
46
- { label: t("EKYC_STATUS_COMPLETED"), value: "COMPLETED" },
47
- { label: t("EKYC_STATUS_PENDING"), value: "PENDING" },
48
- { label: t("EKYC_STATUS_REJECTED"), value: "REJECTED" },
48
+ { label: t("EKYC_STATUS_ACTIVE"), value: "ACTIVE" },
49
+ { label: t("EKYC_STATUS_PENDING"), value: "PENDING START" },
49
50
  ]}
50
51
  optionKey="label"
51
52
  select={onStatusChange}
@@ -165,6 +165,9 @@ const AadhaarVerification = () => {
165
165
  const [aadhaarLastFour, setAadhaarLastFour] = useState("");
166
166
  const [isAadhaarVerified, setIsAadhaarVerified] = useState(false);
167
167
  const [isVerifying, setIsVerifying] = useState(false);
168
+ const [showOtpField, setShowOtpField] = useState(false);
169
+ const [otp, setOtp] = useState("");
170
+ const [otpError, setOtpError] = useState(false);
168
171
  const [nameCorrect, setNameCorrect] = useState({ code: "NO", name: "CORE_COMMON_NO" });
169
172
  const [userName, setUserName] = useState(details.consumerName || "");
170
173
 
@@ -186,17 +189,27 @@ const AadhaarVerification = () => {
186
189
 
187
190
  // ── Handlers ──
188
191
  const handleVerifyAadhaar = () => {
189
- if (aadhaarLastFour.length !== 4 || isVerifying) return;
192
+ if (aadhaarLastFour.length !== 12 || isVerifying) return;
190
193
  setIsVerifying(true);
191
194
  setTimeout(() => {
192
195
  setIsVerifying(false);
196
+ setShowOtpField(true);
197
+ }, 1200);
198
+ };
199
+
200
+ const handleVerifyOtp = () => {
201
+ if (otp === "123456") {
193
202
  setIsAadhaarVerified(true);
203
+ setShowOtpField(false);
204
+ setOtpError(false);
194
205
  // Auto-expand address section upon verification
195
206
  setShowAddressSection(true);
196
207
  setTimeout(() => {
197
208
  addressSectionRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
198
209
  }, 100);
199
- }, 1200);
210
+ } else {
211
+ setOtpError(true);
212
+ }
200
213
  };
201
214
 
202
215
  const handleSaveAndContinue = () => {
@@ -371,17 +384,53 @@ const AadhaarVerification = () => {
371
384
  </div>
372
385
  </LabelFieldPair>
373
386
 
374
- {!isAadhaarVerified && (
387
+ {!isAadhaarVerified && !showOtpField && (
375
388
  <SubmitBar
376
389
  label={isVerifying
377
390
  ? t("EKYC_VERIFYING") || "Verifying..."
378
391
  : t("EKYC_VERIFY_AADHAAR_BTN") || "Verify Aadhaar"}
379
392
  onSubmit={handleVerifyAadhaar}
380
- disabled={aadhaarLastFour.length !== 4 || isVerifying}
393
+ disabled={aadhaarLastFour.length !== 12 || isVerifying}
381
394
  style={{ marginTop: "12px" }}
382
395
  />
383
396
  )}
384
397
 
398
+ {!isAadhaarVerified && showOtpField && (
399
+ <Fragment>
400
+ <div className="ekyc-field-label" style={{ marginTop: "16px" }}>
401
+ {t("EKYC_ENTER_OTP") || "Enter OTP"}
402
+ </div>
403
+ <LabelFieldPair>
404
+ <div className="field">
405
+ <IconInput
406
+ icon={<LockIcon size={15} />}
407
+ value={otp}
408
+ onChange={(e) => {
409
+ const val = e.target.value;
410
+ if (/^\d*$/.test(val)) {
411
+ setOtp(val);
412
+ if (otpError) setOtpError(false);
413
+ }
414
+ }}
415
+ placeholder={t("EKYC_ENTER_OTP_PLACEHOLDER") || "Enter 123456"}
416
+ maxLength={6}
417
+ />
418
+ </div>
419
+ </LabelFieldPair>
420
+ {otpError && (
421
+ <div style={{ color: "#D4351C", fontSize: "12px", marginTop: "4px" }}>
422
+ {t("EKYC_INVALID_OTP") || "Invalid OTP. Please enter 123456."}
423
+ </div>
424
+ )}
425
+ <SubmitBar
426
+ label={t("EKYC_VERIFY_OTP_BTN") || "Verify OTP"}
427
+ onSubmit={handleVerifyOtp}
428
+ disabled={otp.length !== 6}
429
+ style={{ marginTop: "12px" }}
430
+ />
431
+ </Fragment>
432
+ )}
433
+
385
434
  {isAadhaarVerified && (
386
435
  <div style={styles.verifiedCard}>
387
436
  <div style={{ display: "flex", alignItems: "center", gap: "8px", marginBottom: "14px" }}>
@@ -404,7 +453,7 @@ const AadhaarVerification = () => {
404
453
  </div>
405
454
  <div>
406
455
  <div style={styles.infoLabel}>{t("EKYC_AADHAAR") || "Aadhaar"}</div>
407
- <div style={styles.infoValue}>XXXX XXXX {aadhaarLastFour}</div>
456
+ <div style={styles.infoValue}>{aadhaarLastFour}</div>
408
457
  </div>
409
458
  <div style={{ gridColumn: "span 2" }}>
410
459
  <div style={styles.infoLabel}>{t("EKYC_ADDRESS") || "Address"}</div>
@@ -278,7 +278,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
278
278
 
279
279
  const handleUseCurrentLocation = () => {
280
280
  if (!("geolocation" in navigator)) {
281
- alert(t("GEOLOCATION_NOT_SUPPORTED") || "Geolocation is not supported by your browser");
281
+ alert(t("GEOLOCATION_NOT_SUPPORTED"));
282
282
  return;
283
283
  }
284
284
  setIsLocationFetching(true);
@@ -306,7 +306,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
306
306
  (err) => {
307
307
  console.error("Geolocation error:", err);
308
308
  setIsLocationFetching(false);
309
- alert(t("LOCATION_FETCH_FAILED") || "Failed to fetch location. Please grant location permissions.");
309
+ alert(t("LOCATION_FETCH_FAILED"));
310
310
  },
311
311
  { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
312
312
  );
@@ -318,7 +318,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
318
318
  {/* ── Address Type Toggle ── */}
319
319
  <SectionHead
320
320
  icon={<LocationIcon className="icon" styles={{ fill: "#0B0C0C", width: "16px", height: "16px" }} />}
321
- label={t("EKYC_ADDRESS_DETAILS_HEADER") || "Address Details"}
321
+ label={t("EKYC_ADDRESS_DETAILS_HEADER")}
322
322
  />
323
323
 
324
324
  <div style={{ marginBottom: "20px" }}>
@@ -350,7 +350,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
350
350
  <LocationIcon className="icon" styles={{ fill: "#085041", width: "16px", height: "16px" }} />
351
351
  </div>
352
352
  <div style={{ fontSize: "14px", lineHeight: "1.6", color: "#04342C", fontWeight: "500" }}>
353
- {addrDetails.fullAddress || "H.No. 123, Sector 15, Rohini, Delhi – 110085"}
353
+ {addrDetails.fullAddress}
354
354
  </div>
355
355
  </div>
356
356
  )}
@@ -362,7 +362,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
362
362
  {/* Correction toggle */}
363
363
  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "14px" }}>
364
364
  <CardLabel style={{ fontWeight: "500", marginBottom: 0, fontSize: "13px", color: "#505A5F" }}>
365
- {t("EKYC_ADDRESS_CORRECTION_PROMPT") || "Correct the address?"}
365
+ {t("EKYC_ADDRESS_CORRECTION_PROMPT")}
366
366
  </CardLabel>
367
367
  <RadioButtons
368
368
  options={yesNoOptions}
@@ -408,8 +408,8 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
408
408
  </div>
409
409
  <span style={{ fontWeight: "500", fontSize: "14px", color: "#344054" }}>
410
410
  {isLocationFetching
411
- ? t("EKYC_FETCHING_LOCATION") || "Fetching location..."
412
- : t("EKYC_USE_CURRENT_LOCATION") || "Use current location"}
411
+ ? t("EKYC_FETCHING_LOCATION")
412
+ : t("EKYC_USE_CURRENT_LOCATION")}
413
413
  </span>
414
414
  </div>
415
415
  {!isLocationFetching && (
@@ -420,14 +420,14 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
420
420
  {/* Full Address (textarea-style) */}
421
421
  <div style={{ marginBottom: "14px" }}>
422
422
  <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
423
- {t("EKYC_FULL_ADDRESS") || "Full address"}
423
+ {t("EKYC_FULL_ADDRESS")}
424
424
  </div>
425
425
  <IconInput
426
426
  icon={<PropertyHouse styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
427
427
  topAligned
428
428
  value={fullAddress}
429
429
  onChange={(e) => setFullAddress(e.target.value)}
430
- placeholder={t("EKYC_ENTER_FULL_ADDRESS") || "Enter full address"}
430
+ placeholder={t("EKYC_ENTER_FULL_ADDRESS")}
431
431
  inputStyle={{ minHeight: "72px" }}
432
432
  />
433
433
  </div>
@@ -436,24 +436,24 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
436
436
  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "14px" }}>
437
437
  <div>
438
438
  <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
439
- {t("EKYC_FLAT_HOUSE_NUMBER") || "Flat / House no."}
439
+ {t("EKYC_FLAT_HOUSE_NUMBER")}
440
440
  </div>
441
441
  <IconInput
442
442
  icon={<PropertyHouse styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
443
443
  value={flatNo}
444
444
  onChange={(e) => setFlatNo(e.target.value)}
445
- placeholder={t("EKYC_ENTER_FLAT_NO") || "e.g. 45-B"}
445
+ placeholder={t("EKYC_ENTER_FLAT_NO")}
446
446
  />
447
447
  </div>
448
448
  <div>
449
449
  <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
450
- {t("EKYC_BUILDING_TOWER") || "Building / Tower"}
450
+ {t("EKYC_BUILDING_TOWER")}
451
451
  </div>
452
452
  <IconInput
453
453
  icon={<PropertyHouse styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
454
454
  value={building}
455
455
  onChange={(e) => setBuilding(e.target.value)}
456
- placeholder={t("EKYC_ENTER_BUILDING") || "e.g. Tower 4"}
456
+ placeholder={t("EKYC_ENTER_BUILDING")}
457
457
  />
458
458
  </div>
459
459
  </div>
@@ -462,24 +462,24 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
462
462
  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "4px" }}>
463
463
  <div>
464
464
  <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
465
- {t("EKYC_LANDMARK") || "Landmark"}
465
+ {t("EKYC_LANDMARK")}
466
466
  </div>
467
467
  <IconInput
468
468
  icon={<LocationIcon className="icon" styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
469
469
  value={landmark}
470
470
  onChange={(e) => setLandmark(e.target.value)}
471
- placeholder={t("EKYC_ENTER_LANDMARK") || "Near Central Park"}
471
+ placeholder={t("EKYC_ENTER_LANDMARK")}
472
472
  />
473
473
  </div>
474
474
  <div>
475
475
  <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
476
- {t("EKYC_PINCODE") || "Pincode"}
476
+ {t("EKYC_PINCODE")}
477
477
  </div>
478
478
  <IconInput
479
479
  icon={<PincodeIcon size={15} />}
480
480
  value={pincode}
481
481
  onChange={(e) => { if (/^\d*$/.test(e.target.value)) setPincode(e.target.value); }}
482
- placeholder={t("EKYC_ENTER_PINCODE") || "6-digit pincode"}
482
+ placeholder={t("EKYC_ENTER_PINCODE")}
483
483
  maxLength={6}
484
484
  />
485
485
  </div>
@@ -492,14 +492,14 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
492
492
  {/* ── Administrative Division ── */}
493
493
  <SectionHead
494
494
  icon={<PropertyHouse styles={{ fill: "#0B0C0C", width: "16px", height: "16px" }} />}
495
- label={t("EKYC_ADMINISTRATIVE_DIVISION") || "Administrative Division"}
495
+ label={t("EKYC_ADMINISTRATIVE_DIVISION")}
496
496
  />
497
497
 
498
498
  {isMdmsLoading ? <Loader /> : (
499
499
  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "24px" }}>
500
500
  <div>
501
501
  <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
502
- {t("EKYC_ASSEMBLY") || "Assembly Constituency"}
502
+ {t("EKYC_ASSEMBLY")}
503
503
  </div>
504
504
  <Dropdown
505
505
  option={assemblies}
@@ -530,11 +530,11 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
530
530
  {/* ── Door Photo ── */}
531
531
  <SectionHead
532
532
  icon={<CameraIcon size={16} />}
533
- label={t("EKYC_DOOR_PHOTO_HEADER") || "Door photo with GPS stamp"}
533
+ label={t("EKYC_DOOR_PHOTO_HEADER")}
534
534
  />
535
535
 
536
536
  <div style={{ fontSize: "12px", color: "#667085", marginBottom: "12px" }}>
537
- {t("EKYC_REQUIRED_FOR_VERIFICATION") || "Required for verification"}
537
+ {t("EKYC_REQUIRED_FOR_VERIFICATION")}
538
538
  </div>
539
539
 
540
540
  {/* Warning banner */}
@@ -553,10 +553,10 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
553
553
  </div>
554
554
  <div>
555
555
  <div style={{ fontWeight: "600", color: "#B54708", fontSize: "13px", marginBottom: "2px" }}>
556
- {t("EKYC_IMPORTANT") || "Important"}
556
+ {t("EKYC_IMPORTANT")}
557
557
  </div>
558
558
  <div style={{ fontSize: "12px", color: "#92400E" }}>
559
- {t("EKYC_CAPTURE_LIVE_CAMERA") || "Capture with live camera for GPS metadata"}
559
+ {t("EKYC_CAPTURE_LIVE_CAMERA")}
560
560
  </div>
561
561
  </div>
562
562
  </div>
@@ -602,7 +602,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
602
602
  {t("EKYC_TAP_TO_CAPTURE") || "Tap to capture"}
603
603
  </div>
604
604
  <div style={{ fontSize: "12px", color: "#667085" }}>
605
- {t("EKYC_CAPTURE_DOOR_IMAGE") || "Capture door image"}
605
+ {t("EKYC_CAPTURE_DOOR_IMAGE")}
606
606
  </div>
607
607
  </>
608
608
  ) : (
@@ -622,7 +622,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
622
622
  cursor: "pointer", fontSize: "12px", color: "#D92D20", fontWeight: "500",
623
623
  }}
624
624
  >
625
- <TrashIcon size={13} /> {t("EKYC_REMOVE") || "Remove"}
625
+ <TrashIcon size={13} /> {t("EKYC_REMOVE")}
626
626
  </button>
627
627
  </>
628
628
  )}
@@ -632,14 +632,14 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
632
632
  {isSection ? (
633
633
  <div style={{ marginTop: "24px" }}>
634
634
  <SubmitBar
635
- label={t("EKYC_COMPLETE_VERIFICATION_AND_PROCEED") || "Complete & Proceed"}
635
+ label={t("EKYC_COMPLETE_VERIFICATION_AND_PROCEED")}
636
636
  onSubmit={handleCompleteVerification}
637
637
  />
638
638
  </div>
639
639
  ) : (
640
640
  <ActionBar>
641
641
  <SubmitBar
642
- label={t("EKYC_COMPLETE_VERIFICATION") || "Complete Verification"}
642
+ label={t("EKYC_COMPLETE_VERIFICATION")}
643
643
  onSubmit={handleCompleteVerification}
644
644
  />
645
645
  </ActionBar>
@@ -655,7 +655,7 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
655
655
  <rect x="3" y="11" width="18" height="11" rx="2" />
656
656
  <path d="M7 11V7a5 5 0 0 1 10 0v4" />
657
657
  </svg>
658
- {t("EKYC_SECURE_DATA_NOTICE") || "Your data is encrypted and secure"}
658
+ {t("EKYC_SECURE_DATA_NOTICE")}
659
659
  </div>
660
660
  </div>
661
661
  );
@@ -90,10 +90,9 @@ const Inbox = ({
90
90
  name: "status",
91
91
  type: "dropdown",
92
92
  options: [
93
- { label: t("CHOOSE_STATUS"), value: "" },
94
- { label: t("EKYC_STATUS_COMPLETED"), value: "COMPLETED" },
95
- { label: t("EKYC_STATUS_PENDING"), value: "PENDING" },
96
- { label: t("EKYC_STATUS_REJECTED"), value: "REJECTED" },
93
+ { label: t("EKYC_STATUS_ALL"), value: "" },
94
+ { label: t("EKYC_STATUS_ACTIVE"), value: "ACTIVE" },
95
+ { label: t("EKYC_STATUS_PENDING"), value: "PENDING START" },
97
96
  ],
98
97
  optionsKey: "label"
99
98
  },
@@ -366,20 +366,34 @@ const PropertyInfo = () => {
366
366
  </div>
367
367
  <input type="file" ref={fileRef} accept=".pdf" style={{ display: "none" }} onChange={handleFileUpload} />
368
368
  <div
369
- onClick={() => fileRef.current.click()}
370
- onMouseOver={(e) => e.currentTarget.style.borderColor = "#185FA5"}
371
- onMouseOut={(e) => e.currentTarget.style.borderColor = "#B5D4F4"}
369
+ onClick={() => pidNumber && fileRef.current.click()}
370
+ onMouseOver={(e) => { if (pidNumber) e.currentTarget.style.borderColor = "#185FA5"; }}
371
+ onMouseOut={(e) => { if (pidNumber) e.currentTarget.style.borderColor = "#B5D4F4"; }}
372
372
  style={{
373
- border: "1.5px dashed #B5D4F4", borderRadius: "10px",
374
- padding: "28px 20px", textAlign: "center", cursor: "pointer",
375
- backgroundColor: "#E6F1FB", minHeight: "160px",
376
- display: "flex", flexDirection: "column",
377
- alignItems: "center", justifyContent: "center", gap: "10px",
378
- transition: "border-color 0.15s",
373
+ border: pidNumber ? "1.5px dashed #B5D4F4" : "1.5px dashed #D0D5DD",
374
+ borderRadius: "10px",
375
+ padding: "28px 20px",
376
+ textAlign: "center",
377
+ cursor: pidNumber ? "pointer" : "not-allowed",
378
+ backgroundColor: pidNumber ? "#E6F1FB" : "#F9FAFB",
379
+ minHeight: "160px",
380
+ display: "flex",
381
+ flexDirection: "column",
382
+ alignItems: "center",
383
+ justifyContent: "center",
384
+ gap: "10px",
385
+ transition: "all 0.15s",
386
+ opacity: pidNumber ? 1 : 0.6,
379
387
  }}
380
388
  >
381
- <div style={{ background: "#fff", padding: "10px", borderRadius: "10px", display: "flex" }}>
382
- <svg width="32" height="32" viewBox="0 0 24 24" fill="#185FA5">
389
+ <div style={{
390
+ background: pidNumber ? "#fff" : "#EAECF0",
391
+ padding: "10px",
392
+ borderRadius: "10px",
393
+ display: "flex",
394
+ filter: pidNumber ? "none" : "grayscale(100%)"
395
+ }}>
396
+ <svg width="32" height="32" viewBox="0 0 24 24" fill={pidNumber ? "#185FA5" : "#98A2B3"}>
383
397
  <path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13z" />
384
398
  <path d="M12 18v-4M12 14l-2 2M12 14l2 2" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" />
385
399
  </svg>
@@ -390,10 +404,10 @@ const PropertyInfo = () => {
390
404
  </div>
391
405
  ) : (
392
406
  <>
393
- <div style={{ fontSize: "13px", fontWeight: "600", color: "#185FA5" }}>
394
- {t("EKYC_UPLOAD_PROPERTY_DOC_CTA") || "Tap to upload"}
407
+ <div style={{ fontSize: "13px", fontWeight: "600", color: pidNumber ? "#185FA5" : "#98A2B3" }}>
408
+ {pidNumber ? (t("EKYC_UPLOAD_PROPERTY_DOC_CTA") || "Tap to upload") : (t("EKYC_ENTER_PID_FIRST_CTA") || "Enter PID to upload")}
395
409
  </div>
396
- <div style={{ fontSize: "12px", color: "#378ADD" }}>PDF only</div>
410
+ <div style={{ fontSize: "12px", color: pidNumber ? "#378ADD" : "#98A2B3" }}>{pidNumber ? "PDF | Max 5MB" : "Requires PID"}</div>
397
411
  </>
398
412
  )}
399
413
  </div>
@@ -431,6 +445,9 @@ const PropertyInfo = () => {
431
445
  <div style={{ fontSize: "12px", color: "#667085", marginTop: "2px" }}>
432
446
  {t("EKYC_BUILDING_PHOTO") || "Building photo with GPS"}
433
447
  </div>
448
+ <div style={{ fontSize: "11px", color: "#98A2B3", marginTop: "2px" }}>
449
+ JPG, PNG | Max 2MB
450
+ </div>
434
451
  </>
435
452
  ) : (
436
453
  <>
@@ -1,10 +1,11 @@
1
- import React, { Fragment } from "react";
1
+ import React, { Fragment, useState } from "react";
2
2
  import {
3
3
  Card,
4
4
  CardHeader,
5
5
  SubmitBar,
6
6
  HomeIcon,
7
7
  ActionBar,
8
+ Toast,
8
9
  } from "@djb25/digit-ui-react-components";
9
10
  import { useTranslation } from "react-i18next";
10
11
  import { useHistory, useLocation } from "react-router-dom";
@@ -133,8 +134,89 @@ const Review = () => {
133
134
  propertyDetails = {},
134
135
  } = location.state || {};
135
136
 
136
- const handleSubmit = () => {
137
- history.push("/digit-ui/employee/ekyc/dashboard");
137
+ const [isSubmitting, setIsSubmitting] = useState(false);
138
+ const [toast, setToast] = useState(null);
139
+
140
+ // ── Helper: upload a File object to Filestore ──────────────────────────────
141
+ const uploadFile = async (file, tenantId) => {
142
+ if (!file) return null;
143
+ const res = await Digit.UploadServices.Filestorage("EKYC", file, tenantId);
144
+ return res?.data?.files?.[0]?.fileStoreId || null;
145
+ };
146
+
147
+ // ── Helper: convert a data-URL (base64 string) to a File blob ─────────────
148
+ const dataUrlToFile = (dataUrl, filename) => {
149
+ const arr = dataUrl.split(",");
150
+ const mime = arr[0].match(/:(.*?);/)[1];
151
+ const bstr = atob(arr[1]);
152
+ let n = bstr.length;
153
+ const u8arr = new Uint8Array(n);
154
+ while (n--) u8arr[n] = bstr.charCodeAt(n);
155
+ return new File([u8arr], filename, { type: mime });
156
+ };
157
+
158
+ const handleSubmit = async () => {
159
+ setIsSubmitting(true);
160
+ setToast(null);
161
+ try {
162
+ const tenantId = Digit.ULBService.getCurrentTenantId() || "dl.djb";
163
+ const userInfo = Digit.UserService.getUser()?.info || {};
164
+
165
+ // ── 1. Upload property document (PDF File object) ──────────────────────
166
+ const propertyDocFile = propertyDetails.propertyDocument || null; // File object from <input>
167
+ const propertyDocFileStoreId = await uploadFile(propertyDocFile, tenantId);
168
+
169
+ // ── 2. Upload building photo (data-URL string from camera capture) ─────
170
+ let buildingImageFileStoreId = null;
171
+ if (propertyDetails.buildingPhoto) {
172
+ const photoFile = dataUrlToFile(propertyDetails.buildingPhoto, "building_photo.jpg");
173
+ buildingImageFileStoreId = await uploadFile(photoFile, tenantId);
174
+ }
175
+
176
+ // ── 3. Build request payload ───────────────────────────────────────────
177
+ const requestBody = {
178
+ RequestInfo: {
179
+ apiId: "Rainmaker",
180
+ ver: "1.0",
181
+ msgId: `${Date.now()}|${navigator.language || "en_IN"}`,
182
+ tenantId,
183
+ authToken: userInfo.access_token || Digit.UserService.getUser()?.access_token || "",
184
+ },
185
+ updateType: "PROPERTY",
186
+ kno: kNumber,
187
+ pidNumber: propertyDetails.pidNumber || null,
188
+ propertyDocumentFileStoreId: propertyDocFileStoreId,
189
+ buildingImageFileStoreId: buildingImageFileStoreId,
190
+ userType: propertyDetails.userType?.value || null,
191
+ noOfFloor: propertyDetails.noOfFloors?.value ? parseInt(propertyDetails.noOfFloors.value, 10) : null,
192
+ typeOfConnection: propertyDetails.connectionCategory?.value || null,
193
+ connectionCategory: propertyDetails.connectionType?.value || null,
194
+ modifiedBy: userInfo.name || userInfo.userName || null,
195
+ };
196
+
197
+ // ── 4. Call the update API ─────────────────────────────────────────────
198
+ await Digit.CustomService.getResponse({
199
+ url: "/ekyc-service/user/application/_update",
200
+ params: { tenantId },
201
+ body: requestBody,
202
+ useCache: false,
203
+ method: "POST",
204
+ });
205
+
206
+ setToast({ type: "success", message: t("EKYC_SUBMIT_SUCCESS") || "Application submitted successfully!" });
207
+ setTimeout(() => {
208
+ history.push("/digit-ui/employee/ekyc/dashboard");
209
+ }, 1800);
210
+ } catch (err) {
211
+ console.error("eKYC Submit Error:", err);
212
+ setToast({
213
+ type: "error",
214
+ message: err?.response?.data?.Errors?.[0]?.message ||
215
+ t("EKYC_SUBMIT_ERROR") || "Submission failed. Please try again.",
216
+ });
217
+ } finally {
218
+ setIsSubmitting(false);
219
+ }
138
220
  };
139
221
 
140
222
  const handleEditAadhaar = () => {
@@ -150,6 +232,7 @@ const Review = () => {
150
232
  };
151
233
 
152
234
  return (
235
+ <Fragment>
153
236
  <div className="inbox-container">
154
237
  <style>{`
155
238
  @keyframes fadeSlideIn {
@@ -259,7 +342,7 @@ const Review = () => {
259
342
  editLabel={t("CS_COMMON_EDIT") || "Edit"}
260
343
  rows={[
261
344
  { label: t("EKYC_NAME") || "Name", value: aadhaarDetails.userName || "Rajesh Kumar Singh" },
262
- { label: t("EKYC_AADHAAR") || "Aadhaar no.", value: aadhaarDetails.aadhaarLastFour ? `XXXX XXXX ${aadhaarDetails.aadhaarLastFour}` : "XXXX XXXX 1234" },
345
+ { label: t("EKYC_AADHAAR") || "Aadhaar no.", value: aadhaarDetails.aadhaarLastFour ? `${aadhaarDetails.aadhaarLastFour}` : "XXXX XXXX 1234" },
263
346
  { label: t("EKYC_MOBILE_NO") || "Mobile no.", value: aadhaarDetails.mobileNumber || "XXXXXXXXXX" },
264
347
  { label: t("EKYC_EMAIL_ADDRESS") || "Email", value: aadhaarDetails.email || null },
265
348
  ]}
@@ -317,8 +400,12 @@ const Review = () => {
317
400
  {/* Submit (Non-sticky, at form end) */}
318
401
  <div style={{ marginTop: "24px" }}>
319
402
  <SubmitBar
320
- label={t("ES_COMMON_SUBMIT") || "Submit"}
403
+ label={isSubmitting
404
+ ? (t("EKYC_SUBMITTING") || "Submitting...")
405
+ : (t("ES_COMMON_SUBMIT") || "Submit")
406
+ }
321
407
  onSubmit={handleSubmit}
408
+ disabled={isSubmitting}
322
409
  />
323
410
  </div>
324
411
 
@@ -338,6 +425,17 @@ const Review = () => {
338
425
  </Card>
339
426
  </div>
340
427
  </div>
428
+
429
+ {/* Toast notification */}
430
+ {toast && (
431
+ <Toast
432
+ label={toast.message}
433
+ error={toast.type === "error"}
434
+ success={toast.type === "success"}
435
+ onClose={() => setToast(null)}
436
+ />
437
+ )}
438
+ </Fragment>
341
439
  );
342
440
  };
343
441