@djb25/digit-ui-module-ekyc 1.0.7 → 1.0.9

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.
@@ -1,528 +1,312 @@
1
- import React, { Fragment, useState, useEffect } from "react";
1
+ import React, { useState, Fragment, useEffect } from "react";
2
2
  import {
3
3
  Card,
4
4
  CardHeader,
5
+ CardSubHeader,
6
+ StatusTable,
7
+ Row,
5
8
  SubmitBar,
6
- HomeIcon,
9
+ Loader,
7
10
  ActionBar,
8
- Toast,
11
+ CheckBox,
12
+ LinkButton,
13
+ EditIcon,
14
+ GenericFileIcon,
9
15
  } from "@djb25/digit-ui-react-components";
10
16
  import { useTranslation } from "react-i18next";
11
17
  import { useHistory, useLocation } from "react-router-dom";
12
- import { getPayloadDiff, getSavedData } from "../../utils";
13
-
14
- // ─── Icons ────────────────────────────────────────────────────────────────────
15
-
16
- const CheckIcon = ({ size = 11, color = "#fff" }) => (
17
- <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="3" strokeLinecap="round">
18
- <polyline points="20 6 9 17 4 12" />
19
- </svg>
20
- );
21
-
22
- const PersonIcon = ({ size = 16 }) => (
23
- <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
24
- <path d="M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z" />
25
- </svg>
26
- );
27
-
28
- const LocationIcon2 = ({ size = 16 }) => (
29
- <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
30
- <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z" />
31
- </svg>
32
- );
33
-
34
- const BuildingIcon = ({ size = 16 }) => (
35
- <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
36
- <path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8V9h8v10zm-2-8h-4v2h4v-2zm0 4h-4v2h4v-2z" />
37
- </svg>
38
- );
39
-
40
- const EditIcon = ({ size = 14 }) => (
41
- <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
42
- <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
43
- <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
44
- </svg>
45
- );
46
-
47
- // ─── Reusable: Section heading with inline rule ───────────────────────────────
48
-
49
- const SectionHead = ({ icon, label }) => (
50
- <div style={{
51
- display: "flex", alignItems: "center", gap: "8px",
52
- marginBottom: "16px", marginTop: "4px",
53
- }}>
54
- <div style={{ opacity: 0.5, display: "flex" }}>{icon}</div>
55
- <span style={{ fontSize: "15px", fontWeight: "600", color: "#0B0C0C", whiteSpace: "nowrap" }}>
56
- {label}
57
- </span>
58
- <div style={{ flex: 1, height: "1px", background: "#EAECF0" }} />
59
- </div>
60
- );
61
-
62
- // ─── Reusable: Review section card ───────────────────────────────────────────
63
-
64
- const ReviewCard = ({ icon, title, onEdit, editLabel, rows, t }) => (
65
- <div style={{
66
- border: "0.5px solid #EAECF0",
67
- borderRadius: "10px",
68
- overflow: "hidden",
69
- marginBottom: "16px",
70
- animation: "fadeSlideIn 0.3s ease",
71
- }}>
72
- {/* Card header */}
73
- <div style={{
74
- display: "flex", justifyContent: "space-between", alignItems: "center",
75
- padding: "14px 16px",
76
- background: "#F9FAFB",
77
- borderBottom: "0.5px solid #EAECF0",
78
- }}>
79
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
80
- <div style={{ opacity: 0.5, display: "flex" }}>{icon}</div>
81
- <span style={{ fontSize: "14px", fontWeight: "600", color: "#0B0C0C" }}>{title}</span>
82
- </div>
83
- <button
84
- onClick={onEdit}
85
- style={{
86
- display: "flex", alignItems: "center", gap: "5px",
87
- background: "none", border: "0.5px solid #D0D5DD",
88
- borderRadius: "6px", padding: "5px 12px",
89
- fontSize: "12px", fontWeight: "600", color: "#185FA5",
90
- cursor: "pointer",
91
- }}
92
- >
93
- <EditIcon size={12} />
94
- {editLabel}
95
- </button>
96
- </div>
97
18
 
98
- {/* Rows */}
99
- <div style={{ padding: "4px 0" }}>
100
- {rows.map((row, i) => (
101
- row.value ? (
102
- <div key={i} style={{
103
- display: "flex", alignItems: "flex-start",
104
- padding: "10px 16px",
105
- borderBottom: i < rows.length - 1 ? "0.5px solid #F2F4F7" : "none",
106
- }}>
107
- <div style={{
108
- flex: "0 0 180px", fontSize: "12px", fontWeight: "600",
109
- color: "#667085", textTransform: "uppercase",
110
- letterSpacing: "0.04em", paddingTop: "1px",
111
- }}>
112
- {row.label}
113
- </div>
114
- <div style={{ flex: 1, fontSize: "14px", color: "#101828", fontWeight: "500", wordBreak: "break-word", display: "flex", alignItems: "center", gap: "8px" }}>
115
- {row.value}
116
- {row.isModified && (
117
- <span style={{
118
- fontSize: "10px",
119
- background: "#FFF4ED",
120
- color: "#B45309",
121
- border: "0.5px solid #FDE68A",
122
- borderRadius: "4px",
123
- padding: "2px 6px",
124
- fontWeight: "600",
125
- textTransform: "uppercase"
126
- }}>
127
- {t("EKYC_MODIFIED") || "Modified"}
128
- </span>
129
- )}
130
- </div>
131
- </div>
132
- ) : null
133
- ))}
134
- </div>
135
- </div>
136
- );
19
+ // ─── Constants ───────────────────────────────────────────────────────────────
20
+
21
+ const ActionButton = ({ jumpTo, state }) => {
22
+ const { t } = useTranslation();
23
+ const history = useHistory();
24
+ function routeTo() {
25
+ history.push(jumpTo, { ...state, isEditing: true });
26
+ }
27
+ return (
28
+ <LinkButton
29
+ label={<EditIcon style={{ marginTop: "-30px", float: "right", position: "relative", bottom: "32px" }} />}
30
+ className="check-page-link-button"
31
+ onClick={routeTo}
32
+ />
33
+ );
34
+ };
35
+
36
+ const checkForNA = (value) => (value !== null && value !== undefined && value !== "") ? value : "N/A";
37
+
38
+ const boolToYesNo = (value, t) => {
39
+ if (value === true || value === "true" || String(value).toLowerCase() === "yes") return t("CORE_COMMON_YES");
40
+ if (value === false || value === "false" || String(value).toLowerCase() === "no") return t("CORE_COMMON_NO");
41
+ return "N/A";
42
+ };
137
43
 
