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

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,6 +1,5 @@
1
- import React, { useState, useRef, Fragment } from "react";
1
+ import React, { useState, useRef, Fragment, useEffect } from "react";
2
2
  import {
3
- Header,
4
3
  Card,
5
4
  LabelFieldPair,
6
5
  CardLabel,
@@ -12,38 +11,226 @@ import {
12
11
  InfoBannerIcon,
13
12
  PropertyHouse,
14
13
  LocationIcon,
15
- RemoveableTag,
16
14
  HomeIcon,
17
15
  ConnectingCheckPoints,
18
16
  CheckPoint,
17
+ Dropdown,
18
+ Loader,
19
19
  } from "@djb25/digit-ui-react-components";
20
20
  import { useTranslation } from "react-i18next";
21
21
  import { useHistory, useLocation } from "react-router-dom";
22
22
 
23
+ // ─── Icons ────────────────────────────────────────────────────────────────────
24
+
25
+ const FlagIcon = ({ size = 20 }) => (
26
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
27
+ <path d="M14.4 6L13.6 4H5V21H7V14H12.6L13.4 16H20V6H14.4Z" fill="#0F6E56" />
28
+ </svg>
29
+ );
30
+
31
+ const IdCardIcon = ({ size = 20 }) => (
32
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
33
+ <path
34
+ 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"
35
+ fill="#185FA5"
36
+ />
37
+ </svg>
38
+ );
39
+
40
+ const CameraIcon = ({ size = 28 }) => (
41
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
42
+ <path
43
+ 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"
44
+ fill="#185FA5"
45
+ />
46
+ </svg>
47
+ );
48
+
49
+ const TargetIcon = ({ size = 20 }) => (
50
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
51
+ <path
52
+ 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"
53
+ fill="#185FA5"
54
+ />
55
+ </svg>
56
+ );
57
+
58
+ const PincodeIcon = ({ size = 18 }) => (
59
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none">
60
+ <path
61
+ 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"
62
+ fill="currentColor"
63
+ />
64
+ </svg>
65
+ );
66
+
67
+ const TrashIcon = ({ size = 16 }) => (
68
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="#D92D20" strokeWidth="2" strokeLinecap="round">
69
+ <polyline points="3 6 5 6 21 6" />
70
+ <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
71
+ <path d="M10 11v6M14 11v6" />
72
+ <path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2" />
73
+ </svg>
74
+ );
75
+
76
+ const CheckIcon = ({ size = 11, color = "#fff" }) => (
77
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="3" strokeLinecap="round">
78
+ <polyline points="20 6 9 17 4 12" />
79
+ </svg>
80
+ );
81
+
82
+ // ─── Reusable: Icon-prefixed input ────────────────────────────────────────────
83
+
84
+ const IconInput = ({ icon, topAligned = false, inputStyle = {}, ...props }) => (
85
+ <div style={{ position: "relative", width: "100%" }}>
86
+ <div style={{
87
+ position: "absolute",
88
+ left: "10px",
89
+ ...(topAligned ? { top: "14px" } : { top: "50%", transform: "translateY(-50%)" }),
90
+ zIndex: 1,
91
+ opacity: 0.45,
92
+ display: "flex",
93
+ pointerEvents: "none",
94
+ }}>
95
+ {icon}
96
+ </div>
97
+ <TextInput
98
+ textInputStyle={{ paddingLeft: "36px", paddingRight: "12px", ...inputStyle }}
99
+ {...props}
100
+ />
101
+ </div>
102
+ );
103
+
104
+ // ─── Reusable: Section heading with inline rule ───────────────────────────────
105
+
106
+ const SectionHead = ({ icon, label }) => (
107
+ <div style={{
108
+ display: "flex", alignItems: "center", gap: "8px",
109
+ marginBottom: "16px", marginTop: "4px",
110
+ }}>
111
+ <div style={{ opacity: 0.5, display: "flex" }}>{icon}</div>
112
+ <span style={{ fontSize: "15px", fontWeight: "600", color: "#0B0C0C", whiteSpace: "nowrap" }}>
113
+ {label}
114
+ </span>
115
+ <div style={{ flex: 1, height: "1px", background: "#EAECF0" }} />
116
+ </div>
117
+ );
118
+
119
+ // ─── Reusable: Admin info card ────────────────────────────────────────────────
120
+
121
+ const AdminCard = ({ bgColor, iconBg, icon, labelColor, label, value }) => (
122
+ <div style={{
123
+ backgroundColor: bgColor,
124
+ padding: "14px 16px",
125
+ borderRadius: "8px",
126
+ display: "flex",
127
+ alignItems: "center",
128
+ gap: "14px",
129
+ border: "0.5px solid #EAECF0",
130
+ }}>
131
+ <div style={{ backgroundColor: iconBg, padding: "8px", borderRadius: "8px", display: "flex", flexShrink: 0 }}>
132
+ {icon}
133
+ </div>
134
+ <div>
135
+ <div style={{ color: labelColor, fontSize: "10px", fontWeight: "600", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: "3px" }}>
136
+ {label}
137
+ </div>
138
+ <div style={{ fontSize: "14px", fontWeight: "600", color: "#101828" }}>{value}</div>
139
+ </div>
140
+ </div>
141
+ );
142
+
143
+ // ─── Main Component ───────────────────────────────────────────────────────────
144
+
23
145
  const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
24
146
  const { t } = useTranslation();
25
147
  const history = useHistory();
26
148
  const location = useLocation();
27
149
 
28
- // Use parent state if provided, otherwise fallback to location state or defaults
29
- const flowState = parentState ||
30
- location.state || {
31
- kNumber: "EKYC-1234567890",
32
- selectedOption: { code: "SELF", name: "EKYC_SELF" },
33
- connectionDetails: null,
34
- };
150
+ const flowState = parentState || location.state || {
151
+ kNumber: "EKYC-1234567890",
152
+ selectedOption: { code: "SELF", name: "EKYC_SELF" },
153
+ connectionDetails: null,
154
+ };
155
+
156
+ const addrDetails = flowState.connectionDetails?.addressDetails || {};
35
157
 
36
158
  const [addressType, setAddressType] = useState({ code: "AADHAAR", name: "EKYC_AADHAAR_ADDRESS" });
37
159
  const [correctAddress, setCorrectAddress] = useState({ code: "NO", name: "CORE_COMMON_NO" });
38
- const [fullAddress, setFullAddress] = useState("");
39
- const [flatNo, setFlatNo] = useState("");
40
- const [building, setBuilding] = useState("");
41
- const [landmark, setLandmark] = useState("");
42
- const [pincode, setPincode] = useState("");
160
+ const [fullAddress, setFullAddress] = useState(addrDetails.fullAddress || "");
161
+ const [flatNo, setFlatNo] = useState(addrDetails.flatHouseNumber || "");
162
+ const [building, setBuilding] = useState(addrDetails.buildingTower || "");
163
+ const [landmark, setLandmark] = useState(addrDetails.landmark || "");
164
+ const [pincode, setPincode] = useState(addrDetails.pinCode || "");
43
165
  const [doorPhoto, setDoorPhoto] = useState(null);
44
166
  const [isLocationFetching, setIsLocationFetching] = useState(false);
45
167
  const fileInputRef = useRef(null);
46
168
 
169
+ const tenantId = Digit.ULBService.getCurrentTenantId();
170
+ const { data: mdmsData, isLoading: isMdmsLoading } = Digit.Hooks.useCommonMDMS(
171
+ tenantId,
172
+ "egov-location",
173
+ ["TenantBoundary"]
174
+ );
175
+
176
+ const mdmsRes = mdmsData?.MdmsRes || mdmsData;
177
+ const adminHierarchy = mdmsRes?.["egov-location"]?.TenantBoundary?.find(h => h.hierarchyType.code === "ADMIN")
178
+ || mdmsRes?.["egov-location"]?.TenantBoundary?.[0];
179
+
180
+ const rootBoundary = adminHierarchy?.boundary;
181
+
182
+ const getAssemblies = (boundaries) => {
183
+ if (!boundaries) return [];
184
+ let assemblies = [];
185
+ const targetLabel = "assembly constituency";
186
+ for (const boundary of boundaries) {
187
+ const label = (boundary.label || boundary.name || "").toLowerCase().replace(/_/g, " ");
188
+ if (label === targetLabel || label === "assemblyconstituency") {
189
+ assemblies.push({ code: boundary.code, name: boundary.name, children: boundary.children });
190
+ }
191
+ if (boundary.children?.length) {
192
+ assemblies.push(...getAssemblies(boundary.children));
193
+ }
194
+ }
195
+ return assemblies;
196
+ };
197
+
198
+ const getBlocks = (children) => {
199
+ if (!children) return [];
200
+ let blocks = [];
201
+ const targetLabel = "block";
202
+ for (const child of children) {
203
+ const label = (child.label || child.name || "").toLowerCase().replace(/_/g, " ");
204
+ if (label === targetLabel) {
205
+ blocks.push({ code: child.code, name: child.name });
206
+ }
207
+ if (child.children?.length) {
208
+ blocks.push(...getBlocks(child.children));
209
+ }
210
+ }
211
+ return blocks;
212
+ };
213
+
214
+ const assemblies = getAssemblies(Array.isArray(rootBoundary) ? rootBoundary : rootBoundary ? [rootBoundary] : []);
215
+
216
+ const [assembly, setAssembly] = useState(addrDetails.assembly ? { name: addrDetails.assembly } : null);
217
+ const [ward, setWard] = useState(addrDetails.ward ? { name: addrDetails.ward } : null);
218
+
219
+ useEffect(() => {
220
+ if (mdmsRes && addrDetails.assembly && !assembly?.code) {
221
+ const foundAssembly = assemblies.find((a) => a.name === addrDetails.assembly || a.code === addrDetails.assembly);
222
+ if (foundAssembly) setAssembly(foundAssembly);
223
+ }
224
+ }, [mdmsRes, assemblies]);
225
+
226
+ const blocks = assembly ? getBlocks(assembly.children) : [];
227
+
228
+ useEffect(() => {
229
+ if (assembly && ward && !blocks.find((b) => b.name === ward.name)) {
230
+ setWard(null);
231
+ }
232
+ }, [assembly]);
233
+
47
234
  const addressOptions = [
48
235
  { code: "AADHAAR", name: "EKYC_AADHAAR_ADDRESS" },
49
236
  { code: "OLD", name: "EKYC_OLD_ADDRESS" },
@@ -55,32 +242,33 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
55
242
  ];
56
243
 
57
244
  const handleCompleteVerification = () => {
245
+ const payload = {
246
+ addressType,
247
+ fullAddress,
248
+ flatNo,
249
+ building,
250
+ landmark,
251
+ pincode,
252
+ doorPhoto,
253
+ assembly: assembly?.name,
254
+ ward: ward?.name
255
+ };
58
256
  if (onComplete) {
59
- onComplete({ addressType, fullAddress, flatNo, building, landmark, pincode, doorPhoto });
257
+ onComplete(payload);
60
258
  } else {
61
259
  const { kNumber, selectedOption, connectionDetails } = flowState;
62
260
  history.push("/digit-ui/employee/ekyc/property-info", {
63
- kNumber,
64
- selectedOption,
65
- connectionDetails,
66
- addressDetails: { addressType, fullAddress, flatNo, building, landmark, pincode, doorPhoto },
261
+ kNumber, selectedOption, connectionDetails, addressDetails: payload,
67
262
  });
68
263
  }
69
264
  };
70
265
 
71
266
  const handleCapture = (e) => {
72
267
  const file = e.target.files[0];
73
- if (file) {
74
- const reader = new FileReader();
75
- reader.onloadend = () => {
76
- setDoorPhoto(reader.result);
77
- };
78
- reader.readAsDataURL(file);
79
- }
80
- };
81
-
82
- const openGallery = () => {
83
- fileInputRef.current.click();
268
+ if (!file) return;
269
+ const reader = new FileReader();
270
+ reader.onloadend = () => setDoorPhoto(reader.result);
271
+ reader.readAsDataURL(file);
84
272
  };
85
273
 
86
274
  const removePhoto = () => {
@@ -93,98 +281,47 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
93
281
  alert(t("GEOLOCATION_NOT_SUPPORTED") || "Geolocation is not supported by your browser");
94
282
  return;
95
283
  }
96
-
97
284
  setIsLocationFetching(true);
98
285
  navigator.geolocation.getCurrentPosition(
99
- async (position) => {
100
- const { latitude, longitude } = position.coords;
286
+ async ({ coords: { latitude, longitude } }) => {
101
287
  try {
102
- const response = await fetch(
288
+ const res = await fetch(
103
289
  `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&zoom=18&addressdetails=1`
104
290
  );
105
- if (!response.ok) throw new Error("Failed to fetch address");
106
- const data = await response.json();
107
-
108
- if (data && data.address) {
109
- const addr = [
110
- data.address?.amenity,
111
- data.address?.road,
112
- data.address?.neighbourhood,
113
- data.address?.suburb,
114
- data.address?.city,
115
- data.address?.state,
116
- data.address?.postcode,
117
- ]
118
- .filter(Boolean)
119
- .join(", ");
120
-
121
- setFullAddress(addr || "");
122
- setPincode(data.address?.postcode || "");
123
- setLandmark(data.address?.suburb || data.address?.neighbourhood || "");
124
- setFlatNo(data.address?.amenity || "");
291
+ if (!res.ok) throw new Error("Geocode failed");
292
+ const data = await res.json();
293
+ if (data?.address) {
294
+ const a = data.address;
295
+ setFullAddress([a.amenity, a.road, a.neighbourhood, a.suburb, a.city, a.state, a.postcode].filter(Boolean).join(", "));
296
+ setPincode(a.postcode || "");
297
+ setLandmark(a.suburb || a.neighbourhood || "");
298
+ setFlatNo(a.amenity || "");
125
299
  }
126
- } catch (error) {
127
- console.error("Error reverse geocoding:", error);
300
+ } catch (err) {
301
+ console.error("Reverse geocode error:", err);
128
302
  } finally {
129
303
  setIsLocationFetching(false);
130
304
  }
131
305
  },
132
- (error) => {
133
- console.error("Error getting location:", error);
306
+ (err) => {
307
+ console.error("Geolocation error:", err);
134
308
  setIsLocationFetching(false);
135
- alert(t("LOCATION_FETCH_FAILED") || "Failed to fetch your current location. Please ensure location permissions are granted.");
309
+ alert(t("LOCATION_FETCH_FAILED") || "Failed to fetch location. Please grant location permissions.");
136
310
  },
137
311
  { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
138
312
  );
139
313
  };
140
314
 
141
- const FlagIcon = () => (
142
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
143
- <path d="M14.4 6L13.6 4H5V21H7V14H12.6L13.4 16H20V6H14.4Z" fill="#2E9E8F" />
144
- </svg>
145
- );
146
-
147
- const IdCardIcon = () => (
148
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
149
- <path
150
- 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"
151
- fill="#3D51B0"
152
- />
153
- </svg>
154
- );
155
-
156
- const CameraIcon = () => (
157
- <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
158
- <path
159
- 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"
160
- fill="#0068faff"
161
- />
162
- </svg>
163
- );
164
-
165
- const TargetIcon = () => (
166
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
167
- <path
168
- 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"
169
- fill="#0068faff"
170
- />
171
- </svg>
172
- );
315
+ const renderContent = () => (
316
+ <div style={{ animation: "fadeSlideIn 0.3s ease" }}>
173
317
 
174
- const PincodeIcon = () => (
175
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
176
- <path
177
- 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"
178
- fill="#0068faff"
318
+ {/* ── Address Type Toggle ── */}
319
+ <SectionHead
320
+ icon={<LocationIcon className="icon" styles={{ fill: "#0B0C0C", width: "16px", height: "16px" }} />}
321
+ label={t("EKYC_ADDRESS_DETAILS_HEADER") || "Address Details"}
179
322
  />
180
- </svg>
181
- );
182
323
 
183
- const renderContent = () => (
184
- <div style={{ animation: "fadeSlideIn 0.3s ease" }}>
185
- {isSection && <hr style={{ margin: "40px 0", border: "0", borderTop: "2px solid #EAECF0" }} />}
186
- <Header style={{ marginBottom: "24px" }}>{t("EKYC_ADDRESS_DETAILS_HEADER") || "Address Details"}</Header>
187
- <div style={{ marginBottom: "32px" }}>
324
+ <div style={{ marginBottom: "20px" }}>
188
325
  <RadioButtons
189
326
  options={addressOptions}
190
327
  optionsKey="name"
@@ -192,40 +329,40 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
192
329
  onSelect={setAddressType}
193
330
  t={t}
194
331
  innerStyles={{ display: "flex", alignItems: "center" }}
195
- style={{ display: "flex", gap: "50px", justifyContent: "flex-start" }}
332
+ style={{ display: "flex", gap: "40px" }}
196
333
  />
197
334
  </div>
198
335
 
336
+ {/* ── Aadhaar Address display ── */}
199
337
  {addressType.code === "AADHAAR" && (
200
- <div
201
- style={{
202
- backgroundColor: "#F9FAFB",
203
- padding: "16px",
204
- borderRadius: "12px",
205
- marginBottom: "24px",
206
- border: "1px solid #EAECF0",
207
- display: "flex",
208
- alignItems: "flex-start",
209
- gap: "12px",
210
- animation: "fadeSlideIn 0.3s ease",
211
- }}
212
- >
213
- <div style={{ backgroundColor: "#E7F4EE", padding: "8px", borderRadius: "8px" }}>
214
- <LocationIcon className="icon" styles={{ fill: "#2E9E8F", width: "20px", height: "20px" }} />
338
+ <div style={{
339
+ backgroundColor: "#E1F5EE",
340
+ border: "0.5px solid #5DCAA5",
341
+ borderRadius: "8px",
342
+ padding: "14px 16px",
343
+ display: "flex",
344
+ alignItems: "flex-start",
345
+ gap: "12px",
346
+ marginBottom: "20px",
347
+ animation: "fadeSlideIn 0.3s ease",
348
+ }}>
349
+ <div style={{ backgroundColor: "#9FE1CB", padding: "6px", borderRadius: "6px", display: "flex", flexShrink: 0 }}>
350
+ <LocationIcon className="icon" styles={{ fill: "#085041", width: "16px", height: "16px" }} />
215
351
  </div>
216
- <div style={{ fontSize: "16px", lineHeight: "1.6", color: "#344054", fontWeight: "500" }}>
217
- H.No. 123, Sector 15, Rohini
218
- <br />
219
- Delhi - 110085
352
+ <div style={{ fontSize: "14px", lineHeight: "1.6", color: "#04342C", fontWeight: "500" }}>
353
+ {addrDetails.fullAddress || "H.No. 123, Sector 15, Rohini, Delhi – 110085"}
220
354
  </div>
221
355
  </div>
222
356
  )}
223
357
 
358
+ {/* ── Old / Custom Address ── */}
224
359
  {addressType.code === "OLD" && (
225
360
  <div style={{ animation: "fadeSlideIn 0.3s ease" }}>
226
- <div style={{ marginBottom: "24px" }}>
227
- <CardLabel style={{ marginBottom: "12px", fontWeight: "600", color: "#344054" }}>
228
- {t("EKYC_ADDRESS_CORRECTION_PROMPT") || "Do you want to correct the address?"}
361
+
362
+ {/* Correction toggle */}
363
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "14px" }}>
364
+ <CardLabel style={{ fontWeight: "500", marginBottom: 0, fontSize: "13px", color: "#505A5F" }}>
365
+ {t("EKYC_ADDRESS_CORRECTION_PROMPT") || "Correct the address?"}
229
366
  </CardLabel>
230
367
  <RadioButtons
231
368
  options={yesNoOptions}
@@ -233,352 +370,372 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
233
370
  selectedOption={correctAddress}
234
371
  onSelect={setCorrectAddress}
235
372
  t={t}
236
- innerStyles={{ display: "flex", alignItems: "center" }}
237
- style={{ display: "flex", gap: "50px", justifyContent: "flex-start" }}
373
+ innerStyles={{ display: "flex", gap: "20px" }}
374
+ style={{ marginBottom: 0 }}
238
375
  />
239
376
  </div>
240
377
 
378
+ {/* Use Current Location */}
241
379
  <div
380
+ onClick={!isLocationFetching ? handleUseCurrentLocation : undefined}
242
381
  style={{
243
- border: "1px solid #D0D5DD",
244
- borderRadius: "12px",
245
- padding: "14px 16px",
382
+ border: "0.5px solid #D0D5DD",
383
+ borderRadius: "8px",
384
+ padding: "12px 16px",
246
385
  display: "flex",
247
386
  alignItems: "center",
248
387
  justifyContent: "space-between",
249
- marginBottom: "24px",
388
+ marginBottom: "20px",
250
389
  cursor: isLocationFetching ? "not-allowed" : "pointer",
251
- backgroundColor: isLocationFetching ? "#F2F4F7" : "#FFFFFF",
252
- transition: "all 0.2s ease",
390
+ backgroundColor: isLocationFetching ? "#F9FAFB" : "#fff",
391
+ transition: "background 0.15s",
253
392
  opacity: isLocationFetching ? 0.7 : 1,
254
- boxShadow: "0px 1px 2px rgba(16, 24, 40, 0.05)",
255
393
  }}
256
- onClick={!isLocationFetching ? handleUseCurrentLocation : undefined}
257
- onMouseOver={(e) => (!isLocationFetching ? (e.currentTarget.style.backgroundColor = "#F9FAFB") : null)}
258
- onMouseOut={(e) => (!isLocationFetching ? (e.currentTarget.style.backgroundColor = "#FFFFFF") : null)}
394
+ onMouseOver={(e) => { if (!isLocationFetching) e.currentTarget.style.background = "#F9FAFB"; }}
395
+ onMouseOut={(e) => { if (!isLocationFetching) e.currentTarget.style.background = "#fff"; }}
259
396
  >
260
397
  <div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
261
- <div style={{ backgroundColor: "#EEF4FF", padding: "8px", borderRadius: "8px" }}>
398
+ <div style={{ backgroundColor: "#E6F1FB", padding: "7px", borderRadius: "7px", display: "flex" }}>
262
399
  {isLocationFetching ? (
263
- <div
264
- className="location-loader"
265
- style={{
266
- width: "20px",
267
- height: "20px",
268
- border: "2px solid #0068faff",
269
- borderTopColor: "transparent",
270
- borderRadius: "50%",
271
- animation: "spin 1s linear infinite",
272
- }}
273
- ></div>
400
+ <div style={{
401
+ width: "18px", height: "18px", border: "2px solid #185FA5",
402
+ borderTopColor: "transparent", borderRadius: "50%",
403
+ animation: "spin 1s linear infinite",
404
+ }} />
274
405
  ) : (
275
- <TargetIcon />
406
+ <TargetIcon size={18} />
276
407
  )}
277
408
  </div>
278
- <span style={{ fontWeight: "600", color: "#344054" }}>
409
+ <span style={{ fontWeight: "500", fontSize: "14px", color: "#344054" }}>
279
410
  {isLocationFetching
280
- ? t("EKYC_FETCHING_LOCATION") || "Fetching Location..."
281
- : t("EKYC_USE_CURRENT_LOCATION") || "Use Current Location"}
411
+ ? t("EKYC_FETCHING_LOCATION") || "Fetching location..."
412
+ : t("EKYC_USE_CURRENT_LOCATION") || "Use current location"}
282
413
  </span>
283
414
  </div>
284
- {!isLocationFetching && <span style={{ fontSize: "20px", color: "#98A2B3" }}>›</span>}
415
+ {!isLocationFetching && (
416
+ <span style={{ fontSize: "18px", color: "#98A2B3", lineHeight: 1 }}>›</span>
417
+ )}
285
418
  </div>
286
419
 
287
- <LabelFieldPair>
288
- <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_FULL_ADDRESS") || "Full Address"}</CardLabel>
289
- <div className="field" style={{ position: "relative" }}>
290
- <div style={{ position: "absolute", left: "12px", top: "16px", zIndex: 1, opacity: 0.6 }}>
291
- <PropertyHouse styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
420
+ {/* Full Address (textarea-style) */}
421
+ <div style={{ marginBottom: "14px" }}>
422
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
423
+ {t("EKYC_FULL_ADDRESS") || "Full address"}
424
+ </div>
425
+ <IconInput
426
+ icon={<PropertyHouse styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
427
+ topAligned
428
+ value={fullAddress}
429
+ onChange={(e) => setFullAddress(e.target.value)}
430
+ placeholder={t("EKYC_ENTER_FULL_ADDRESS") || "Enter full address"}
431
+ inputStyle={{ minHeight: "72px" }}
432
+ />
433
+ </div>
434
+
435
+ {/* Flat + Building */}
436
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "14px" }}>
437
+ <div>
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."}
292
440
  </div>
293
- <TextInput
294
- value={fullAddress}
295
- onChange={(e) => setFullAddress(e.target.value)}
296
- placeholder={t("EKYC_ENTER_FULL_ADDRESS") || "Enter Full Address"}
297
- textInputStyle={{ paddingLeft: "40px", minHeight: "80px" }}
441
+ <IconInput
442
+ icon={<PropertyHouse styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
443
+ value={flatNo}
444
+ onChange={(e) => setFlatNo(e.target.value)}
445
+ placeholder={t("EKYC_ENTER_FLAT_NO") || "e.g. 45-B"}
298
446
  />
299
447
  </div>
300
- </LabelFieldPair>
301
-
302
- <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "24px" }}>
303
- <LabelFieldPair>
304
- <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_FLAT_HOUSE_NUMBER") || "Flat/House Number"}</CardLabel>
305
- <div className="field" style={{ position: "relative" }}>
306
- <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
307
- <PropertyHouse styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
308
- </div>
309
- <TextInput
310
- value={flatNo}
311
- onChange={(e) => setFlatNo(e.target.value)}
312
- placeholder={t("EKYC_ENTER_FLAT_NO") || "e.g. 45-B"}
313
- textInputStyle={{ paddingLeft: "40px" }}
314
- />
315
- </div>
316
- </LabelFieldPair>
317
- <LabelFieldPair>
318
- <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_BUILDING_TOWER") || "Building/Tower"}</CardLabel>
319
- <div className="field" style={{ position: "relative" }}>
320
- <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
321
- <PropertyHouse styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
322
- </div>
323
- <TextInput
324
- value={building}
325
- onChange={(e) => setBuilding(e.target.value)}
326
- placeholder={t("EKYC_ENTER_BUILDING") || "e.g. Tower 4"}
327
- textInputStyle={{ paddingLeft: "40px" }}
328
- />
448
+ <div>
449
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
450
+ {t("EKYC_BUILDING_TOWER") || "Building / Tower"}
329
451
  </div>
330
- </LabelFieldPair>
452
+ <IconInput
453
+ icon={<PropertyHouse styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
454
+ value={building}
455
+ onChange={(e) => setBuilding(e.target.value)}
456
+ placeholder={t("EKYC_ENTER_BUILDING") || "e.g. Tower 4"}
457
+ />
458
+ </div>
331
459
  </div>
332
460
 
333
- <LabelFieldPair>
334
- <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_LANDMARK") || "Landmark"}</CardLabel>
335
- <div className="field" style={{ position: "relative" }}>
336
- <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
337
- <LocationIcon className="icon" styles={{ fill: "#0068faff", width: "20px", height: "20px" }} />
461
+ {/* Landmark + Pincode */}
462
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "4px" }}>
463
+ <div>
464
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
465
+ {t("EKYC_LANDMARK") || "Landmark"}
338
466
  </div>
339
- <TextInput
467
+ <IconInput
468
+ icon={<LocationIcon className="icon" styles={{ fill: "#0068fa", width: "15px", height: "15px" }} />}
340
469
  value={landmark}
341
470
  onChange={(e) => setLandmark(e.target.value)}
342
471
  placeholder={t("EKYC_ENTER_LANDMARK") || "Near Central Park"}
343
- textInputStyle={{ paddingLeft: "40px" }}
344
472
  />
345
473
  </div>
346
- </LabelFieldPair>
347
-
348
- <LabelFieldPair>
349
- <CardLabel style={{ fontWeight: "600" }}>{t("EKYC_PINCODE") || "Pincode"}</CardLabel>
350
- <div className="field" style={{ position: "relative" }}>
351
- <div style={{ position: "absolute", left: "12px", top: "50%", transform: "translateY(-50%)", zIndex: 1, opacity: 0.6 }}>
352
- <PincodeIcon />
474
+ <div>
475
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
476
+ {t("EKYC_PINCODE") || "Pincode"}
353
477
  </div>
354
- <TextInput
478
+ <IconInput
479
+ icon={<PincodeIcon size={15} />}
355
480
  value={pincode}
356
- onChange={(e) => setPincode(e.target.value)}
481
+ onChange={(e) => { if (/^\d*$/.test(e.target.value)) setPincode(e.target.value); }}
357
482
  placeholder={t("EKYC_ENTER_PINCODE") || "6-digit pincode"}
358
- textInputStyle={{ paddingLeft: "40px" }}
359
483
  maxLength={6}
360
484
  />
361
485
  </div>
362
- </LabelFieldPair>
486
+ </div>
363
487
  </div>
364
488
  )}
365
489
 
366
- <hr style={{ margin: "32px 0", border: "0", borderTop: "1px solid #EAECF0" }} />
490
+ <hr style={{ margin: "24px 0", border: 0, borderTop: "1px solid #EAECF0" }} />
367
491
 
368
- <div style={{ display: "flex", alignItems: "center", gap: "10px", marginBottom: "20px" }}>
369
- <div style={{ backgroundColor: "#EEF4FF", padding: "8px", borderRadius: "8px" }}>
370
- <PropertyHouse styles={{ fill: "#0068faff", width: "24px", height: "24px" }} />
371
- </div>
372
- <CardHeader style={{ margin: 0, fontSize: "20px" }}>{t("EKYC_ADMINISTRATIVE_DIVISION") || "Administrative Division"}</CardHeader>
373
- </div>
492
+ {/* ── Administrative Division ── */}
493
+ <SectionHead
494
+ icon={<PropertyHouse styles={{ fill: "#0B0C0C", width: "16px", height: "16px" }} />}
495
+ label={t("EKYC_ADMINISTRATIVE_DIVISION") || "Administrative Division"}
496
+ />
374
497
 
375
- <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "24px", marginBottom: "32px" }}>
376
- <div
377
- style={{
378
- backgroundColor: "#F9FAFB",
379
- padding: "16px",
380
- borderRadius: "12px",
381
- display: "flex",
382
- alignItems: "center",
383
- gap: "16px",
384
- border: "1px solid #EAECF0",
385
- }}
386
- >
387
- <div style={{ backgroundColor: "#E7F4EE", padding: "10px", borderRadius: "10px", display: "flex" }}>
388
- <FlagIcon />
389
- </div>
498
+ {isMdmsLoading ? <Loader /> : (
499
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "24px" }}>
390
500
  <div>
