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

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,401 +1,494 @@
1
- import React, { useState, useRef } from "react";
2
- import { Header, Card, LabelFieldPair, CardLabel, TextInput, SubmitBar, CardHeader, ActionBar, Dropdown, PropertyHouse, InfoBannerIcon, RemoveableTag, HomeIcon, ConnectingCheckPoints, CheckPoint } from "@djb25/digit-ui-react-components";
1
+ import React, { useState, useRef, Fragment } from "react";
2
+ import {
3
+ Card,
4
+ CardLabel,
5
+ TextInput,
6
+ SubmitBar,
7
+ CardHeader,
8
+ ActionBar,
9
+ Dropdown,
10
+ InfoBannerIcon,
11
+ HomeIcon,
12
+ } from "@djb25/digit-ui-react-components";
3
13
  import { useTranslation } from "react-i18next";
4
14
  import { useHistory, useLocation } from "react-router-dom";
5
15
 
16
+ // ─── Icons ────────────────────────────────────────────────────────────────────
17
+
18
+ const CheckIcon = ({ size = 11, color = "#fff" }) => (
19
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="3" strokeLinecap="round">
20
+ <polyline points="20 6 9 17 4 12" />
21
+ </svg>
22
+ );
23
+
24
+ const BuildingIcon = ({ size = 16 }) => (
25
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
26
+ <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" />
27
+ </svg>
28
+ );
29
+
30
+ const BriefcaseIcon = ({ size = 16 }) => (
31
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
32
+ <path d="M20 6H16V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-2 .89-2 2v13c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6-2v2h-4V4h4z" />
33
+ </svg>
34
+ );
35
+
36
+ const DocumentIcon = ({ size = 16 }) => (
37
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
38
+ <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" />
39
+ </svg>
40
+ );
41
+
42
+ const CameraIcon = ({ size = 24 }) => (
43
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor">
44
+ <path d="M9 2L7.17 4H4C2.9 4 2 4.9 2 6v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L13 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
45
+ </svg>
46
+ );
47
+
48
+ const TrashIcon = ({ size = 14 }) => (
49
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="#D92D20" strokeWidth="2" strokeLinecap="round">
50
+ <polyline points="3 6 5 6 21 6" />
51
+ <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
52
+ <path d="M10 11v6M14 11v6" />
53
+ <path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2" />
54
+ </svg>
55
+ );
56
+
57
+ const PidIcon = ({ size = 16 }) => (
58
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
59
+ <rect x="3" y="4" width="18" height="16" rx="2" />
60
+ <path d="M7 8h5M7 12h8M7 16h4" />
61
+ </svg>
62
+ );
63
+
64
+ // ─── Reusable: Section heading with inline rule ───────────────────────────────
65
+
66
+ const SectionHead = ({ icon, label }) => (
67
+ <div style={{
68
+ display: "flex", alignItems: "center", gap: "8px",
69
+ marginBottom: "16px", marginTop: "4px",
70
+ }}>
71
+ <div style={{ opacity: 0.5, display: "flex" }}>{icon}</div>
72
+ <span style={{ fontSize: "15px", fontWeight: "600", color: "#0B0C0C", whiteSpace: "nowrap" }}>
73
+ {label}
74
+ </span>
75
+ <div style={{ flex: 1, height: "1px", background: "#EAECF0" }} />
76
+ </div>
77
+ );
78
+
79
+ // ─── Reusable: Icon-prefixed input ────────────────────────────────────────────
80
+
81
+ const IconInput = ({ icon, ...props }) => (
82
+ <div style={{ position: "relative", width: "100%" }}>
83
+ <div style={{
84
+ position: "absolute", left: "10px",
85
+ top: "50%", transform: "translateY(-50%)",
86
+ zIndex: 1, opacity: 0.45, display: "flex", pointerEvents: "none",
87
+ }}>
88
+ {icon}
89
+ </div>
90
+ <TextInput textInputStyle={{ paddingLeft: "36px", paddingRight: "12px" }} {...props} />
91
+ </div>
92
+ );
93
+
94
+ // ─── Main Component ───────────────────────────────────────────────────────────
95
+
6
96
  const PropertyInfo = () => {
7
97
  const { t } = useTranslation();
8
98
  const history = useHistory();
9
99
  const location = useLocation();
10
100
 
11
- const { kNumber } = location.state || { kNumber: "EKYC-1234567890" };
101
+ const flowState = location.state || { kNumber: "EKYC-1234567890" };
102
+ const { kNumber } = flowState;
103
+
12
104
  const tenantId = Digit.ULBService.getCurrentTenantId();
13
- const { data: dataV0, isLoading: isLoadingV0 } = Digit.Hooks.ekyc.useGetPropertyType(tenantId);
14
- const { data, isLoading } = Digit.Hooks.ekyc.useGetConnectionTypeV2(tenantId);
15
- const { data: dataV1, isLoading: isLoadingV1 } = Digit.Hooks.ekyc.useGetUserType(tenantId);
16
- const { data: dataV2, isLoading: isLoadingV2 } = Digit.Hooks.ekyc.useGetFloorCount(tenantId);
105
+ const { data: dataV0 } = Digit.Hooks.ekyc.useGetPropertyType(tenantId);
106
+ const { data: dataConn } = Digit.Hooks.ekyc.useGetConnectionTypeV2(tenantId);
107
+ const { data: dataV1 } = Digit.Hooks.ekyc.useGetUserType(tenantId);
108
+ const { data: dataV2 } = Digit.Hooks.ekyc.useGetFloorCount(tenantId);
17
109
 
18
110
  const [ownerType, setOwnerType] = useState("OWNER");
19
111
  const [pidNumber, setPidNumber] = useState("");
20
- const [connectionType, setConnectionType] = useState(null);
21
112
  const [connectionCategory, setConnectionCategory] = useState(null);
113
+ const [connectionType, setConnectionType] = useState(null);
22
114
  const [userType, setUserType] = useState(null);
23
115
  const [noOfFloors, setNoOfFloors] = useState(null);
24
116
  const [propertyDocument, setPropertyDocument] = useState(null);
25
117
  const [buildingPhoto, setBuildingPhoto] = useState(null);
118
+
26
119
  const fileRef = useRef(null);
27
120
  const cameraRef = useRef(null);
28
121
 
29
122
  const handleSaveAndContinue = () => {
30
123
  history.push("/digit-ui/employee/ekyc/review", {
31
- ...location.state,
124
+ ...flowState,
32
125
  propertyDetails: {
33
- ownerType,
34
- pidNumber,
35
- connectionType,
36
- connectionCategory,
37
- userType,
38
- noOfFloors,
39
- propertyDocument,
40
- buildingPhoto
41
- }
126
+ ownerType, pidNumber, connectionType,
127
+ connectionCategory, userType, noOfFloors,
128
+ propertyDocument, buildingPhoto,
129
+ },
42
130
  });
43
131
  };