138
- // ─── Main Component ───────────────────────────────────────────────────────────
44
+ /**
45
+ * Robust data extraction based on the screenshot provided.
46
+ * The API returns { applicationReview: { newData: { ...flatFields } } }
47
+ */
48
+ const extractActiveData = (searchData, flowState) => {
49
+ const rawData = (searchData && Object.keys(searchData).length > 0) ? searchData : flowState?.reviewData || {};
50
+
51
+ // Navigate through applicationReview -> newData
52
+ const reviewWrapper = rawData?.applicationReview || rawData;
53
+ const applicationData = (Array.isArray(reviewWrapper) ? reviewWrapper[0] : reviewWrapper) || {};
54
+ return applicationData?.newData || applicationData;
55
+ };
139
56
 
140
57
  const Review = () => {
141
58
  const { t } = useTranslation();
142
59
  const history = useHistory();
143
60
  const location = useLocation();
144
61
 
145
- // ── Restore State Logic ──
146
- const state = location.state || {};
147
- const initialData = state.initialData || getSavedData("EKYC_INITIAL_DATA", {});
148
- const kNumber = state.kNumber || sessionStorage.getItem("EKYC_K_NUMBER") || "EKYC-1234567890";
149
-
150
- // Reconstruct nested objects if state is lost on refresh
151
- const aadhaarDetails = state.aadhaarDetails || {
152
- userName: sessionStorage.getItem("EKYC_USER_NAME"),
153
- mobileNumber: sessionStorage.getItem("EKYC_MOBILE_NUMBER"),
154
- whatsappNumber: sessionStorage.getItem("EKYC_WHATSAPP_NUMBER"),
155
- email: sessionStorage.getItem("EKYC_EMAIL"),
156
- noOfPersons: sessionStorage.getItem("EKYC_NO_OF_PERSONS"),
157
- };
158
-
159
- const addressDetails = state.addressDetails || {
160
- fullAddress: sessionStorage.getItem("EKYC_FULL_ADDRESS"),
161
- flatNo: sessionStorage.getItem("EKYC_FLAT_NO"),
162
- building: sessionStorage.getItem("EKYC_BUILDING"),
163
- landmark: sessionStorage.getItem("EKYC_LANDMARK"),
164
- pincode: sessionStorage.getItem("EKYC_PINCODE"),
165
- assembly: getSavedData("EKYC_ASSEMBLY_DATA")?.name,
166
- ward: getSavedData("EKYC_WARD_DATA")?.name,
167
- doorPhoto: sessionStorage.getItem("EKYC_DOOR_PHOTO"),
168
- doorPhotoFileStoreId: sessionStorage.getItem("EKYC_DOOR_PHOTO_FILESTORE_ID"),
169
- };
62
+ const [agree, setAgree] = useState(false);
63
+ const [isSubmitting, setIsSubmitting] = useState(false);
170
64
 
171
- const propertyDetails = state.propertyDetails || {
172
- ownerType: sessionStorage.getItem("EKYC_OWNER_TYPE"),
173
- pidNumber: sessionStorage.getItem("EKYC_PID_NUMBER"),
174
- connectionCategory: getSavedData("EKYC_TYPE_OF_CONNECTION_DATA"),
175
- connectionType: getSavedData("EKYC_CONNECTION_CATEGORY_DATA"),
176
- userType: getSavedData("EKYC_USER_TYPE_DATA"),
177
- noOfFloors: getSavedData("EKYC_NO_OF_FLOORS_DATA"),
178
- propertyDocument: sessionStorage.getItem("EKYC_PROPERTY_DOC"),
179
- propertyDocumentFileStoreId: sessionStorage.getItem("EKYC_PROPERTY_DOC_FILESTORE_ID"),
180
- buildingPhoto: sessionStorage.getItem("EKYC_BUILDING_PHOTO"),
181
- buildingPhotoFileStoreId: sessionStorage.getItem("EKYC_BUILDING_PHOTO_FILESTORE_ID"),
182
- };
65
+ const flowState = location.state || {};
66
+ const { kNumber, kno, edits = {} } = flowState;
67
+ const activeKno = kNumber || kno;
183
68
 
184
- useEffect(() => {
185
- sessionStorage.setItem("EKYC_CURRENT_STEP", "REVIEW");
186
- }, []);
69
+ const { aadhaarData = {}, addressDetails: editedAddress = {}, propertyDetails: editedProperty = {}, meterDetails: editedMeter = {} } = edits;
187
70
 
188
- // Helper to check if a field is modified
189
- const isFieldModified = (key, currentVal) => {
190
- const initialVal = initialData[key];
191
- if (initialVal === undefined) return false;
192
- return JSON.stringify(initialVal) !== JSON.stringify(currentVal);
193
- };
71
+ const tenantId = Digit.ULBService.getCurrentTenantId();
194
72
 
195
- const [toast, setToast] = useState(null);
196
- const tenantId = Digit.ULBService.getCurrentTenantId() || "dl";
197
- const { mutate, isLoading: isMutationLoading } = Digit.Hooks.ekyc.useEkycApplicationUpdate(tenantId);
198
- const isSubmitting = isMutationLoading;
73
+ const { data: searchData, isLoading: isSearchLoading } = Digit.Hooks.ekyc.useEkycSearchReview(
74
+ { kno: activeKno, fetchType: "REVIEW" },
75
+ tenantId,
76
+ { enabled: !!activeKno }
77
+ );
199
78
 
200
- // ── Helper: upload a File object to Filestore ──────────────────────────────
201
- const uploadFile = async (file, tenantId) => {
202
- if (!file) return null;
203
- const res = await Digit.UploadServices.Filestorage("EKYC", file, tenantId);
204
- return res?.data?.files?.[0]?.fileStoreId || null;
79
+ // ── Data Consolidation ──────────────────────────────────────────────────
80
+ const activeData = extractActiveData(searchData, flowState);
81
+
82
+ // Support both structured (connectionDetails.x) and flat (x) formats
83
+ const apiConn = activeData?.connectionDetails || activeData || {};
84
+ const apiAddr = activeData?.addressDetails || activeData || {};
85
+ const apiProp = activeData?.propertyInfo || activeData || {};
86
+ const apiMeter = activeData?.meterDetails || activeData || {};
87
+
88
+ const connectionData = {
89
+ consumerName: aadhaarData?.name || apiConn?.consumerName,
90
+ address: apiConn?.address || apiConn?.addressRaw,
91
+ connectionType: apiConn?.connectionType || apiConn?.connectionCategory,
92
+ meterNumber: apiConn?.meterNumber || apiConn?.meterNo,
93
+ phoneNumber: aadhaarData?.mobileNumber || apiConn?.phoneNumber || apiConn?.mobileNo,
94
+ email: apiConn?.email,
95
+ statusflag: apiConn?.statusflag || apiConn?.statusFlag,
96
+ ekycStatus: apiConn?.ekycStatus,
97
+ knumber: apiConn?.knumber || apiConn?.kno || activeKno,
205
98
  };
206
99
 
207
- // ── Helper: convert a data-URL (base64 string) to a File blob ─────────────
208
- const dataUrlToFile = (dataUrl, filename) => {
209
- const arr = dataUrl.split(",");
210
- const mime = arr[0].match(/:(.*?);/)[1];
211
- const bstr = atob(arr[1]);
212
- let n = bstr.length;
213
- const u8arr = new Uint8Array(n);
214
- while (n--) u8arr[n] = bstr.charCodeAt(n);
215
- return new File([u8arr], filename, { type: mime });
100
+ const addressData = {
101
+ fullAddress: editedAddress?.fullAddress || apiAddr?.fullAddress || apiAddr?.addressRaw,
102
+ flatHouseNumber: editedAddress?.flatHouseNumber || editedAddress?.flatNo || apiAddr?.flatHouseNumber,
103
+ buildingTower: editedAddress?.buildingTower || editedAddress?.building || apiAddr?.buildingTower,
104
+ landmark: editedAddress?.landmark || apiAddr?.landmark,
105
+ pinCode: editedAddress?.pinCode || editedAddress?.pincode || apiAddr?.pinCode || apiAddr?.pincode,
106
+ ward: editedAddress?.ward || apiAddr?.ward || apiAddr?.locality,
107
+ assembly: editedAddress?.assembly || apiAddr?.assembly,
108
+ gpsValid: editedAddress?.gpsValid !== undefined ? editedAddress.gpsValid : apiAddr?.gpsValid,
109
+ latitude: editedAddress?.latitude || apiAddr?.latitude,
110
+ longitude: editedAddress?.longitude || apiAddr?.longitude,
111
+ mobileNo: editedAddress?.mobileNo || aadhaarData?.mobileNumber || apiAddr?.mobileNo,
112
+ whatsappNo: editedAddress?.whatsappNo || aadhaarData?.whatsappNumber || apiAddr?.whatsappNo,
113
+ email: editedAddress?.email || apiAddr?.email,
114
+ noOfPerson: editedAddress?.noOfPerson || aadhaarData?.noOfPersons || apiAddr?.noOfPerson,
115
+ knumber: editedAddress?.knumber || apiAddr?.knumber || apiAddr?.kno || activeKno,
116
+ doorPhotoFilestoreId: editedAddress?.doorPhotoFileStoreId || apiAddr?.doorPhotoFilestoreId,
216
117
  };
217
118
 
218
- const handleSubmit = async () => {
219
- setToast(null);
220
- try {
221
- const userInfo = Digit.UserService.getUser()?.info || {};
222
-
223
- // ── 1. Upload property document ──────────────────────────────────────
224
- let propertyDocFileStoreId = propertyDetails.propertyDocumentFileStoreId || null;
225
- if (!propertyDocFileStoreId && propertyDetails.propertyDocument instanceof File) {
226
- propertyDocFileStoreId = await uploadFile(propertyDetails.propertyDocument, tenantId);
227
- }
228
-
229
- // ── 2. Upload building photo ──────────────────────────────────────────
230
- let buildingImageFileStoreId = propertyDetails.buildingPhotoFileStoreId || null;
231
- if (!buildingImageFileStoreId && propertyDetails.buildingPhoto) {
232
- // Fallback if we only have the dataURL
233
- const photoFile = dataUrlToFile(propertyDetails.buildingPhoto, "building_photo.jpg");
234
- buildingImageFileStoreId = await uploadFile(photoFile, tenantId);
235
- }
236
-
237
- // ── 3. Upload door photo ──────────────────────────────────────────────
238
- let doorPhotoFileStoreId = addressDetails.doorPhotoFileStoreId || null;
239
- if (!doorPhotoFileStoreId && addressDetails.doorPhoto) {
240
- const doorFile = dataUrlToFile(addressDetails.doorPhoto, "door_photo.jpg");
241
- doorPhotoFileStoreId = await uploadFile(doorFile, tenantId);
242
- }
243
-
244
- // ── 4. Build optimized request payload ────────────────────────────────
245
- // Note: RequestInfo is added automatically by the Digit Request utility
246
- const requestBody = {
247
- updateType: "PROPERTY",
248
- kno: kNumber,
249
- pidNumber: propertyDetails.pidNumber || null,
250
- propertyDocumentFileStoreId: propertyDocFileStoreId,
251
- buildingImageFileStoreId: buildingImageFileStoreId,
252
- userType: propertyDetails.userType?.value || null,
253
- noOfFloor: propertyDetails.noOfFloors?.value ? parseInt(propertyDetails.noOfFloors.value, 10) : null,
254
- typeOfConnection: propertyDetails.connectionCategory?.value || null,
255
- connectionCategory: propertyDetails.connectionType?.value || null,
256
- modifiedBy: userInfo.name || userInfo.userName || null,
257
- mobileNumber: aadhaarDetails.mobileNumber || null,
258
- email: aadhaarDetails.email || null,
259
- userName: aadhaarDetails.userName || null,
260
- noOfPersons: aadhaarDetails.noOfPersons || null,
261
- doorPhotoFileStoreId: doorPhotoFileStoreId,
262
- fullAddress: addressDetails.fullAddress || null,
263
- flatNo: addressDetails.flatNo || null,
264
- building: addressDetails.building || null,
265
- landmark: addressDetails.landmark || null,
266
- pincode: addressDetails.pincode || null,
267
- assembly: addressDetails.assembly || null,
268
- ward: addressDetails.ward || null,
269
- };
119
+ const propertyData = {
120
+ kno: apiProp?.kno || activeKno,
121
+ pidNumber: editedProperty?.pidNumber || apiProp?.pidNumber,
122
+ typeOfConnection: editedProperty?.connectionTypeData?.label || apiProp?.typeOfConnection,
123
+ connectionCategory: editedProperty?.connectionCategoryData?.label || apiProp?.connectionCategory,
124
+ userType: editedProperty?.userTypeData?.label || apiProp?.userType,
125
+ numberOfFloors: editedProperty?.noOfFloorsData?.label || apiProp?.numberOfFloors,
126
+ tenantName: apiProp?.tenantName,
127
+ tenantMobile: apiProp?.tenantMobile,
128
+ ekycStatus: apiProp?.ekycStatus,
129
+ propertyDocumentFileStoreId: editedProperty?.propertyDocumentFileStoreId || apiProp?.propertyDocumentFileStoreId,
130
+ buildingImageFileStoreId: apiProp?.buildingImageFileStoreId,
131
+ };
270
132
 
271
- // ── 4. Call the update API using the new Hook ──────────────────────────
272
- mutate(requestBody, {
273
- onSuccess: (res) => {
274
- setToast({ type: "success", message: t("EKYC_SUBMIT_SUCCESS") || "Application submitted successfully!" });
133
+ const meterData = {
134
+ kno: editedMeter?.kno || apiMeter?.kno || activeKno,
135
+ metered: editedMeter?.meterStatusData?.value === "Metered" || apiMeter?.metered,
136
+ meterNumber: apiMeter?.meterNumber || apiMeter?.meterNo,
137
+ meterMake: editedMeter?.meterMake || apiMeter?.meterMake,
138
+ meterLocationAddress: editedMeter?.meterLocation || apiMeter?.meterLocationAddress,
139
+ meterLatitude: apiMeter?.meterLatitude,
140
+ meterLongitude: apiMeter?.meterLongitude,
141
+ workingStatus: editedMeter?.workingStatusData?.value === "Working" || apiMeter?.workingStatus,
142
+ lastBillRaised: editedMeter?.lastBillRaisedData?.value === "Yes" || apiMeter?.lastBillRaised,
143
+ systemMeterId: apiMeter?.systemMeterId,
144
+ meterPhotoFileStoreId: editedMeter?.meterPhotoFileStoreId || apiMeter?.meterPhotoFileStoreId,
145
+ };
275
146
 
276
- // Cleanup sessionStorage on success
277
- Object.keys(sessionStorage).forEach(key => {
278
- if (key.startsWith("EKYC_")) sessionStorage.removeItem(key);
279
- });
147
+ const handleDeclaration = () => setAgree(!agree);
280
148
 
281
- setTimeout(() => {
282
- history.push("/digit-ui/employee/ekyc/dashboard");
283
- }, 1800);
149
+ const handleFinalSubmit = async () => {
150
+ setIsSubmitting(true);
151
+ try {
152
+ const payload = {
153
+ kno: activeKno,
154
+ tenantId: tenantId,
155
+ newData: {
156
+ connectionDetails: connectionData,
157
+ addressDetails: addressData,
158
+ propertyInfo: propertyData,
159
+ meterDetails: meterData,
284
160
  },
285
- onError: (err) => {
286
- console.error("eKYC Submit Error:", err);
287
- setToast({
288
- type: "error",
289
- message: err?.response?.data?.Errors?.[0]?.message ||
290
- t("EKYC_SUBMIT_ERROR") || "Submission failed. Please try again.",
291
- });
292
- }
293
- });
161
+ };
294
162
 
163
+ const result = await Digit.EkycService.application_update(payload, tenantId);
164
+ if (result) {
165
+ history.push("/digit-ui/employee/ekyc/response", { success: true, result });
166
+ }
295
167
  } catch (err) {
296
- console.error("eKYC Frontend Error:", err);
297
- setToast({
298
- type: "error",
299
- message: t("EKYC_SUBMIT_ERROR") || "An error occurred during submission.",
300
- });
168
+ console.error("Submit Error:", err);
169
+ } finally {
170
+ setIsSubmitting(false);
301
171
  }
302
172
  };
303
173
 
304
- const handleEditAadhaar = () => {
305
- history.push("/digit-ui/employee/ekyc/aadhaar-verification", location.state);
306
- };
174
+ if (isSearchLoading || isSubmitting) return <Loader />;
307
175
 
308
- const handleEditAddress = () => {
309
- history.push("/digit-ui/employee/ekyc/address-details", location.state);
310
- };
311
-
312
- const handleEditProperty = () => {
313
- history.push("/digit-ui/employee/ekyc/property-info", location.state);
314
- };
176
+ const baseUrl = "/digit-ui/employee/ekyc";
315
177
 
316
178
  return (
317
- <div className="ground-container employee-app-container form-container">
318
-
319
- <Fragment>
320
- <div className="inbox-container">
321
- <style>{`
322
- @keyframes fadeSlideIn {
323
- from { opacity: 0; transform: translateY(8px); }
324
- to { opacity: 1; transform: translateY(0); }
325
- }
326
- `}</style>
327
-
328
- {/* ── Sidebar ── */}
329
- <div className="filters-container">
330
- <Card style={{ display: "flex", alignItems: "center", padding: "12px 16px", marginBottom: "12px", borderRadius: "8px" }}>
331
- <div style={{ color: "#185FA5", marginRight: "10px", display: "flex" }}>
332
- <HomeIcon style={{ width: "20px", height: "20px" }} />
333
- </div>
334
- <div style={{ fontWeight: "600", fontSize: "15px", color: "#0B0C0C" }}>
335
- {t("EKYC_PROCESS") || "eKYC Process"}
336
- </div>
337
- </Card>
338
-
339
- <div style={{ background: "#fff", padding: "16px 14px", borderRadius: "8px", border: "1px solid #EAECF0" }}>
340
- {[
341
- { label: t("EKYC_STEP_AADHAAR") || "Aadhaar", done: true, active: false },
342
- { label: t("EKYC_STEP_ADDRESS") || "Address", done: true, active: false },
343
- { label: t("EKYC_STEP_PROPERTY") || "Property", done: true, active: false },
344
- { label: t("EKYC_STEP_REVIEW") || "Review", done: false, active: true },
345
- ].map((step, i) => (
346
- <div key={i} style={{
347
- display: "flex", gap: "10px", alignItems: "flex-start",
348
- position: "relative", paddingBottom: i < 3 ? "18px" : 0,
349
- }}>
350
- {i < 3 && (
351
- <div style={{
352
- position: "absolute", left: "10px", top: "22px",
353
- width: "1px", height: "calc(100% - 10px)", background: "#EAECF0",
354
- }} />
355
- )}
356
- <div style={{
357
- width: "20px", height: "20px", borderRadius: "50%", flexShrink: 0, marginTop: "1px",
358
- border: step.done ? "none" : step.active ? "1.5px solid #185FA5" : "1.5px solid #D0D5DD",
359
- background: step.done ? "#0F6E56" : step.active ? "#E6F1FB" : "#fff",
360
- display: "flex", alignItems: "center", justifyContent: "center",
361
- fontSize: "10px", fontWeight: "500",
362
- color: step.done ? "#fff" : step.active ? "#185FA5" : "#98A2B3",
363
- }}>
364
- {step.done ? <CheckIcon size={11} color="#fff" /> : i + 1}
365
- </div>
366
- <div style={{
367
- fontSize: "12px", paddingTop: "2px",
368
- color: step.done ? "#0F6E56" : step.active ? "#0B0C0C" : "#667085",
369
- fontWeight: step.done || step.active ? "600" : "400",
370
- }}>
371
- {step.label}
372
- </div>
373
- </div>
374
- ))}
375
- </div>
376
- </div>
377
-
378
- {/* ── Main Content ── */}
379
- <div style={{ flex: 1, marginLeft: "16px" }}>
380
- <Card>
381
-
382
- {/* Page header */}
383
- <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }}>
384
- <CardHeader style={{ margin: 0, fontSize: "18px" }}>
385
- {t("EKYC_REVIEW_DETAILS") || "Review Details"}
386
- </CardHeader>
387
- <div style={{
388
- background: "#F9FAFB", border: "0.5px solid #EAECF0",
389
- borderRadius: "20px", padding: "4px 14px",
390
- fontSize: "12px", color: "#667085",
391
- }}>
392
- {t("EKYC_K_NUMBER") || "K Number"}:{" "}
393
- <span style={{ color: "#0B0C0C", fontWeight: "600" }}>{kNumber}</span>
394
- </div>
395
- </div>
396
-
397
- {/* Confirmation banner */}
398
- <div style={{
399
- backgroundColor: "#E1F5EE", border: "0.5px solid #5DCAA5",
400
- borderRadius: "8px", padding: "12px 16px",
401
- display: "flex", alignItems: "center", gap: "10px",
402
- marginBottom: "24px",
403
- }}>
404
- <div style={{ backgroundColor: "#9FE1CB", padding: "5px", borderRadius: "6px", display: "flex", flexShrink: 0 }}>
405
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#085041" strokeWidth="3" strokeLinecap="round">
406
- <polyline points="20 6 9 17 4 12" />
407
- </svg>
408
- </div>
409
- <div style={{ fontSize: "13px", color: "#04342C", fontWeight: "500" }}>
410
- {t("EKYC_REVIEW_NOTICE") || "Please review all details carefully before submitting. You can edit any section by clicking Edit."}
411
- </div>
412
- </div>
413
-
414
- <div style={{ animation: "fadeSlideIn 0.3s ease" }}>
415
-
416
- {/* ── Aadhaar section head ── */}
417
- <SectionHead
418
- icon={<PersonIcon size={16} />}
419
- label={t("EKYC_AADHAAR_VERIFICATION_HEADER") || "Aadhaar details"}
420
- />
421
-
422
- <ReviewCard
423
- icon={<PersonIcon size={16} />}
424
- title={t("EKYC_AADHAAR_VERIFICATION_HEADER") || "Aadhaar details"}
425
- onEdit={handleEditAadhaar}
426
- editLabel={t("CS_COMMON_EDIT") || "Edit"}
427
- t={t}
428
- rows={[
429
- { label: t("EKYC_NAME") || "Name", value: aadhaarDetails.userName || "Rajesh Kumar Singh", isModified: isFieldModified("userName", aadhaarDetails.userName) },
430
- { label: t("EKYC_AADHAAR") || "Aadhaar no.", value: aadhaarDetails.aadhaarLastFour ? `${aadhaarDetails.aadhaarLastFour}` : "XXXX XXXX 1234" },
431
- { label: t("EKYC_MOBILE_NO") || "Mobile no.", value: aadhaarDetails.mobileNumber || "XXXXXXXXXX", isModified: isFieldModified("mobileNumber", aadhaarDetails.mobileNumber) },
432
- { label: t("EKYC_EMAIL_ADDRESS") || "Email", value: aadhaarDetails.email || null, isModified: isFieldModified("email", aadhaarDetails.email) },
433
- ]}
434
- />
435
-
436
- <hr style={{ margin: "20px 0", border: 0, borderTop: "1px solid #EAECF0" }} />
437
-
438
- {/* ── Address section head ── */}
439
- <SectionHead
440
- icon={<LocationIcon2 size={16} />}
441
- label={t("EKYC_ADDRESS_DETAILS_HEADER") || "Address details"}
442
- />
443
-
444
- <ReviewCard
445
- icon={<LocationIcon2 size={16} />}
446
- title={t("EKYC_ADDRESS_DETAILS_HEADER") || "Address details"}
447
- onEdit={handleEditAddress}
448
- editLabel={t("CS_COMMON_EDIT") || "Edit"}
449
- t={t}
450
- rows={[
451
- { label: t("EKYC_FULL_ADDRESS") || "Full address", value: addressDetails.fullAddress || "H.No. 123, Sector 15, Rohini, Delhi – 110085", isModified: isFieldModified("fullAddress", addressDetails.fullAddress) },
452
- { label: t("EKYC_FLAT_HOUSE_NUMBER") || "Flat / House no.", value: addressDetails.flatNo || null, isModified: isFieldModified("flatNo", addressDetails.flatNo) },
453
- { label: t("EKYC_BUILDING_TOWER") || "Building", value: addressDetails.building || null, isModified: isFieldModified("building", addressDetails.building) },
454
- { label: t("EKYC_LANDMARK") || "Landmark", value: addressDetails.landmark || null, isModified: isFieldModified("landmark", addressDetails.landmark) },
455
- { label: t("EKYC_PINCODE") || "Pincode", value: addressDetails.pincode || "110085", isModified: isFieldModified("pincode", addressDetails.pincode) },
456
- { label: t("EKYC_ASSEMBLY") || "Assembly", value: addressDetails.assembly || "AC-12 Chandni Chowk", isModified: isFieldModified("assembly", addressDetails.assembly) },
457
- { label: t("EKYC_WARD") || "Ward", value: addressDetails.ward || "WARD-45 Civil Lines", isModified: isFieldModified("ward", addressDetails.ward) },
458
- ]}
459
- />
460
-
461
- <hr style={{ margin: "20px 0", border: 0, borderTop: "1px solid #EAECF0" }} />
462
-
463
- {/* ── Property section head ── */}
464
- <SectionHead
465
- icon={<BuildingIcon size={16} />}
466
- label={t("EKYC_PROPERTY_INFO") || "Property details"}
467
- />
468
-
469
- <ReviewCard
470
- icon={<BuildingIcon size={16} />}
471
- title={t("EKYC_PROPERTY_INFO") || "Property details"}
472
- onEdit={handleEditProperty}
473
- editLabel={t("CS_COMMON_EDIT") || "Edit"}
474
- t={t}
475
- rows={[
476
- { label: t("EKYC_PROPERTY_OWNER") || "Property owner", value: propertyDetails.ownerType || "Owner" },
477
- { label: t("EKYC_PID_NUMBER") || "PID number", value: propertyDetails.pidNumber || null, isModified: isFieldModified("pidNumber", propertyDetails.pidNumber) },
478
- { label: t("EKYC_TYPE_OF_CONNECTION") || "Type of connection", value: propertyDetails.connectionCategory?.label || null, isModified: isFieldModified("typeOfConnection", propertyDetails.connectionCategory?.value) },
479
- { label: t("EKYC_CONNECTION_CATEGORY") || "Connection category", value: propertyDetails.connectionType?.label || null, isModified: isFieldModified("connectionCategory", propertyDetails.connectionType?.value) },
480
- { label: t("EKYC_USER_TYPE") || "User type", value: propertyDetails.userType?.label || null, isModified: isFieldModified("userType", propertyDetails.userType?.value) },
481
- { label: t("EKYC_NO_OF_FLOORS") || "No. of floors", value: propertyDetails.noOfFloors?.label || null, isModified: isFieldModified("noOfFloor", propertyDetails.noOfFloors?.value) },
482
- ]}
483
- />
484
-
485
- </div>
486
-
487
- {/* Submit (Non-sticky, at form end) */}
488
- <div style={{ marginTop: "24px" }}>
489
- <SubmitBar
490
- label={isSubmitting
491
- ? (t("EKYC_SUBMITTING") || "Submitting...")
492
- : (t("ES_COMMON_SUBMIT") || "Submit")
493
- }
494
- onSubmit={handleSubmit}
495
- disabled={isSubmitting}
496
- />
497
- </div>
498
-
499
- {/* Secure notice */}
500
- <div style={{
501
- display: "flex", alignItems: "center", justifyContent: "center",
502
- gap: "5px", marginTop: "16px",
503
- fontSize: "11px", color: "#98A2B3",
504
- }}>
505
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
506
- <rect x="3" y="11" width="18" height="11" rx="2" />
507
- <path d="M7 11V7a5 5 0 0 1 10 0v4" />
508
- </svg>
509
- {t("EKYC_SECURE_DATA_NOTICE") || "Your data is encrypted and secure"}
510
- </div>
511
-
512
- </Card>
179
+ <div className="review-container">
180
+ <Card>
181
+ {/* ── Header ───────────────────────────────────────────────────── */}
182
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }}>
183
+ <CardHeader style={{ margin: 0 }}>{t("EKYC_REVIEW_APPLICATION")}</CardHeader>
184
+ <div style={{
185
+ background: "#F9FAFB", border: "0.5px solid #EAECF0",
186
+ borderRadius: "20px", padding: "4px 14px",
187
+ fontSize: "12px", color: "#667085",
188
+ }}>
189
+ {t("EKYC_K_NUMBER")}: <span style={{ color: "#0B0C0C", fontWeight: "600" }}>{activeKno}</span>
513
190
  </div>
514
191
  </div>
515
192
 
516
- {/* Toast notification */}
517
- {toast && (
518
- <Toast
519
- label={toast.message}
520
- error={toast.type === "error"}
521
- success={toast.type === "success"}
522
- onClose={() => setToast(null)}
193
+ {/* ── 1. Connection Details ────────────────────────────────────── */}
194
+ <CardSubHeader>{t("EKYC_CONNECTION_DETAILS")}</CardSubHeader>
195
+ <StatusTable style={{ marginTop: "20px", marginBottom: "30px" }}>
196
+ <Row label={t("EKYC_K_NUMBER")} text={checkForNA(connectionData.knumber)} />
197
+ <Row label={t("EKYC_CONSUMER_NAME")} text={checkForNA(connectionData.consumerName)} />
198
+ <Row label={t("EKYC_ADDRESS")} text={checkForNA(connectionData.address)} />
199
+ <Row label={t("EKYC_CONNECTION_TYPE")} text={checkForNA(connectionData.connectionType)} />
200
+ <Row label={t("EKYC_METER_NO")} text={checkForNA(connectionData.meterNumber)} />
201
+ <Row label={t("EKYC_MOBILE_NO")} text={checkForNA(connectionData.phoneNumber)} />
202
+ <Row label={t("EKYC_EMAIL")} text={checkForNA(connectionData.email)} />
203
+ <Row label={t("EKYC_STATUS_FLAG")} text={checkForNA(connectionData.statusflag)} />
204
+ <Row label={t("EKYC_STATUS")} text={checkForNA(connectionData.ekycStatus)} />
205
+ </StatusTable>
206
+
207
+ {/* ── 2. Address Details ──────────────────────────────────────── */}
208
+ <CardSubHeader>{t("EKYC_ADDRESS_DETAILS")}</CardSubHeader>
209
+ <StatusTable style={{ marginTop: "20px", marginBottom: "30px" }}>
210
+ <Row
211
+ label={t("EKYC_FULL_ADDRESS")}
212
+ text={checkForNA(addressData.fullAddress)}
213
+ actionButton={<ActionButton jumpTo={`${baseUrl}/address-details`} state={{ ...flowState, reviewData: searchData, edits }} />}
214
+ />
215
+ <Row label={t("EKYC_FLAT_HOUSE_NO")} text={checkForNA(addressData.flatHouseNumber)} />
216
+ <Row label={t("EKYC_BUILDING_TOWER")} text={checkForNA(addressData.buildingTower)} />
217
+ <Row label={t("EKYC_LANDMARK")} text={checkForNA(addressData.landmark)} />
218
+ <Row label={t("EKYC_PINCODE")} text={checkForNA(addressData.pinCode)} />
219
+ <Row label={t("EKYC_LOCALITY")} text={checkForNA(addressData.ward)} />
220
+ <Row label={t("EKYC_ASSEMBLY")} text={checkForNA(addressData.assembly)} />
221
+ <Row label={t("EKYC_GPS_VALID")} text={boolToYesNo(addressData.gpsValid, t)} />
222
+ <Row label={t("EKYC_LATITUDE")} text={checkForNA(addressData.latitude)} />
223
+ <Row label={t("EKYC_LONGITUDE")} text={checkForNA(addressData.longitude)} />
224
+ <Row label={t("EKYC_MOBILE_NO")} text={checkForNA(addressData.mobileNo)} />
225
+ <Row label={t("EKYC_WHATSAPP_NO")} text={checkForNA(addressData.whatsappNo)} />
226
+ <Row label={t("EKYC_EMAIL")} text={checkForNA(addressData.email)} />
227
+ <Row label={t("EKYC_NO_OF_PERSONS")} text={checkForNA(addressData.noOfPerson)} />
228
+ <Row label={t("EKYC_K_NUMBER")} text={checkForNA(addressData.knumber)} />
229
+ </StatusTable>
230
+
231
+ {/* ── 3. Property Info ────────────────────────────────────────── */}
232
+ <CardSubHeader>{t("EKYC_PROPERTY_INFO")}</CardSubHeader>
233
+ <StatusTable style={{ marginTop: "20px", marginBottom: "30px" }}>
234
+ <Row
235
+ label={t("EKYC_CONNECTION_CATEGORY")}
236
+ text={checkForNA(propertyData.connectionCategory)}
237
+ actionButton={<ActionButton jumpTo={`${baseUrl}/property-info`} state={{ ...flowState, reviewData: searchData, edits }} />}
238
+ />
239
+ {/* <Row label={t("EKYC_KNO")} text={checkForNA(propertyData.kno)} /> */}
240
+ <Row label={t("EKYC_PID_NUMBER")} text={checkForNA(propertyData.pidNumber)} />
241
+ <Row label={t("EKYC_TYPE_OF_CONNECTION")} text={checkForNA(propertyData.typeOfConnection)} />
242
+ <Row label={t("EKYC_USER_TYPE")} text={checkForNA(propertyData.userType)} />
243
+ <Row label={t("EKYC_FLOOR_COUNT")} text={checkForNA(propertyData.numberOfFloors)} />
244
+ <Row label={t("EKYC_TENANT_NAME")} text={checkForNA(propertyData.tenantName)} />
245
+ <Row label={t("EKYC_TENANT_MOBILE")} text={checkForNA(propertyData.tenantMobile)} />
246
+ <Row label={t("EKYC_STATUS")} text={checkForNA(propertyData.ekycStatus)} />
247
+ </StatusTable>
248
+
249
+ {/* ── 4. Meter Details ────────────────────────────────────────── */}
250
+ <CardSubHeader>{t("EKYC_METER_DETAILS")}</CardSubHeader>
251
+ <StatusTable style={{ marginTop: "20px", marginBottom: "30px" }}>
252
+ <Row
253
+ label={t("EKYC_METERED")}
254
+ text={boolToYesNo(meterData.metered, t)}
255
+ actionButton={<ActionButton jumpTo={`${baseUrl}/meter-details`} state={{ ...flowState, reviewData: searchData, edits }} />}
256
+ />
257
+ {/* <Row label={t("EKYC_KNO")} text={checkForNA(meterData.kno)} /> */}
258
+ <Row label={t("EKYC_METER_NO")} text={checkForNA(meterData.meterNumber)} />
259
+ <Row label={t("EKYC_METER_MAKE")} text={checkForNA(meterData.meterMake)} />
260
+ <Row label={t("EKYC_METER_LOCATION_ADDRESS")} text={checkForNA(meterData.meterLocationAddress)} />
261
+ <Row label={t("EKYC_METER_LATITUDE")} text={checkForNA(meterData.meterLatitude)} />
262
+ <Row label={t("EKYC_METER_LONGITUDE")} text={checkForNA(meterData.meterLongitude)} />
263
+ <Row label={t("EKYC_WORKING_STATUS")} text={boolToYesNo(meterData.workingStatus, t)} />
264
+ <Row label={t("EKYC_LAST_BILL_RAISED")} text={boolToYesNo(meterData.lastBillRaised, t)} />
265
+ <Row label={t("EKYC_SYSTEM_METER_ID")} text={checkForNA(meterData.systemMeterId)} />
266
+ </StatusTable>
267
+
268
+ {/* ── 5. Documents ────────────────────────────────────────────── */}
269
+ <CardSubHeader>{t("EKYC_DOCUMENTS")}</CardSubHeader>
270
+ <StatusTable style={{ marginTop: "20px", marginBottom: "30px" }}>
271
+ <Row
272
+ label={t("EKYC_DOOR_PHOTO")}
273
+ text={addressData.doorPhotoFilestoreId ? t("EKYC_PHOTO_AVAILABLE") : t("CS_NA")}
274
+ actionButton={addressData.doorPhotoFilestoreId && <GenericFileIcon style={{ cursor: "pointer" }} />}
275
+ />
276
+ <Row
277
+ label={t("EKYC_METER_PHOTO")}
278
+ text={meterData.meterPhotoFileStoreId ? t("EKYC_PHOTO_AVAILABLE") : t("CS_NA")}
279
+ actionButton={meterData.meterPhotoFileStoreId && <GenericFileIcon style={{ cursor: "pointer" }} />}
280
+ />
281
+ <Row
282
+ label={t("EKYC_BUILDING_IMAGE")}
283
+ text={propertyData.buildingImageFileStoreId ? t("EKYC_PHOTO_AVAILABLE") : t("CS_NA")}
284
+ actionButton={propertyData.buildingImageFileStoreId && <GenericFileIcon style={{ cursor: "pointer" }} />}
285
+ />
286
+ <Row
287
+ label={t("EKYC_PROPERTY_DOCUMENTS")}
288
+ text={propertyData.propertyDocumentFileStoreId ? t("EKYC_DOCS_AVAILABLE") : t("CS_NA")}
289
+ actionButton={propertyData.propertyDocumentFileStoreId && <GenericFileIcon style={{ cursor: "pointer" }} />}
523
290
  />
524
- )}
525
- </Fragment>
291
+ </StatusTable>
292
+
293
+ <CheckBox
294
+ id="agreeDeclaration"
295
+ name="agreeDeclaration"
296
+ label={t("EKYC_FINAL_DECLARATION")}
297
+ onChange={handleDeclaration}
298
+ checked={agree}
299
+ style={{ marginTop: "20px" }}
300
+ />
301
+ </Card>
302
+
303
+ <ActionBar style={{ position: "static", marginTop: "24px" }}>
304
+ <SubmitBar
305
+ label={t("EKYC_SUBMIT_APPLICATION")}
306
+ onSubmit={handleFinalSubmit}
307
+ disabled={!agree}
308
+ />
309
+ </ActionBar>
526
310
  </div>
527
311
  );
528
312
  };