391
- <div style={{ color: "#2E9E8F", fontSize: "12px", fontWeight: "700", textTransform: "uppercase", letterSpacing: "0.5px" }}>
392
- {t("EKYC_ASSEMBLY") || "ASSEMBLY"}
501
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
502
+ {t("EKYC_ASSEMBLY") || "Assembly Constituency"}
393
503
  </div>
394
- <div style={{ fontSize: "15px", fontWeight: "700", color: "#101828", marginTop: "2px" }}>AC-12 Chandni Chowk</div>
395
- </div>
396
- </div>
397
- <div
398
- style={{
399
- backgroundColor: "#F9FAFB",
400
- padding: "16px",
401
- borderRadius: "12px",
402
- display: "flex",
403
- alignItems: "center",
404
- gap: "16px",
405
- border: "1px solid #EAECF0",
406
- }}
407
- >
408
- <div style={{ backgroundColor: "#EEF4FF", padding: "10px", borderRadius: "10px", display: "flex" }}>
409
- <IdCardIcon />
504
+ <Dropdown
505
+ option={assemblies}
506
+ optionKey="name"
507
+ selected={assembly}
508
+ select={(val) => { setAssembly(val); setWard(null); }}
509
+ t={t}
510
+ />
410
511
  </div>
411
512
  <div>