44
132
 
45
- const SuitcaseIcon = () => (
46
- <div style={{ backgroundColor: "#E8EAF6", padding: "8px", borderRadius: "8px", display: "flex", alignItems: "center", justifyContent: "center" }}>
47
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
48
- <path d="M20 6H16V4C16 2.89 15.11 2 14 2H10C8.89 2 8 2.89 8 4V6H4C2.89 6 2 6.89 2 8V19C2 20.11 2.89 21 4 21H20C21.11 21 22 20.11 22 19V8C22 6.89 21.11 6 20 6ZM10 4H14V6H10V4ZM20 19H4V8H20V19ZM13 13V10H11V13H8L12 17L16 13H13Z" fill="#3D51B0" />
49
- </svg>
50
- </div>
51
- );
133
+ const handleFileUpload = (e) => {
134
+ const file = e.target.files[0];
135
+ if (!file) return;
136
+ setPropertyDocument(file);
137
+ };
52
138
 
53
- const BuildingIcon = () => (
54
- <div style={{ backgroundColor: "#E8EAF6", padding: "8px", borderRadius: "8px", display: "flex", alignItems: "center", justifyContent: "center" }}>
55
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
56
- <path d="M12 7V3H2V21H22V7H12ZM6 19H4V17H6V19ZM6 15H4V13H6V15ZM6 11H4V9H6V11ZM6 7H4V5H6V7ZM10 19H8V17H10V19ZM10 15H8V13H10V15ZM10 11H8V9H10V11ZM10 7H8V5H10V7ZM20 19H12V9H20V19ZM18 11H14V13H18V11ZM18 15H14V17H18V15Z" fill="#3D51B0" />
57
- </svg>
58
- </div>
59
- );
139
+ const handlePhotoCapture = (e) => {
140
+ const file = e.target.files[0];
141
+ if (!file) return;
142
+ const reader = new FileReader();
143
+ reader.onloadend = () => setBuildingPhoto(reader.result);
144
+ reader.readAsDataURL(file);
145
+ };
60
146
 
61
- const PdfIcon = () => (
62
- <svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
63
- <path d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2ZM13 9V3.5L18.5 9H13Z" fill="#1976D2" />
64
- <path d="M11 14H13V18H11V14ZM11 12H13V13H11V12Z" fill="white" />
65
- <path d="M8 16H9V17H8V16Z" fill="white" />
66
- <path d="M15 16H16V17H15V16Z" fill="white" />
67
- <path d="M12 15.5L14 13.5H10L12 15.5Z" fill="white" />
68
- {/* Simple representation of upload arrow on document */}
69
- <path d="M12 18V14M12 14L10 16M12 14L14 16" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
70
- </svg>
71
- );
147
+ const connectionCategoryOptions =
148
+ dataV0?.["ws-services-calculation"]?.propertyTypeV2?.map((item) => ({
149
+ label: t(item.code), value: item.code,
150
+ })) || [];
72
151
 
