@djb25/digit-ui-module-ekyc 1.0.8 → 1.0.10

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,11 +1,645 @@
1
- import React from 'react'
1
+ import React, { useState, useMemo } from "react";
2
+ import { Card, Dropdown, Toast, Table } from "@djb25/digit-ui-react-components";
3
+ import { useTranslation } from "react-i18next";
4
+
5
+ // ─── Icons ────────────────────────────────────────────────────────────────────
6
+
7
+ const UserIcon = ({ size = 18, color = "currentColor" }) => (
8
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
9
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
10
+ <circle cx="12" cy="7" r="4" />
11
+ </svg>
12
+ );
13
+
14
+ const DiaryIcon = ({ size = 18, color = "currentColor" }) => (
15
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
16
+ <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
17
+ <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" />
18
+ </svg>
19
+ );
20
+
21
+ const TrashIcon = ({ size = 14 }) => (
22
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
23
+ <polyline points="3 6 5 6 21 6" />
24
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
25
+ </svg>
26
+ );
27
+
28
+ const EditIcon = ({ size = 14 }) => (
29
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
30
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
31
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
32
+ </svg>
33
+ );
34
+
35
+ const MapPinIcon = ({ size = 12 }) => (
36
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
37
+ <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
38
+ <circle cx="12" cy="10" r="3" />
39
+ </svg>
40
+ );
41
+
42
+ const PlusIcon = ({ size = 16 }) => (
43
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
44
+ <line x1="12" y1="5" x2="12" y2="19" />
45
+ <line x1="5" y1="12" x2="19" y2="12" />
46
+ </svg>
47
+ );
48
+
49
+ const SaveIcon = ({ size = 16 }) => (
50
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
51
+ <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
52
+ <polyline points="17 21 17 13 7 13 7 21" />
53
+ <polyline points="7 3 7 8 15 8" />
54
+ </svg>
55
+ );
56
+
57
+ const CheckIcon = ({ size = 16 }) => (
58
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
59
+ <polyline points="20 6 9 17 4 12" />
60
+ </svg>
61
+ );
62
+
63
+ const XIcon = ({ size = 16 }) => (
64
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
65
+ <line x1="18" y1="6" x2="6" y2="18" />
66
+ <line x1="6" y1="6" x2="18" y2="18" />
67
+ </svg>
68
+ );
69
+
70
+ // ─── Static Mock Data ─────────────────────────────────────────────────────────
71
+
72
+ const MOCK_SURVEYORS = [
73
+ { name: "Amit Kumar", id: "SVR001", role: "Surveyor" },
74
+ { name: "Sanjay Singh", id: "SVR002", role: "Surveyor" },
75
+ { name: "Rahul Sharma", id: "SVR003", role: "Surveyor" },
76
+ { name: "Priya Verma", id: "SVR004", role: "Surveyor" },
77
+ ];
78
+
79
+ const MOCK_MRDS = [
80
+ { name: "MRD - 01 (Central Zone)", code: "MRD01", zone: "Central" },
81
+ { name: "MRD - 02 (North Zone)", code: "MRD02", zone: "North" },
82
+ { name: "MRD - 03 (South Zone)", code: "MRD03", zone: "South" },
83
+ { name: "MRD - 04 (West Zone)", code: "MRD04", zone: "West" },
84
+ { name: "MRD - 05 (East Zone)", code: "MRD05", zone: "East" },
85
+ { name: "MRD - 06 (Rohini)", code: "MRD06", zone: "Rohini" },
86
+ { name: "MRD - 07 (Dwarka)", code: "MRD07", zone: "Dwarka" },
87
+ ];
88
+
89
+ // ─── Confirm Delete Modal ─────────────────────────────────────────────────────
90
+
91
+ const ConfirmDeleteModal = ({ mapping, onConfirm, onCancel }) => (
92
+ <div style={{
93
+ position: "fixed", inset: 0, background: "rgba(0,0,0,0.35)",
94
+ display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1000,
95
+ }}>
96
+ <div style={{
97
+ background: "#fff", borderRadius: "12px", padding: "28px 28px 24px",
98
+ width: "400px", boxShadow: "0 20px 60px rgba(0,0,0,0.15)",
99
+ }}>
100
+ <div style={{ display: "flex", alignItems: "center", gap: "12px", marginBottom: "16px" }}>
101
+ <div style={{
102
+ width: "40px", height: "40px", borderRadius: "50%",
103
+ background: "#FEF3F2", display: "flex", alignItems: "center", justifyContent: "center",
104
+ }}>
105
+ <TrashIcon size={18} />
106
+ </div>
107
+ <div>
108
+ <div style={{ fontWeight: "700", fontSize: "16px", color: "#0D1B2A" }}>Remove Mapping</div>
109
+ <div style={{ fontSize: "12px", color: "#6B7B8E" }}>This action cannot be undone</div>
110
+ </div>
111
+ </div>
112
+ <p style={{ fontSize: "13px", color: "#344054", marginBottom: "24px", lineHeight: "1.6" }}>
113
+ Are you sure you want to remove the mapping between{" "}
114
+ <strong>{mapping?.surveyorName}</strong> and <strong>{mapping?.mrdName}</strong>?
115
+ </p>
116
+ <div style={{ display: "flex", gap: "10px", justifyContent: "flex-end" }}>
117
+ <button onClick={onCancel} style={{
118
+ background: "none", border: "1px solid #D0D5DD", borderRadius: "8px",
119
+ padding: "8px 20px", fontWeight: "600", fontSize: "13px",
120
+ color: "#344054", cursor: "pointer",
121
+ }}>Cancel</button>
122
+ <button onClick={onConfirm} style={{
123
+ background: "#B42318", border: "none", borderRadius: "8px",
124
+ padding: "8px 20px", fontWeight: "600", fontSize: "13px",
125
+ color: "#fff", cursor: "pointer",
126
+ display: "flex", alignItems: "center", gap: "6px",
127
+ }}>
128
+ <TrashIcon size={13} /> Remove
129
+ </button>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ );
134
+
135
+ // ─── Main Component ───────────────────────────────────────────────────────────
2
136
 