412
- <div style={{ color: "#0068faff", fontSize: "12px", fontWeight: "700", textTransform: "uppercase", letterSpacing: "0.5px" }}>
413
- {t("EKYC_WARD") || "WARD"}
513
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
514
+ {t("EKYC_WARD") || "Block"}
414
515
  </div>
415
- <div style={{ fontSize: "15px", fontWeight: "700", color: "#101828", marginTop: "2px" }}>WARD-45 Civil Lines</div>
516
+ <Dropdown
517
+ option={blocks}
518
+ optionKey="name"
519
+ selected={ward}
520
+ select={setWard}
521
+ disabled={!assembly}
522
+ t={t}
523
+ />
416
524
  </div>
417
525
  </div>
418
- </div>
526
+ )}
527
+
528
+ <hr style={{ margin: "24px 0", border: 0, borderTop: "1px solid #EAECF0" }} />
529
+
530
+ {/* ── Door Photo ── */}
531
+ <SectionHead
532
+ icon={<CameraIcon size={16} />}
533
+ label={t("EKYC_DOOR_PHOTO_HEADER") || "Door photo with GPS stamp"}
534
+ />
419
535
 
420
- <CardHeader style={{ fontSize: "18px", color: "#101828", marginBottom: "4px" }}>
421
- {t("EKYC_DOOR_PHOTO_HEADER") || "Door Photo with GPS Stamp"}
422
- </CardHeader>
423
- <div style={{ color: "#667085", fontSize: "14px", marginBottom: "16px" }}>
536
+ <div style={{ fontSize: "12px", color: "#667085", marginBottom: "12px" }}>
424
537
  {t("EKYC_REQUIRED_FOR_VERIFICATION") || "Required for verification"}