73
- const CameraIcon = () => (
74
- <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
75
- <path d="M9 2L7.17 4H4C2.9 4 2 4.9 2 6V18C2 19.1 2.9 20 4 20H20C21.1 20 22 19.1 22 18V6C22 4.9 21.1 4 20 4H16.83L15 2H9ZM12 17C9.24 17 7 14.76 7 12C7 9.24 9.24 7 12 7C14.76 7 17 9.24 17 12C17 14.76 14.76 17 12 17ZM12 9C10.34 9 9 10.34 9 12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12C15 10.34 13.66 9 12 9Z" fill="#3D51B0" />
76
- </svg>
77
- );
152
+ const connectionTypeOptions =
153
+ dataConn?.["ws-services-calculation"]?.connectionTypeV2?.map((item) => ({
154
+ label: t(item.code), value: item.code,
155
+ })) || [];
156
+
157
+ const userTypeOptions =
158
+ dataV1?.["ws-services-calculation"]?.userTypeV2?.map((item) => ({
159
+ label: t(item.code), value: item.code,
160
+ })) || [];
161
+
162
+ const floorOptions =
163
+ dataV2?.["ws-services-calculation"]?.floorCount?.map((item) => ({
164
+ label: t(item.code), value: item.code,
165
+ })) || [];
78
166
 
79
167
  return (
80
168
  <div className="inbox-container">
169
+ <style>{`
170
+ @keyframes fadeSlideIn {
171
+ from { opacity: 0; transform: translateY(8px); }
172
+ to { opacity: 1; transform: translateY(0); }
173
+ }
174
+ `}</style>
175
+
176
+ {/* ── Sidebar ── */}
81
177
  <div className="filters-container">
82
- {/* Sidebar Title Card */}
83
- <Card className="sidebar-title-card" style={{ display: "flex", alignItems: "center", padding: "16px", marginBottom: "16px", borderRadius: "4px" }}>
84
- <div className="icon-container" style={{ color: "#0068faff", marginRight: "12px" }}>
85
- <HomeIcon style={{ width: "24px", height: "24px" }} />
178
+ <Card style={{ display: "flex", alignItems: "center", padding: "12px 16px", marginBottom: "12px", borderRadius: "8px" }}>
179
+ <div style={{ color: "#185FA5", marginRight: "10px", display: "flex" }}>
180
+ <HomeIcon style={{ width: "20px", height: "20px" }} />
86
181
  </div>
87
- <div style={{ fontWeight: "700", fontSize: "18px", color: "#0B0C0C" }}>
88
- {t("EKYC_PROCESS")}
182
+ <div style={{ fontWeight: "600", fontSize: "15px", color: "#0B0C0C" }}>
183
+ {t("EKYC_PROCESS") || "eKYC Process"}
89
184
  </div>
90
185
  </Card>
91
186
 
92
- {/* Progress Steps Sidebar */}
93
- <div style={{ backgroundColor: "#FFFFFF", padding: "16px", borderRadius: "8px", border: "1px solid #EAECF0", boxShadow: "0 2px 4px rgba(0,0,0,0.02)" }}>
94
- <ConnectingCheckPoints>
95
- <CheckPoint label={t("EKYC_STEP_AADHAAR") || "Aadhaar"} isCompleted={true} />
96
- <CheckPoint label={t("EKYC_STEP_ADDRESS") || "Address"} isCompleted={true} />
97
- <CheckPoint label={t("EKYC_STEP_PROPERTY") || "Property"} isCompleted={false} />
98
- <CheckPoint label={t("EKYC_STEP_REVIEW") || "Review"} />
99
- </ConnectingCheckPoints>
187
+ <div style={{ background: "#fff", padding: "16px 14px", borderRadius: "8px", border: "1px solid #EAECF0" }}>
188
+ {[
189
+ { label: t("EKYC_STEP_AADHAAR") || "Aadhaar", done: true, active: false },
190
+ { label: t("EKYC_STEP_ADDRESS") || "Address", done: true, active: false },
191
+ { label: t("EKYC_STEP_PROPERTY") || "Property", done: false, active: true },
192
+ { label: t("EKYC_STEP_REVIEW") || "Review", done: false, active: false },
193
+ ].map((step, i) => (
194
+ <div key={i} style={{ display: "flex", gap: "10px", alignItems: "flex-start", position: "relative", paddingBottom: i < 3 ? "18px" : 0 }}>
195
+ {i < 3 && (
196
+ <div style={{ position: "absolute", left: "10px", top: "22px", width: "1px", height: "calc(100% - 10px)", background: "#EAECF0" }} />
197
+ )}
198
+ <div style={{
199
+ width: "20px", height: "20px", borderRadius: "50%", flexShrink: 0, marginTop: "1px",
200
+ border: step.done ? "none" : step.active ? "1.5px solid #185FA5" : "1.5px solid #D0D5DD",
201
+ background: step.done ? "#0F6E56" : step.active ? "#E6F1FB" : "#fff",
202
+ display: "flex", alignItems: "center", justifyContent: "center",
203
+ fontSize: "10px", fontWeight: "500",
204
+ color: step.done ? "#fff" : step.active ? "#185FA5" : "#98A2B3",
205
+ }}>
206
+ {step.done ? <CheckIcon size={11} color="#fff" /> : i + 1}
207
+ </div>
208
+ <div style={{
209
+ fontSize: "12px", paddingTop: "2px",
210
+ color: step.done ? "#0F6E56" : step.active ? "#0B0C0C" : "#667085",
211
+ fontWeight: step.done || step.active ? "600" : "400",
212
+ }}>
213
+ {step.label}
214
+ </div>
215
+ </div>
216
+ ))}
100
217
  </div>
101
218
  </div>
102
219
 
220
+ {/* ── Main Content ── */}
103
221
  <div style={{ flex: 1, marginLeft: "16px" }}>
104
222
  <Card>
105
- <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "24px" }}>
106
- <Header>{t("Property Details")}</Header>
107
- <div style={{ fontSize: "14px", fontWeight: "700", color: "#505A5F" }}>
108
- {t("EKYC_K_NUMBER")}: <span style={{ color: "#0B0C0C" }}>{kNumber}</span>
223
+ {/* Header */}
224
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }}>
225
+ <CardHeader style={{ margin: 0, fontSize: "18px" }}>
226
+ {t("EKYC_PROPERTY_DETAILS_HEADER") || "Property Details"}
227
+ </CardHeader>
228
+ <div style={{
229
+ background: "#F9FAFB", border: "0.5px solid #EAECF0",
230
+ borderRadius: "20px", padding: "4px 14px",
231
+ fontSize: "12px", color: "#667085",
232
+ }}>
233
+ {t("EKYC_K_NUMBER") || "K Number"}:{" "}
234
+ <span style={{ color: "#0B0C0C", fontWeight: "600" }}>{kNumber}</span>
109
235
  </div>