3
137
  const Mapping = () => {
138
+ const { t } = useTranslation();
139
+
140
+ // Form state
141
+ const [selectedSurveyor, setSelectedSurveyor] = useState(null);
142
+ const [selectedMRD, setSelectedMRD] = useState(null);
143
+
144
+ // Mappings list
145
+ const [mappings, setMappings] = useState([]);
146
+
147
+ // Edit state: stores the id of the row being edited + its draft values
148
+ const [editingId, setEditingId] = useState(null);
149
+ const [editSurveyor, setEditSurveyor] = useState(null);
150
+ const [editMRD, setEditMRD] = useState(null);
151
+
152
+ // Delete confirm modal
153
+ const [deleteTarget, setDeleteTarget] = useState(null);
154
+
155
+ const [toast, setToast] = useState(null);
156
+
157
+ // ── Handlers ──────────────────────────────────────────────────────────────
158
+
159
+ const handleAddMapping = () => {
160
+ if (!selectedSurveyor || !selectedMRD) {
161
+ setToast({ type: "error", message: t("EKYC_SELECT_BOTH_ERROR") || "Please select both Surveyor and MRD" });
162
+ return;
163
+ }
164
+ const exists = mappings.some(
165
+ m => m.surveyorId === selectedSurveyor.id && m.mrdCode === selectedMRD.code
166
+ );
167
+ if (exists) {
168
+ setToast({ type: "warning", message: t("EKYC_MAPPING_EXISTS") || "This mapping already exists" });
169
+ return;
170
+ }
171
+ setMappings(prev => [...prev, {
172
+ id: Date.now(),
173
+ surveyorName: selectedSurveyor.name,
174
+ surveyorId: selectedSurveyor.id,
175
+ mrdName: selectedMRD.name,
176
+ mrdCode: selectedMRD.code,
177
+ zone: selectedMRD.zone,
178
+ }]);
179
+ setSelectedMRD(null);
180
+ setToast({ type: "success", message: t("EKYC_MAPPING_ADDED") || "Mapping added successfully" });
181
+ };
182
+
183
+ const handleEditStart = (row) => {
184
+ setEditingId(row.id);
185
+ setEditSurveyor(MOCK_SURVEYORS.find(s => s.id === row.surveyorId) || null);
186
+ setEditMRD(MOCK_MRDS.find(m => m.code === row.mrdCode) || null);
187
+ };
188
+
189
+ const handleEditSave = (id) => {
190
+ if (!editSurveyor || !editMRD) {
191
+ setToast({ type: "error", message: "Please select both Surveyor and MRD to save" });
192
+ return;
193
+ }
194
+ // Check duplicate (excluding self)
195
+ const duplicate = mappings.some(
196
+ m => m.id !== id && m.surveyorId === editSurveyor.id && m.mrdCode === editMRD.code
197
+ );
198
+ if (duplicate) {
199
+ setToast({ type: "warning", message: "This mapping already exists" });
200
+ return;
201
+ }
202
+ setMappings(prev => prev.map(m => m.id !== id ? m : {
203
+ ...m,
204
+ surveyorName: editSurveyor.name,
205
+ surveyorId: editSurveyor.id,
206
+ mrdName: editMRD.name,
207
+ mrdCode: editMRD.code,
208
+ zone: editMRD.zone,
209
+ }));
210
+ setEditingId(null);
211
+ setToast({ type: "success", message: "Mapping updated successfully" });
212
+ };
213
+
214
+ const handleEditCancel = () => {
215
+ setEditingId(null);
216
+ setEditSurveyor(null);
217
+ setEditMRD(null);
218
+ };
219
+
220
+ const handleDeleteConfirm = () => {
221
+ setMappings(prev => prev.filter(m => m.id !== deleteTarget.id));
222
+ setDeleteTarget(null);
223
+ setToast({ type: "info", message: t("EKYC_MAPPING_REMOVED") || "Mapping removed" });
224
+ };
225
+
226
+ const handleSaveMappings = () => {
227
+ if (mappings.length === 0) {
228
+ setToast({ type: "error", message: t("EKYC_NO_MAPPINGS_TO_SAVE") || "No mappings to save" });
229
+ return;
230
+ }
231
+ // TODO: replace with real API call
232
+ setToast({ type: "success", message: t("EKYC_MAPPINGS_SAVED_SUCCESS") || "Surveyor mappings saved successfully!" });
233
+ };
234
+
235
+ // ── Table columns ─────────────────────────────────────────────────────────
236
+
237
+ const columns = useMemo(() => [
238
+ {
239
+ Header: t("EKYC_SURVEYOR_NAME") || "Surveyor",
240
+ accessor: "surveyorName",
241
+ Cell: ({ row }) => {
242
+ const isEditing = editingId === row.original.id;
243
+ if (isEditing) {
244
+ return (
245
+ <div style={{ minWidth: "180px" }}>
246
+ <Dropdown
247
+ selected={editSurveyor}
248
+ select={setEditSurveyor}
249
+ option={MOCK_SURVEYORS}
250
+ optionKey="name"
251
+ t={t}
252
+ placeholder="Choose surveyor..."
253
+ />
254
+ </div>
255
+ );
256
+ }
257
+ return (
258
+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
259
+ <div style={{
260
+ width: "32px", height: "32px", borderRadius: "50%",
261
+ background: "#EEF4FB", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
262
+ }}>
263
+ <UserIcon size={15} color="#3A7BD5" />
264
+ </div>
265
+ <div>
266
+ <div style={{ fontWeight: "600", fontSize: "13px", color: "#0D1B2A" }}>{row.original.surveyorName}</div>
267
+ <div style={{ fontSize: "11px", color: "#6B7B8E" }}>ID: {row.original.surveyorId}</div>
268
+ </div>
269
+ </div>
270
+ );
271
+ },
272
+ },
273
+ {
274
+ Header: t("EKYC_MRD_ASSIGNED") || "MRD Assigned",
275
+ accessor: "mrdName",
276
+ Cell: ({ row }) => {
277
+ const isEditing = editingId === row.original.id;
278
+ if (isEditing) {
279
+ return (
280
+ <div style={{ minWidth: "200px" }}>
281
+ <Dropdown
282
+ selected={editMRD}
283
+ select={setEditMRD}
284
+ option={MOCK_MRDS}
285
+ optionKey="name"
286
+ t={t}
287
+ placeholder="Choose MRD..."
288
+ />
289
+ </div>
290
+ );
291
+ }
292
+ return (
293
+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
294
+ <div style={{
295
+ width: "32px", height: "32px", borderRadius: "50%",
296
+ background: "#EEF4FB", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
297
+ }}>
298
+ <DiaryIcon size={15} color="#3A7BD5" />
299
+ </div>
300
+ <div>
301
+ <div style={{ fontWeight: "600", fontSize: "13px", color: "#0D1B2A" }}>{row.original.mrdName}</div>
302
+ <div style={{ fontSize: "11px", color: "#3A7BD5", display: "flex", alignItems: "center", gap: "3px" }}>
303
+ <MapPinIcon size={10} /> {row.original.zone}
304
+ </div>
305
+ </div>
306
+ </div>
307
+ );
308
+ },
309
+ },
310
+ {
311
+ Header: t("ES_COMMON_ACTION") || "Actions",
312
+ Cell: ({ row }) => {
313
+ const isEditing = editingId === row.original.id;
314
+ if (isEditing) {
315
+ return (
316
+ <div style={{ display: "flex", gap: "8px" }}>
317
+ <button
318
+ onClick={() => handleEditSave(row.original.id)}
319
+ style={{
320
+ display: "flex", alignItems: "center", gap: "5px",
321
+ background: "#3A7BD5", color: "#fff",
322
+ border: "none", borderRadius: "6px",
323
+ padding: "6px 12px", fontWeight: "600",
324
+ fontSize: "12px", cursor: "pointer",
325
+ }}
326
+ >
327
+ <CheckIcon size={13} /> Save
328
+ </button>
329
+ <button
330
+ onClick={handleEditCancel}
331
+ style={{
332
+ display: "flex", alignItems: "center", gap: "5px",
333
+ background: "none", border: "1px solid #D0D5DD",
334
+ borderRadius: "6px", padding: "6px 12px",
335
+ fontWeight: "600", fontSize: "12px",
336
+ color: "#344054", cursor: "pointer",
337
+ }}
338
+ >
339
+ <XIcon size={13} /> Cancel
340
+ </button>
341
+ </div>
342
+ );
343
+ }
344
+ return (
345
+ <div style={{ display: "flex", gap: "8px" }}>
346
+ <button
347
+ onClick={() => handleEditStart(row.original)}
348
+ style={{
349
+ display: "flex", alignItems: "center", gap: "5px",
350
+ background: "none", border: "1px solid #D0D5DD",
351
+ borderRadius: "6px", padding: "6px 12px",
352
+ color: "#344054", fontSize: "12px",
353
+ fontWeight: "600", cursor: "pointer",
354
+ }}
355
+ >
356
+ <EditIcon size={13} /> Edit
357
+ </button>
358
+ <button
359
+ onClick={() => setDeleteTarget(row.original)}
360
+ style={{
361
+ display: "flex", alignItems: "center", gap: "5px",
362
+ background: "none", border: "1px solid #FECDCA",
363
+ borderRadius: "6px", padding: "6px 12px",
364
+ color: "#B42318", fontSize: "12px",
365
+ fontWeight: "600", cursor: "pointer",
366
+ }}
367
+ >
368
+ <TrashIcon size={13} /> Remove
369
+ </button>
370
+ </div>
371
+ );
372
+ },
373
+ },
374
+ ], [editingId, editSurveyor, editMRD, mappings, t]);
375
+
376
+ // ── Render ────────────────────────────────────────────────────────────────
377
+
4
378
  return (
5
- <div style={{textAlign:"center", fontSize:"20px", fontWeight:"bold", marginTop:"20px", marginBottom:"20px"}}>
6
- <h1>Mapping</h1>
379
+ <div style={{ background: "#F5F7FA", minHeight: "100vh", padding: "0" }}>
380
+
381
+ {/* Page Header Bar */}
382
+ <Card style={{
383
+ display: "flex", alignItems: "center", justifyContent: "space-between",
384
+ padding: "16px 24px", marginBottom: "0", borderRadius: "4px",
385
+ }}>
386
+ <div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
387
+ <div style={{ color: "#3A8DCC" }}>
388
+ <DiaryIcon size={24} />
389
+ </div>
390
+ <div style={{ fontWeight: "700", fontSize: "18px", color: "#0B0C0C" }}>
391
+ {t("EKYC_SURVEYOR_MAPPING") || "Surveyor Mapping"}
392
+ </div>
393
+ </div>
394
+ <button
395
+ onClick={handleSaveMappings}
396
+ disabled={mappings.length === 0}
397
+ style={{
398
+ display: "flex", alignItems: "center", gap: "8px",
399
+ background: mappings.length === 0 ? "#D0D5DD" : "#0F3460",
400
+ color: "#fff", border: "none", borderRadius: "8px",
401
+ padding: "10px 20px", fontWeight: "600", fontSize: "14px",
402
+ cursor: mappings.length === 0 ? "not-allowed" : "pointer",
403
+ transition: "background 0.2s",
404
+ }}
405
+ >
406
+ <SaveIcon size={15} />
407
+ {t("EKYC_SAVE_MAPPINGS") || "Save Mappings"}
408
+ </button>
409
+ </Card>
410
+
411
+ {/* Content */}
412
+ <div style={{ padding: "24px 32px" }}>
413
+ <p style={{ fontSize: "13px", color: "#6B7B8E", marginBottom: "24px", marginTop: "0" }}>
414
+ {t("EKYC_MAPPING_SUBHEADER") || "Assign Meter Reading Dairies to surveyors to manage their data access."}
415
+ </p>
416
+
417
+ <div style={{ display: "grid", gridTemplateColumns: "360px 1fr", gap: "20px", alignItems: "start" }}>
418
+
419
+ {/* ── Left: New Mapping Panel ── */}
420
+ <div style={{
421
+ background: "#fff", borderRadius: "12px",
422
+ border: "1px solid #E5E9EF", overflow: "hidden",
423
+ }}>
424
+ {/* Header */}
425
+ <div style={{
426
+ display: "flex", alignItems: "center", gap: "10px",
427
+ padding: "16px 20px", borderBottom: "1px solid #F0F2F5", background: "#FAFBFC",
428
+ }}>
429
+ <div style={{
430
+ width: "32px", height: "32px", borderRadius: "8px",
431
+ background: "#EEF4FB", display: "flex", alignItems: "center", justifyContent: "center",
432
+ }}>
433
+ <DiaryIcon size={16} color="#3A7BD5" />
434
+ </div>
435
+ <span style={{ fontWeight: "700", fontSize: "15px", color: "#0D1B2A" }}>
436
+ {t("EKYC_NEW_MAPPING") || "New Mapping"}
437
+ </span>
438
+ </div>
439
+
440
+ {/* Body */}
441
+ <div style={{ padding: "20px" }}>
442
+ {/* Surveyor */}
443
+ <div style={{ marginBottom: "16px" }}>
444
+ <label style={{
445
+ display: "flex", alignItems: "center", gap: "6px",
446
+ fontSize: "11px", fontWeight: "700", letterSpacing: "0.06em",
447
+ color: "#3A7BD5", textTransform: "uppercase", marginBottom: "8px",
448
+ }}>
449
+ <UserIcon size={12} color="#3A7BD5" />
450
+ {t("EKYC_SELECT_SURVEYOR") || "Select Surveyor"}
451
+ </label>
452
+ <Dropdown
453
+ selected={selectedSurveyor}
454
+ select={setSelectedSurveyor}
455
+ option={MOCK_SURVEYORS}
456
+ optionKey="name"
457
+ t={t}
458
+ placeholder={t("EKYC_SELECT_SURVEYOR_PLACEHOLDER") || "Choose a surveyor..."}
459
+ />
460
+ </div>
461
+
462
+ {/* MRD */}
463
+ <div style={{ marginBottom: "16px" }}>
464
+ <label style={{
465
+ display: "flex", alignItems: "center", gap: "6px",
466
+ fontSize: "11px", fontWeight: "700", letterSpacing: "0.06em",
467
+ color: "#3A7BD5", textTransform: "uppercase", marginBottom: "8px",
468
+ }}>
469
+ <DiaryIcon size={12} color="#3A7BD5" />
470
+ {t("EKYC_SELECT_MRD") || "Select MRD"}
471
+ </label>
472
+ <Dropdown
473
+ selected={selectedMRD}
474
+ select={setSelectedMRD}
475
+ option={MOCK_MRDS}
476
+ optionKey="name"
477
+ t={t}
478
+ placeholder={t("EKYC_SELECT_MRD_PLACEHOLDER") || "Choose an MRD..."}
479
+ />
480
+ </div>
481
+
482
+ {/* MRD Info Preview */}
483
+ {selectedMRD && (
484
+ <div style={{
485
+ background: "#EEF4FB", borderLeft: "3px solid #3A7BD5",
486
+ borderRadius: "6px", padding: "10px 14px",
487
+ fontSize: "12px", color: "#0D1B2A", marginBottom: "16px",
488
+ display: "flex", alignItems: "center", gap: "8px",
489
+ }}>
490
+ <MapPinIcon size={13} />
491
+ <span><strong>{selectedMRD.name}</strong> — {selectedMRD.zone} Zone</span>
492
+ </div>
493
+ )}
494
+
495
+ {/* Surveyor Preview */}
496
+ {selectedSurveyor && (
497
+ <div style={{
498
+ background: "#F5F7FA", borderLeft: "3px solid #6B7B8E",
499
+ borderRadius: "6px", padding: "10px 14px",
500
+ fontSize: "12px", color: "#0D1B2A", marginBottom: "16px",
501
+ display: "flex", alignItems: "center", gap: "8px",
502
+ }}>
503
+ <UserIcon size={13} color="#6B7B8E" />
504
+ <span><strong>{selectedSurveyor.name}</strong> — {selectedSurveyor.id}</span>
505
+ </div>
506
+ )}
507
+
508
+ {/* Add Button */}
509
+ <button
510
+ onClick={handleAddMapping}
511
+ disabled={!selectedSurveyor || !selectedMRD}
512
+ style={{
513
+ width: "100%",
514
+ display: "flex", alignItems: "center", justifyContent: "center", gap: "8px",
515
+ background: (!selectedSurveyor || !selectedMRD) ? "#D0D5DD" : "#3A7BD5",
516
+ color: "#fff", border: "none", borderRadius: "8px",
517
+ padding: "12px", fontWeight: "600", fontSize: "14px",
518
+ cursor: (!selectedSurveyor || !selectedMRD) ? "not-allowed" : "pointer",
519
+ marginTop: "4px", transition: "background 0.2s",
520
+ }}
521
+ >
522
+ <PlusIcon size={16} />
523
+ {t("EKYC_ADD_TO_LIST") || "Add to Mapping List"}
524
+ </button>
525
+ </div>
526
+
527
+ {/* Stats Footer */}
528
+ {mappings.length > 0 && (
529
+ <div style={{
530
+ padding: "12px 20px", borderTop: "1px solid #F0F2F5",
531
+ background: "#FAFBFC", display: "flex", gap: "16px",
532
+ }}>
533
+ <div style={{ textAlign: "center", flex: 1 }}>
534
+ <div style={{ fontSize: "20px", fontWeight: "700", color: "#0F3460" }}>
535
+ {mappings.length}
536
+ </div>
537
+ <div style={{ fontSize: "11px", color: "#6B7B8E" }}>Total Mappings</div>
538
+ </div>
539
+ <div style={{ width: "1px", background: "#E5E9EF" }} />
540
+ <div style={{ textAlign: "center", flex: 1 }}>
541
+ <div style={{ fontSize: "20px", fontWeight: "700", color: "#0F3460" }}>
542
+ {[...new Set(mappings.map(m => m.surveyorId))].length}
543
+ </div>
544
+ <div style={{ fontSize: "11px", color: "#6B7B8E" }}>Surveyors</div>
545
+ </div>
546
+ <div style={{ width: "1px", background: "#E5E9EF" }} />
547
+ <div style={{ textAlign: "center", flex: 1 }}>
548
+ <div style={{ fontSize: "20px", fontWeight: "700", color: "#0F3460" }}>
549
+ {[...new Set(mappings.map(m => m.mrdCode))].length}
550
+ </div>
551
+ <div style={{ fontSize: "11px", color: "#6B7B8E" }}>MRDs</div>
552
+ </div>
553
+ </div>
554
+ )}
555
+ </div>
556
+
557
+ {/* ── Right: Summary Table Panel ── */}
558
+ <div style={{
559
+ background: "#fff", borderRadius: "12px",
560
+ border: "1px solid #E5E9EF", overflow: "hidden", minHeight: "420px",
561
+ }}>
562
+ {/* Header */}
563
+ <div style={{
564
+ display: "flex", alignItems: "center", justifyContent: "space-between",
565
+ padding: "16px 20px", borderBottom: "1px solid #F0F2F5", background: "#FAFBFC",
566
+ }}>
567
+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
568
+ <div style={{
569
+ width: "32px", height: "32px", borderRadius: "8px",
570
+ background: "#EEF4FB", display: "flex", alignItems: "center", justifyContent: "center",
571
+ }}>
572
+ <UserIcon size={16} color="#3A7BD5" />
573
+ </div>
574
+ <span style={{ fontWeight: "700", fontSize: "15px", color: "#0D1B2A" }}>
575
+ {t("EKYC_MAPPING_SUMMARY") || "Mapping Summary"}
576
+ </span>
577
+ </div>
578
+ <span style={{
579
+ background: mappings.length > 0 ? "#0F3460" : "#E5E9EF",
580
+ color: mappings.length > 0 ? "#fff" : "#6B7B8E",
581
+ fontSize: "12px", fontWeight: "700",
582
+ padding: "3px 10px", borderRadius: "20px",
583
+ }}>
584
+ {mappings.length} {t("EKYC_ITEMS") || "items"}
585
+ </span>
586
+ </div>
587
+
588
+ {/* Body */}
589
+ <div style={{ padding: "20px" }}>
590
+ {mappings.length > 0 ? (
591
+ <div style={{ border: "1px solid #E5E9EF", borderRadius: "8px", overflow: "hidden" }}>
592
+ <Table
593
+ t={t}
594
+ data={mappings}
595
+ columns={columns}
596
+ getCellProps={() => ({})}
597
+ tableClassName="digit-table"
598
+ />
599
+ </div>
600
+ ) : (
601
+ <div style={{
602
+ display: "flex", flexDirection: "column",
603
+ alignItems: "center", justifyContent: "center",
604
+ minHeight: "320px", textAlign: "center",
605
+ }}>
606
+ <div style={{ marginBottom: "16px", opacity: 0.2 }}>
607
+ <DiaryIcon size={56} color="#0F3460" />
608
+ </div>
609
+ <div style={{ fontSize: "15px", fontWeight: "700", color: "#0D1B2A", marginBottom: "6px" }}>
610
+ {t("EKYC_NO_MAPPINGS") || "No Mappings Created Yet"}
611
+ </div>
612
+ <div style={{ fontSize: "13px", color: "#6B7B8E" }}>
613
+ {t("EKYC_NO_MAPPINGS_HINT") || "Select a surveyor and MRD on the left to get started."}
614
+ </div>
615
+ </div>
616
+ )}
617
+ </div>
618
+ </div>
619
+ </div>
620
+ </div>
621
+
622
+ {/* Delete Confirm Modal */}
623
+ {deleteTarget && (
624
+ <ConfirmDeleteModal
625
+ mapping={deleteTarget}
626
+ onConfirm={handleDeleteConfirm}
627
+ onCancel={() => setDeleteTarget(null)}
628
+ />
629
+ )}
630
+
631
+ {/* Toast */}
632
+ {toast && (
633
+ <Toast
634
+ label={toast.message}
635
+ error={toast.type === "error"}
636
+ warning={toast.type === "warning"}
637
+ isDsc={true}
638
+ onClose={() => setToast(null)}
639
+ />
640
+ )}
7
641
  </div>
8
- )
9
- }
642
+ );
643
+ };
10
644
 
11
- export default Mapping
645
+ export default Mapping;