425
538
  </div>
426
539
 
427
- <div
428
- style={{
429
- backgroundColor: "#FFFAEB",
430
- padding: "14px",
431
- borderRadius: "12px",
432
- display: "flex",
433
- alignItems: "center",
434
- gap: "12px",
435
- marginBottom: "20px",
436
- border: "1px solid #FEDF89",
437
- }}
438
- >
439
- <InfoBannerIcon fill="#B54708" />
540
+ {/* Warning banner */}
541
+ <div style={{
542
+ backgroundColor: "#FFFAEB",
543
+ border: "0.5px solid #FEDF89",
544
+ borderRadius: "8px",
545
+ padding: "12px 14px",
546
+ display: "flex",
547
+ alignItems: "flex-start",
548
+ gap: "10px",
549
+ marginBottom: "16px",
550
+ }}>
551
+ <div style={{ flexShrink: 0, marginTop: "1px" }}>
552
+ <InfoBannerIcon fill="#B54708" />
553
+ </div>
440
554
  <div>
441
- <div style={{ fontWeight: "700", color: "#B54708", fontSize: "14px" }}>{t("EKYC_IMPORTANT") || "Important"}</div>
442
- <div style={{ fontSize: "13px", color: "#B54708", marginTop: "2px" }}>
555
+ <div style={{ fontWeight: "600", color: "#B54708", fontSize: "13px", marginBottom: "2px" }}>
556
+ {t("EKYC_IMPORTANT") || "Important"}
557
+ </div>
558
+ <div style={{ fontSize: "12px", color: "#92400E" }}>
443
559
  {t("EKYC_CAPTURE_LIVE_CAMERA") || "Capture with live camera for GPS metadata"}