110
236
  </div>
111
237
 
112
- <div style={{ display: "flex", alignItems: "center", gap: "12px", marginBottom: "24px" }}>
113
- <SuitcaseIcon />
114
- <span style={{ fontSize: "20px", fontWeight: "700", color: "#101828" }}>{t("Property Details")}</span>
115
- </div>
238
+ <div style={{ animation: "fadeSlideIn 0.3s ease" }}>
116
239
 
117
- <Card style={{ padding: "20px", borderRadius: "16px", border: "1px solid #EAECF0", marginBottom: "24px" }}>
118
- {/* Property Owner Selection */}
119
- <div style={{ marginBottom: "24px" }}>
120
- <CardLabel style={{ fontSize: "14px", fontWeight: "600", color: "#667085", marginBottom: "12px" }}>{t("Property_Owner")}</CardLabel>
121
- <div style={{ display: "flex", backgroundColor: "#F2F4F7", padding: "4px", borderRadius: "12px" }}>
122
- <button
123
- onClick={() => setOwnerType("OWNER")}
124
- style={{
125
- flex: 1,
126
- padding: "10px",
127
- borderRadius: "10px",
128
- border: "none",
129
- backgroundColor: ownerType === "OWNER" ? "#3D51B0" : "transparent",
130
- color: ownerType === "OWNER" ? "#FFFFFF" : "#667085",
131
- fontWeight: "600",
132
- cursor: "pointer",
133
- transition: "all 0.2s"
134
- }}
135
- >
136
- {t("Owner")}
137
- </button>
138
- <button
139
- onClick={() => setOwnerType("TENANT")}
140
- style={{
141
- flex: 1,
142
- padding: "10px",
143
- borderRadius: "10px",
144
- border: "none",
145
- backgroundColor: ownerType === "TENANT" ? "#3D51B0" : "transparent",
146
- color: ownerType === "TENANT" ? "#FFFFFF" : "#667085",
147
- fontWeight: "600",
148
- cursor: "pointer",
149
- transition: "all 0.2s"
150
- }}
151
- >
152
- {t("Tenant")}
153
- </button>
240
+ {/* ── Property Details Section ── */}
241
+ <SectionHead
242
+ icon={<BriefcaseIcon size={16} />}
243
+ label={t("EKYC_PROPERTY_DETAILS") || "Property details"}
244
+ />
245
+
246
+ {/* Owner / Tenant Toggle */}
247
+ <div style={{ marginBottom: "20px" }}>
248
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "8px" }}>
249
+ {t("EKYC_PROPERTY_OWNER") || "Property owner"}
250
+ </div>
251
+ <div style={{ display: "flex", backgroundColor: "#F2F4F7", padding: "4px", borderRadius: "10px", gap: "4px" }}>
252
+ {["OWNER", "TENANT"].map((type) => (
253
+ <button
254
+ key={type}
255
+ onClick={() => setOwnerType(type)}
256
+ style={{
257
+ flex: 1, padding: "9px 12px", borderRadius: "7px", border: "none", cursor: "pointer",
258
+ fontSize: "13px", fontWeight: "600", transition: "all 0.15s",
259
+ background: ownerType === type ? "#185FA5" : "transparent",
260
+ color: ownerType === type ? "#fff" : "#667085",
261
+ }}
262
+ >
263
+ {t(`EKYC_${type}`) || (type === "OWNER" ? "Owner" : "Tenant")}
264
+ </button>
265
+ ))}
154
266
  </div>