444
560
  </div>
445
561
  </div>
446
562
  </div>
447
563
 
564
+ {/* Drop zone */}
565
+ <input
566
+ type="file"
567
+ ref={fileInputRef}
568
+ onChange={handleCapture}
569
+ accept="image/*"
570
+ style={{ display: "none" }}
571
+ />
448
572
  <div
573
+ onClick={!doorPhoto ? () => fileInputRef.current.click() : undefined}
574
+ onMouseOver={(e) => { if (!doorPhoto) e.currentTarget.style.borderColor = "#185FA5"; }}
575
+ onMouseOut={(e) => { if (!doorPhoto) e.currentTarget.style.borderColor = "#D0D5DD"; }}
449
576
  style={{
450
- border: "2px dashed #D0D5DD",
451
- borderRadius: "16px",
452
- padding: doorPhoto ? "12px" : "40px 24px",
453
- textAlign: "center",
454
- cursor: "pointer",
455
- position: "relative",
456
- overflow: "hidden",
457
- minHeight: "180px",
577
+ border: "1.5px dashed #D0D5DD",
578
+ borderRadius: "10px",
579
+ minHeight: "160px",
458
580
  display: "flex",
459
581
  flexDirection: "column",
460
582
  alignItems: "center",
461
583
  justifyContent: "center",
462
584
  backgroundColor: "#F9FAFB",
463
- transition: "all 0.2s ease",
464
- boxShadow: "inset 0px 2px 4px rgba(0, 0, 0, 0.02)",
585
+ cursor: doorPhoto ? "default" : "pointer",
586
+ overflow: "hidden",
587
+ transition: "border-color 0.15s",
588
+ position: "relative",
589
+ padding: doorPhoto ? "0" : "32px 24px",
465
590
  }}
466
- onClick={!doorPhoto ? openGallery : undefined}
467
- onMouseOver={(e) => (!doorPhoto ? (e.currentTarget.style.borderColor = "#0068faff") : null)}
468
- onMouseOut={(e) => (!doorPhoto ? (e.currentTarget.style.borderColor = "#D0D5DD") : null)}
469
591
  >
470
- <input type="file" ref={fileInputRef} onChange={handleCapture} accept="image/*" style={{ display: "none" }} />
471
592
  {!doorPhoto ? (
472
593
  <>
473
- <div
474
- style={{
475
- backgroundColor: "#FFFFFF",
476
- width: "64px",
477
- height: "64px",
478
- borderRadius: "50%",
479
- display: "flex",
480
- alignItems: "center",
481
- justifyContent: "center",
482
- margin: "0 auto 16px",
483
- boxShadow: "0px 1px 3px rgba(16, 24, 40, 0.1)",
484
- }}
485
- >
486
- <CameraIcon />
594
+ <div style={{
595
+ width: "52px", height: "52px", borderRadius: "50%",
596
+ background: "#E6F1FB", display: "flex",
597
+ alignItems: "center", justifyContent: "center", marginBottom: "12px",
598
+ }}>
599
+ <CameraIcon size={26} />
600
+ </div>
601
+ <div style={{ fontWeight: "600", fontSize: "14px", color: "#101828", marginBottom: "4px" }}>
602
+ {t("EKYC_TAP_TO_CAPTURE") || "Tap to capture"}
487
603
  </div>
488
- <div style={{ fontWeight: "700", fontSize: "16px", marginBottom: "4px", color: "#101828" }}>
489
- {t("EKYC_TAP_TO_CAPTURE") || "Tap to Capture"}
604
+ <div style={{ fontSize: "12px", color: "#667085" }}>
605
+ {t("EKYC_CAPTURE_DOOR_IMAGE") || "Capture door image"}
490
606
  </div>
491
- <div style={{ color: "#667085", fontSize: "14px" }}>{t("EKYC_CAPTURE_DOOR_IMAGE") || "Capture Door Image"}</div>
492
607
  </>
493
608
  ) : (
494
- <div style={{ position: "relative", width: "100%", height: "100%", display: "flex", justifyContent: "center" }}>
609
+ <>
495
610
  <img
496
611
  src={doorPhoto}
497
612
  alt="Door"
498
- style={{ width: "100%", maxHeight: "300px", objectFit: "cover", borderRadius: "12px", display: "block" }}
613
+ style={{ width: "100%", maxHeight: "280px", objectFit: "cover", display: "block" }}
499
614
  />
500
- <div style={{ position: "absolute", top: "12px", right: "12px" }}>
501
- <button
502
- onClick={(e) => {
503
- e.stopPropagation();
504
- removePhoto();
505
- }}
506
- style={{
507
- background: "#FFFFFF",
508
- border: "1px solid #EAECF0",
509
- borderRadius: "8px",
510
- padding: "8px",
511
- display: "flex",
512
- boxShadow: "0px 1px 2px rgba(16, 24, 40, 0.05)",
513
- cursor: "pointer",
514
- }}
515
- >
516
- <RemoveableTag text="" onClick={() => {}} extraStyles={{ tagStyles: { margin: 0, padding: 0 } }} />
517
- </button>
518
- </div>
519
- </div>
615
+ <button
616
+ onClick={(e) => { e.stopPropagation(); removePhoto(); }}
617
+ style={{
618
+ position: "absolute", top: "10px", right: "10px",
619
+ background: "#fff", border: "0.5px solid #EAECF0",
620
+ borderRadius: "7px", padding: "6px 10px",
621
+ display: "flex", alignItems: "center", gap: "5px",
622
+ cursor: "pointer", fontSize: "12px", color: "#D92D20", fontWeight: "500",
623
+ }}
624
+ >
625
+ <TrashIcon size={13} /> {t("EKYC_REMOVE") || "Remove"}
626
+ </button>
627
+ </>
520
628
  )}