155
267
  </div>
156
268
 
157
- {/* PID Number Section */}
158
- <div style={{ marginBottom: "8px" }}>
159
- <CardLabel style={{ fontSize: "14px", fontWeight: "600", color: "#667085", marginBottom: "12px" }}>
160
- {t("PID_Number")} <span style={{ fontStyle: "italic", fontWeight: "400", color: "#98A2B3" }}>{t("Optional")}</span>
161
- </CardLabel>
162
- <div className="field" style={{ position: "relative" }}>
163
- <TextInput
164
- value={pidNumber}
165
- onChange={(e) => setPidNumber(e.target.value)}
166
- placeholder={t("Enter_PID_Number")}
167
- textInputStyle={{ paddingLeft: "44px", borderRadius: "12px", border: "1px solid #D0D5DD", height: "56px" }}
168
- />
169
- <div style={{ position: "absolute", left: "16px", top: "50%", transform: "translateY(-50%)", color: "#3D51B0", fontSize: "20px", fontWeight: "600" }}>#</div>
269
+ {/* PID Number */}
270
+ <div style={{ marginBottom: "20px" }}>
271
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
272
+ {t("EKYC_PID_NUMBER") || "PID number"}{" "}
273
+ <span style={{ fontStyle: "italic", fontWeight: "400", textTransform: "none", color: "#98A2B3" }}>
274
+ {t("EKYC_OPTIONAL") || "optional"}
275
+ </span>
170
276
  </div>
277
+ <IconInput
278
+ icon={<PidIcon size={15} />}
279
+ value={pidNumber}
280
+ onChange={(e) => setPidNumber(e.target.value)}
281
+ placeholder={t("EKYC_ENTER_PID_NUMBER") || "Enter PID number"}
282
+ />
171
283
  </div>
172
- </Card>
173
284
 
174
- {/* Building Info Section */}
175
- <div style={{ display: "flex", alignItems: "center", gap: "12px", marginBottom: "24px" }}>
176
- <BuildingIcon />
177
- <span style={{ fontSize: "20px", fontWeight: "700", color: "#101828" }}>{t("Building_Info")}</span>
178
- </div>
285
+ <hr style={{ margin: "24px 0", border: 0, borderTop: "1px solid #EAECF0" }} />
286
+
287
+ {/* ── Building Info Section ── */}
288
+ <SectionHead
289
+ icon={<BuildingIcon size={16} />}
290
+ label={t("EKYC_BUILDING_INFO") || "Building info"}
291
+ />
179
292
 
180
- <Card style={{ padding: "20px", borderRadius: "16px", border: "1px solid #EAECF0", marginBottom: "24px" }}>
181
-
182
- {/* Dropdowns */}
183
- {[
184
- {
185
- label: "Type_of_Connection",
186
- state: connectionCategory,
187
- setState: setConnectionCategory,
188
- options: dataV0?.["ws-services-calculation"]?.propertyTypeV2?.map(item => ({
189
- label: t(item.code),
190
- value: item.code
191
- })) || []
192
- },
193
- {
194
- label: "Connection_Category",
195
- state: connectionType,
196
- setState: setConnectionType,
197
- options: data?.["ws-services-calculation"]?.connectionTypeV2?.map(item => ({
198
- label: t(item.code),
199
- value: item.code
200
- })) || []
201
- },
202
- // { label: "User_Type", state: userType, setState: setUserType },
203
- {
204
- label: "User_Type",
205
- state: userType,
206
- setState: setUserType,
207
- options: dataV1?.["ws-services-calculation"]?.userTypeV2?.map(item => ({
208
- label: t(item.code),
209
- value: item.code
210
- })) || []
211
- },
212
- {
213
- label: "No_of_Floor",
214
- state: noOfFloors,
215
- setState: setNoOfFloors,
216
- options: dataV2?.["ws-services-calculation"]?.floorCount?.map(item => ({
217
- label: t(item.code),
218
- value: item.code
219
- })) || []
220
- }
221
- ].map((item, idx) => (
222
- <div key={idx} style={{ marginBottom: "20px" }}>
223
- <CardLabel style={{ fontSize: "14px", fontWeight: "600", color: "#344054", marginBottom: "8px" }}>{t(item.label)}</CardLabel>
293
+ {/* Dropdowns grid */}
294
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "14px" }}>
295
+ <div>
296
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
297
+ {t("EKYC_TYPE_OF_CONNECTION") || "Type of connection"}
298
+ </div>
224
299
  <Dropdown
225
- selected={item.state}
226
- select={item.setState}
227
- option={item.options || [{ label: `Select ${item.label}`, value: "" }]}
300
+ selected={connectionCategory}
301
+ select={setConnectionCategory}
302
+ option={connectionCategoryOptions}
228
303
  optionKey="label"
229
304
  t={t}
230
- style={{ borderRadius: "12px", border: "1px solid #D0D5DD", height: "48px" }}
305
+ placeholder={t("EKYC_SELECT") || "Select"}
231
306
  />
232
307
  </div>
233
- ))}
234
- <div
235
- style={{
236
- display: "flex",
237
- gap: "24px",
238
- alignItems: "stretch", // important
239
- flexWrap: "wrap"
240
- }}
241
- >
242
- {/* PDF Upload Box */}
243
- <div
244
- style={{
245
- flex: 1,
246
- minWidth: "300px",
247
- display: "flex",
248
- flexDirection: "column"
249
- }}
250
- >
251
- <CardLabel
252
- style={{
253
- fontSize: "14px",
254
- fontWeight: "600",
255
- color: "#344054",
256
- marginBottom: "8px"
257
- }}
258
- >
259
- {t("Upload_Property_Document")}
260
- </CardLabel>
308
+ <div>
309
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
310
+ {t("EKYC_CONNECTION_CATEGORY") || "Connection category"}
311
+ </div>
312
+ <Dropdown
313
+ selected={connectionType}
314
+ select={setConnectionType}
315
+ option={connectionTypeOptions}
316
+ optionKey="label"
317
+ t={t}
318
+ placeholder={t("EKYC_SELECT") || "Select"}
319
+ />
320
+ </div>
321
+ </div>
322
+
323
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "24px" }}>
324
+ <div>
325
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
326
+ {t("EKYC_USER_TYPE") || "User type"}
327
+ </div>
328
+ <Dropdown
329
+ selected={userType}
330
+ select={setUserType}
331
+ option={userTypeOptions}
332
+ optionKey="label"
333
+ t={t}
334
+ placeholder={t("EKYC_SELECT") || "Select"}
335
+ />
336
+ </div>
337
+ <div>
338
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "6px" }}>
339
+ {t("EKYC_NO_OF_FLOORS") || "No. of floors"}
340
+ </div>
341
+ <Dropdown
342
+ selected={noOfFloors}
343
+ select={setNoOfFloors}
344
+ option={floorOptions}
345
+ optionKey="label"
346
+ t={t}
347
+ placeholder={t("EKYC_SELECT") || "Select"}
348
+ />
349
+ </div>
350
+ </div>
351
+
352
+ <hr style={{ margin: "24px 0", border: 0, borderTop: "1px solid #EAECF0" }} />
261
353
 
354
+ {/* ── Documents & Photo Section ── */}
355
+ <SectionHead
356
+ icon={<DocumentIcon size={16} />}
357
+ label={t("EKYC_DOCUMENTS_PHOTO") || "Documents & photo"}
358
+ />
359
+
360
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "14px", marginBottom: "20px" }}>
361
+
362
+ {/* PDF Upload */}
363
+ <div>
364
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "8px" }}>
365
+ {t("EKYC_UPLOAD_PROPERTY_DOC") || "Upload property document"}
366
+ </div>
367
+ <input type="file" ref={fileRef} accept=".pdf" style={{ display: "none" }} onChange={handleFileUpload} />
262
368
  <div
369
+ onClick={() => fileRef.current.click()}
370
+ onMouseOver={(e) => e.currentTarget.style.borderColor = "#185FA5"}
371
+ onMouseOut={(e) => e.currentTarget.style.borderColor = "#B5D4F4"}
263
372
  style={{
264
- flex: 1, // important for equal height
265
- backgroundColor: "#EBF5FF",
266
- border: "1px solid #B2DDFF",
267
- borderRadius: "16px",
268
- padding: "32px 24px",
269
- textAlign: "center",
270
- cursor: "pointer",
271
- display: "flex",
272
- flexDirection: "column",
273
- justifyContent: "center"
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",
274
379
  }}
275
- onClick={() => fileRef.current.click()}
276
380
  >