521
629
  </div>
522
630
 
523
- <ActionBar>
524
- <SubmitBar
525
- label={
526
- isSection
527
- ? t("EKYC_COMPLETE_VERIFICATION_AND_PROCEED") || "Complete & Proceed"
528
- : t("EKYC_COMPLETE_VERIFICATION") || "Complete Verification"
529
- }
530
- onSubmit={handleCompleteVerification}
531
- />
532
- </ActionBar>
631
+ {/* Submit */}
632
+ {isSection ? (
633
+ <div style={{ marginTop: "24px" }}>
634
+ <SubmitBar
635
+ label={t("EKYC_COMPLETE_VERIFICATION_AND_PROCEED") || "Complete & Proceed"}
636
+ onSubmit={handleCompleteVerification}
637
+ />
638
+ </div>
639
+ ) : (
640
+ <ActionBar>
641
+ <SubmitBar
642
+ label={t("EKYC_COMPLETE_VERIFICATION") || "Complete Verification"}
643
+ onSubmit={handleCompleteVerification}
644
+ />
645
+ </ActionBar>
646
+ )}
647
+
648
+ {/* Secure notice */}
649
+ <div style={{
650
+ display: "flex", alignItems: "center", justifyContent: "center",
651
+ gap: "5px", marginTop: "16px",
652
+ fontSize: "11px", color: "#98A2B3",
653
+ }}>
654
+ <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
655
+ <rect x="3" y="11" width="18" height="11" rx="2" />
656
+ <path d="M7 11V7a5 5 0 0 1 10 0v4" />
657
+ </svg>
658
+ {t("EKYC_SECURE_DATA_NOTICE") || "Your data is encrypted and secure"}
659
+ </div>
533
660
  </div>
534
661
  );
535
662
 
536
- if (isSection) return renderContent();
663
+ // ── When rendered as inline section inside AadhaarVerification ──
664
+ if (isSection) {
665
+ return (
666
+ <Fragment>
667
+ <hr style={{ margin: "32px 0", border: 0, borderTop: "2px solid #EAECF0" }} />
668
+ {renderContent()}
669
+ </Fragment>
670
+ );
671
+ }
537
672
 