277
- <input type="file" ref={fileRef} style={{ display: "none" }} accept=".pdf" />
278
-
279
- <div
280
- style={{
281
- color: "#1570EF",
282
- fontWeight: "600",
283
- fontSize: "16px",
284
- marginBottom: "20px",
285
- lineHeight: "1.5"
286
- }}
287
- >
288
- {t("Upload_your_property_document_in_PDF_Only")}
381
+ <div style={{ background: "#fff", padding: "10px", borderRadius: "10px", display: "flex" }}>
382
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="#185FA5">
383
+ <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
+ <path d="M12 18v-4M12 14l-2 2M12 14l2 2" stroke="#fff" strokeWidth="1.5" strokeLinecap="round" />
385
+ </svg>
289
386
  </div>
290
-
291
- <div style={{ display: "flex", justifyContent: "center" }}>
292
- <div
293
- style={{
294
- backgroundColor: "#FFFFFF",
295
- padding: "12px",
296
- borderRadius: "12px",
297
- boxShadow: "0px 1px 2px rgba(16, 24, 40, 0.05)"
298
- }}
299
- >
300
- <PdfIcon />
387
+ {propertyDocument ? (
388
+ <div style={{ fontSize: "13px", fontWeight: "600", color: "#0F6E56" }}>
389
+ ✓ {propertyDocument.name}
301
390
  </div>
302
- </div>
391
+ ) : (
392
+ <>
393
+ <div style={{ fontSize: "13px", fontWeight: "600", color: "#185FA5" }}>
394
+ {t("EKYC_UPLOAD_PROPERTY_DOC_CTA") || "Tap to upload"}
395
+ </div>
396
+ <div style={{ fontSize: "12px", color: "#378ADD" }}>PDF only</div>
397
+ </>
398
+ )}
303
399
  </div>
304
400
  </div>
305
401
 
306
- {/* Capture Building Image */}
307
- <div
308
- style={{
309
- flex: 1,
310
- minWidth: "300px",
311
- display: "flex",
312
- flexDirection: "column"
313
- }}
314
- >
315
- <CardLabel
316
- style={{
317
- fontSize: "14px",
318
- fontWeight: "600",
319
- color: "#344054",
320
- marginBottom: "8px"
321
- }}
322
- >
323
- {t("Capture_Building_Image")}
324
- </CardLabel>
325
-
402
+ {/* Camera Capture */}
403
+ <div>
404
+ <div style={{ fontSize: "11px", fontWeight: "600", color: "#667085", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: "8px" }}>
405
+ {t("EKYC_CAPTURE_BUILDING_IMAGE") || "Capture building image"}
406
+ </div>
407
+ <input type="file" ref={cameraRef} accept="image/*" capture="environment" style={{ display: "none" }} onChange={handlePhotoCapture} />
326
408
  <div
409
+ onClick={!buildingPhoto ? () => cameraRef.current.click() : undefined}
410
+ onMouseOver={(e) => { if (!buildingPhoto) e.currentTarget.style.borderColor = "#185FA5"; }}
411
+ onMouseOut={(e) => { if (!buildingPhoto) e.currentTarget.style.borderColor = "#D0D5DD"; }}
327
412
  style={{
328
- flex: 1, // important
329
- border: "1px solid #D0D5DD",
330
- borderRadius: "16px",
331
- padding: "40px 24px",
332
- textAlign: "center",
333
- cursor: "pointer",
334
- backgroundColor: "#FFFFFF",
335
- display: "flex",
336
- flexDirection: "column",
337
- justifyContent: "center"
413
+ border: "1.5px dashed #D0D5DD", borderRadius: "10px",
414
+ minHeight: "160px", display: "flex", flexDirection: "column",
415
+ alignItems: "center", justifyContent: "center",
416
+ backgroundColor: "#F9FAFB",
417
+ cursor: buildingPhoto ? "default" : "pointer",
418
+ overflow: "hidden", transition: "border-color 0.15s",
419
+ position: "relative",
420
+ padding: buildingPhoto ? "0" : "28px 20px",
338
421
  }}
339
- onClick={() => cameraRef.current.click()}
340
422
  >
341
- <input
342
- type="file"
343
- ref={cameraRef}
344
- style={{ display: "none" }}
345
- accept="image/*"
346
- capture="environment"
347
- />
348
-
349
- <div
350
- style={{
351
- backgroundColor: "#EEF4FF",
352
- width: "56px",
353
- height: "56px",
354
- borderRadius: "50%",
355
- display: "flex",
356
- alignItems: "center",
357
- justifyContent: "center",
358
- margin: "0 auto 16px"
359
- }}
360
- >
361
- <CameraIcon />
362
- </div>
363
-
364
- <div
365
- style={{
366
- fontWeight: "600",
367
- color: "#101828",
368
- fontSize: "16px",
369
- marginBottom: "4px"
370
- }}
371
- >
372
- {t("Tap_to_Capture")}
373
- </div>
374
-
375
- <div style={{ color: "#667085", fontSize: "14px" }}>
376
- {t("Building_Photo")}
377
- </div>
423
+ {!buildingPhoto ? (
424
+ <>
425
+ <div style={{ background: "#E6F1FB", width: "52px", height: "52px", borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: "10px" }}>
426
+ <CameraIcon size={26} />
427
+ </div>
428
+ <div style={{ fontSize: "13px", fontWeight: "600", color: "#101828" }}>
429
+ {t("EKYC_TAP_TO_CAPTURE") || "Tap to capture"}
430
+ </div>
431
+ <div style={{ fontSize: "12px", color: "#667085", marginTop: "2px" }}>
432
+ {t("EKYC_BUILDING_PHOTO") || "Building photo with GPS"}
433
+ </div>
434
+ </>
435
+ ) : (
436
+ <>
437
+ <img src={buildingPhoto} alt="Building" style={{ width: "100%", maxHeight: "200px", objectFit: "cover", display: "block" }} />
438
+ <button
439
+ onClick={(e) => { e.stopPropagation(); setBuildingPhoto(null); if (cameraRef.current) cameraRef.current.value = ""; }}
440
+ style={{
441
+ position: "absolute", top: "8px", right: "8px",
442
+ background: "#fff", border: "0.5px solid #EAECF0",
443
+ borderRadius: "7px", padding: "5px 10px",
444
+ display: "flex", alignItems: "center", gap: "5px",
445
+ cursor: "pointer", fontSize: "12px", color: "#D92D20", fontWeight: "500",
446
+ }}
447
+ >
448
+ <TrashIcon size={13} /> {t("EKYC_REMOVE") || "Remove"}
449
+ </button>
450
+ </>
451
+ )}
378
452
  </div>
379
453
  </div>
380
454
  </div>
381
455
 
382
456
  {/* Info Banner */}
383
- <div style={{ marginTop: "24px", backgroundColor: "#EFF8FF", padding: "16px", borderRadius: "12px", display: "flex", gap: "12px", border: "1px solid #B2DDFF" }}>
384
- <div style={{ color: "#1570EF" }}>
385
- <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
386
- <path d="M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM11 15H9V9H11V15ZM11 7H9V5H11V7Z" fill="currentColor" />
387
- </svg>
457
+ <div style={{
458
+ backgroundColor: "#E6F1FB", border: "0.5px solid #B5D4F4",
459
+ borderRadius: "8px", padding: "12px 14px",
460
+ display: "flex", alignItems: "flex-start", gap: "10px", marginBottom: "4px",
461
+ }}>
462
+ <div style={{ flexShrink: 0, marginTop: "1px" }}>
463
+ <InfoBannerIcon fill="#185FA5" />
388
464
  </div>
389
- <div style={{ color: "#175CD3", fontSize: "14px", fontWeight: "500", lineHeight: "1.4" }}>
390
- {t("This_section_is_enabled_only_if_user_is_not_the_owner.")}
465
+ <div style={{ fontSize: "13px", color: "#185FA5", lineHeight: "1.5" }}>
466
+ {t("EKYC_TENANT_INFO_NOTICE") || "This section is enabled only if the user is not the owner. Tenant details will be required if tenant is selected."}
391
467
  </div>
392
468
  </div>
393
- <div style={{ display: "flex", justifyContent: "flex-start", marginTop: "24px" }}>
394
- <SubmitBar label={t("Save_&_Continue")} onSubmit={handleSaveAndContinue} style={{ borderRadius: "8px", height: "48px", margin: 0 }} />
395
- </div>
396
- </Card>
397
469
 
470
+ </div>
471
+
472
+ {/* Submit (Non-sticky, at form end) */}
473
+ <div style={{ marginTop: "24px" }}>
474
+ <SubmitBar
475
+ label={t("EKYC_SAVE_AND_CONTINUE") || "Save & Continue"}
476
+ onSubmit={handleSaveAndContinue}
477
+ />
478
+ </div>
398
479
 
480
+ {/* Secure notice */}
481
+ <div style={{
482
+ display: "flex", alignItems: "center", justifyContent: "center",
483
+ gap: "5px", marginTop: "16px",
484
+ fontSize: "11px", color: "#98A2B3",
485
+ }}>
486
+ <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
487
+ <rect x="3" y="11" width="18" height="11" rx="2" />
488
+ <path d="M7 11V7a5 5 0 0 1 10 0v4" />
489
+ </svg>
490
+ {t("EKYC_SECURE_DATA_NOTICE") || "Your data is encrypted and secure"}
491
+ </div>
399
492
  </Card>
400
493
  </div>
401
494
  </div>