673
+ // ── When rendered as a standalone page ──
538
674
  return (
539
675
  <div className="inbox-container">
540
676
  <style>{`
541
- @keyframes spin { to { transform: rotate(360deg); } }
542
- @keyframes fadeSlideIn { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
543
- `}</style>
677
+ @keyframes spin { to { transform: rotate(360deg); } }
678
+ @keyframes fadeSlideIn { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
679
+ `}</style>
544
680
 
681
+ {/* Sidebar */}
545
682
  <div className="filters-container">
546
- {/* Sidebar Title Card */}
547
- <Card
548
- className="sidebar-title-card"
549
- style={{ display: "flex", alignItems: "center", padding: "16px", marginBottom: "16px", borderRadius: "4px" }}
550
- >
551
- <div className="icon-container" style={{ color: "#0068faff", marginRight: "12px" }}>
552
- <HomeIcon style={{ width: "24px", height: "24px" }} />
683
+ <Card style={{ display: "flex", alignItems: "center", padding: "12px 16px", marginBottom: "12px", borderRadius: "8px" }}>
684
+ <div style={{ color: "#185FA5", marginRight: "10px", display: "flex" }}>
685
+ <HomeIcon style={{ width: "20px", height: "20px" }} />
686
+ </div>
687
+ <div style={{ fontWeight: "600", fontSize: "15px", color: "#0B0C0C" }}>
688
+ {t("EKYC_PROCESS") || "eKYC Process"}
553
689
  </div>
554
- <div style={{ fontWeight: "700", fontSize: "18px", color: "#0B0C0C" }}>{t("EKYC_PROCESS")}</div>
555
690
  </Card>
556
691
 
557
- {/* Progress Steps Sidebar */}
558
- <div
559
- style={{
560
- backgroundColor: "#FFFFFF",
561
- padding: "16px",
562
- borderRadius: "8px",
563
- border: "1px solid #EAECF0",
564
- boxShadow: "0 2px 4px rgba(0,0,0,0.02)",
565
- }}
566
- >
567
- <ConnectingCheckPoints>
568
- <CheckPoint label={t("EKYC_STEP_AADHAAR") || "Aadhaar"} isCompleted={true} />
569
- <CheckPoint label={t("EKYC_STEP_ADDRESS") || "Address"} isCompleted={true} />
570
- <CheckPoint label={t("EKYC_STEP_PROPERTY") || "Property"} isCompleted={false} />
571
- <CheckPoint label={t("EKYC_STEP_REVIEW") || "Review"} />
572
- </ConnectingCheckPoints>
692
+ <div style={{ background: "#fff", padding: "16px 14px", borderRadius: "8px", border: "1px solid #EAECF0" }}>
693
+ {[
694
+ { label: t("EKYC_STEP_AADHAAR") || "Aadhaar", done: true, active: false },
695
+ { label: t("EKYC_STEP_ADDRESS") || "Address", done: false, active: true },
696
+ { label: t("EKYC_STEP_PROPERTY") || "Property", done: false, active: false },
697
+ { label: t("EKYC_STEP_REVIEW") || "Review", done: false, active: false },
698
+ ].map((step, i) => (
699
+ <div key={i} style={{ display: "flex", gap: "10px", alignItems: "flex-start", position: "relative", paddingBottom: i < 3 ? "18px" : 0 }}>
700
+ {i < 3 && (
701
+ <div style={{ position: "absolute", left: "10px", top: "22px", width: "1px", height: "calc(100% - 10px)", background: "#EAECF0" }} />
702
+ )}
703
+ <div style={{
704
+ width: "20px", height: "20px", borderRadius: "50%", flexShrink: 0, marginTop: "1px",
705
+ border: step.done ? "none" : step.active ? "1.5px solid #185FA5" : "1.5px solid #D0D5DD",
706
+ background: step.done ? "#0F6E56" : step.active ? "#E6F1FB" : "#fff",
707
+ display: "flex", alignItems: "center", justifyContent: "center",
708
+ fontSize: "10px", fontWeight: "500",
709
+ color: step.done ? "#fff" : step.active ? "#185FA5" : "#98A2B3",
710
+ }}>
711
+ {step.done ? <CheckIcon size={11} color="#fff" /> : i + 1}
712
+ </div>
713
+ <div style={{
714
+ fontSize: "12px", paddingTop: "2px",
715
+ color: step.done ? "#0F6E56" : step.active ? "#0B0C0C" : "#667085",
716
+ fontWeight: step.done || step.active ? "600" : "400",
717
+ }}>
718
+ {step.label}
719
+ </div>
720
+ </div>
721
+ ))}
573
722
  </div>
574
723
  </div>
575
724
 
725
+ {/* Main */}
576
726
  <div style={{ flex: 1, marginLeft: "16px" }}>
577
727
  <Card>
578
- <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "24px" }}>
579
- <Header>{t("EKYC_ADDRESS_DETAILS_HEADER") || "Address Details"}</Header>
580
- <div style={{ fontSize: "14px", fontWeight: "700", color: "#505A5F" }}>
581
- {t("EKYC_K_NUMBER")}: <span style={{ color: "#0B0C0C" }}>{flowState?.kNumber}</span>
728
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }}>
729
+ <CardHeader style={{ margin: 0, fontSize: "18px" }}>
730
+ {t("EKYC_ADDRESS_DETAILS_HEADER") || "Address Details"}
731
+ </CardHeader>
732
+ <div style={{
733
+ background: "#F9FAFB", border: "0.5px solid #EAECF0",
734
+ borderRadius: "20px", padding: "4px 14px",
735
+ fontSize: "12px", color: "#667085",
736
+ }}>
737
+ {t("EKYC_K_NUMBER") || "K Number"}:{" "}
738
+ <span style={{ color: "#0B0C0C", fontWeight: "600" }}>{flowState?.kNumber}</span>
582
739
  </div>
583
740
  </div>
584
741
  {renderContent()}
@@ -588,4 +745,4 @@ const AddressDetails = ({ isSection = false, onComplete, parentState }) => {
588
745
  );
589
746
  };
590
747
 
591
- export default AddressDetails;
748
+ export default AddressDetails;