@djb25/digit-ui-module-ekyc 1.0.14 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djb25/digit-ui-module-ekyc",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "Digit UI Module for Ekyc",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.modern.js",
@@ -0,0 +1,317 @@
1
+ import React, { useMemo, useCallback, useReducer, useState } from "react";
2
+ import { useLocation } from "react-router-dom";
3
+ import { InboxComposer } from "@djb25/digit-ui-react-components";
4
+ import SupervisorInboxTableConfig from "../hook/SupervisorInboxTableConfig";
5
+ import SearchFormFieldsComponents from "./SearchFormFieldsComponent";
6
+
7
+ // Mock data removed in favor of API integration
8
+
9
+ const AssignEkyc = () => {
10
+ const tenantId = Digit.ULBService.getCurrentTenantId();
11
+ const location = useLocation();
12
+ const [sortParams, setSortParams] = useState([{ id: "", desc: true }]);
13
+ const [pageOffset, setPageOffset] = useState(0);
14
+ const [pageSize, setPageSize] = useState(10);
15
+ let paginationParms = { limit: pageSize, offset: pageOffset, sortBy: sortParams?.[0]?.id, sortOrder: sortParams?.[0]?.desc ? "DESC" : "ASC" };
16
+
17
+ const formInitValue = {
18
+ filterForm: {},
19
+ searchForm: {},
20
+ tableForm: {
21
+ limit: 10,
22
+ offset: 0,
23
+ sortBy: "createdTime",
24
+ sortOrder: "DESC",
25
+ },
26
+ };
27
+
28
+ const [formState, dispatch] = useReducer(formReducer, formInitValue);
29
+
30
+ const { data: dashboardData, isLoading } = Digit.Hooks.fsm.useSurveyorSearch(
31
+ tenantId,
32
+ { ...paginationParms, status: "ACTIVE,DISABLED" },
33
+ { enabled: !!tenantId, keepPreviousData: true }
34
+ );
35
+
36
+ const handleSort = useCallback((args) => {
37
+ if (args?.length === 0) return;
38
+ setSortParams(args);
39
+ }, []);
40
+
41
+ const fetchNextPage = () => {
42
+ setPageOffset((prevState) => prevState + pageSize);
43
+ };
44
+
45
+ const fetchPrevPage = () => {
46
+ setPageOffset((prevState) => prevState - pageSize);
47
+ };
48
+
49
+ const handlePageSizeChange = (e) => {
50
+ setPageSize(Number(e.target.value));
51
+ };
52
+
53
+ const searchDetails = useMemo(
54
+ () => ({
55
+ kno: formState?.searchForm?.kNumber || "",
56
+ name: formState?.searchForm?.kName || "",
57
+ }),
58
+ [formState?.searchForm?.kNumber, formState?.searchForm?.kName]
59
+ );
60
+
61
+ const isSearchActive = !!(searchDetails.kno || searchDetails.name);
62
+
63
+ const { isLoading: isSearchLoading, data: searchData } = Digit.Hooks.ekyc.useSearchConnection(
64
+ {
65
+ tenantId,
66
+ details: searchDetails,
67
+ },
68
+ {
69
+ enabled: !!tenantId && !!searchDetails.kno, // 🔥 important
70
+ keepPreviousData: true,
71
+ }
72
+ );
73
+
74
+ const sourceData = useMemo(() => {
75
+ if (isSearchActive) {
76
+ if (!searchData) return [];
77
+ return [searchData];
78
+ }
79
+
80
+ return dashboardData?.surveyors || [];
81
+ }, [isSearchActive, searchData, dashboardData]);
82
+
83
+ const filteredData = useMemo(() => {
84
+ return (sourceData || []).map((item) => {
85
+ const owner = item?.owner || {};
86
+
87
+ const roleCodes = owner?.roles?.map((role) => role.code)?.join(", ") || "";
88
+
89
+ return {
90
+ ...item,
91
+
92
+ id: item?.id || "",
93
+
94
+ surveyorName: item?.name || owner?.name || "",
95
+
96
+ mobileNo: item?.mobileNo || owner?.mobileNumber || "",
97
+
98
+ email: owner?.emailId || "",
99
+
100
+ vendorId: item?.vendorId || "",
101
+
102
+ tenantId: item?.tenantId || "",
103
+
104
+ supervisorId: item?.supervisorId || "",
105
+
106
+ status: item?.status || "",
107
+
108
+ roleCodes,
109
+
110
+ userName: owner?.userName || "",
111
+
112
+ gender: owner?.gender || "",
113
+
114
+ serviceType: item?.additionalDetails?.serviceType || "",
115
+
116
+ createdTime: item?.auditDetails?.createdTime || 0,
117
+
118
+ lastModifiedTime: item?.auditDetails?.lastModifiedTime || 0,
119
+ };
120
+ });
121
+ }, [sourceData]);
122
+
123
+ const totalRecords = dashboardData?.dashboardInfo?.totalRecords || dashboardData?.totalCount || 0;
124
+
125
+ const checkPathName = location.pathname.includes("ekyc/inbox");
126
+ const PropsForInboxLinks = {
127
+ headerText: checkPathName ? "EKYC_MODULE" : "MODULE_SW",
128
+ };
129
+
130
+ const SearchFormFields = useCallback(
131
+ ({ registerRef, searchFormState, controlSearchForm }) => (
132
+ <SearchFormFieldsComponents {...{ registerRef, searchFormState, controlSearchForm }} className="search" />
133
+ ),
134
+ []
135
+ );
136
+
137
+ const tableOrderFormDefaultValues = {
138
+ sortBy: "createdTime",
139
+ limit: window.Digit.Utils.browser.isMobile() ? 50 : 10,
140
+ offset: 0,
141
+ sortOrder: "DESC",
142
+ };
143
+
144
+ const onSearchFormSubmit = (data) => {
145
+ data.hasOwnProperty("") && delete data?.[""];
146
+ dispatch({ action: "mutateTableForm", data: { ...tableOrderFormDefaultValues }, checkPathName });
147
+ dispatch({ action: "mutateSearchForm", data, checkPathName });
148
+ };
149
+
150
+ const searchFormDefaultValues = {
151
+ mobileNumber: "",
152
+ applicationNumber: "",
153
+ consumerNo: "",
154
+ };
155
+
156
+ const onSearchFormReset = (setSearchFormValue) => {
157
+ setSearchFormValue("mobileNumber", null);
158
+ setSearchFormValue("applicationNumber", null);
159
+ setSearchFormValue("consumerNo", null);
160
+ dispatch({ action: "mutateSearchForm", data: searchFormDefaultValues });
161
+ };
162
+
163
+ const propsForSearchForm = {
164
+ SearchFormFields,
165
+ onSearchFormSubmit,
166
+ searchFormDefaultValues: formState?.searchForm,
167
+ resetSearchFormDefaultValues: searchFormDefaultValues,
168
+ onSearchFormReset,
169
+ className: "search-form-wns-inbox",
170
+ };
171
+
172
+ const FilterFormFields = useCallback(
173
+ ({ registerRef, controlFilterForm, setFilterFormValue, getFilterFormValue }) => <React.Fragment></React.Fragment>,
174
+ []
175
+ );
176
+
177
+ const propsForFilterForm = {
178
+ FilterFormFields,
179
+ onFilterFormSubmit: () => {},
180
+ filterFormDefaultValues: "",
181
+ resetFilterFormDefaultValues: "",
182
+ onFilterFormReset: () => {},
183
+ };
184
+
185
+ function formReducer(state, payload) {
186
+ const storageKey = payload.checkPathName ? "EKYC.INBOX" : "EKYC.SW.INBOX";
187
+
188
+ // ✅ safety for SLA
189
+ switch (payload.action) {
190
+ case "mutateSearchForm":
191
+ Digit.SessionStorage.set(storageKey, { ...state, searchForm: payload.data });
192
+ return { ...state, searchForm: payload.data };
193
+
194
+ case "mutateFilterForm":
195
+ Digit.SessionStorage.set(storageKey, { ...state, filterForm: payload.data });
196
+ return { ...state, filterForm: payload.data };
197
+
198
+ case "mutateTableForm":
199
+ Digit.SessionStorage.set(storageKey, { ...state, tableForm: payload.data });
200
+ return { ...state, tableForm: payload.data };
201
+
202
+ default:
203
+ return state; // ✅ IMPORTANT
204
+ }
205
+ }
206
+
207
+ const onPageSizeChange = (e) => {
208
+ const newLimit = Number(e.target.value);
209
+
210
+ dispatch({
211
+ action: "mutateTableForm",
212
+ data: {
213
+ ...formState.tableForm,
214
+ limit: newLimit,
215
+ offset: 0, // reset page
216
+ },
217
+ checkPathName,
218
+ });
219
+ };
220
+
221
+ const onSortingByData = (e) => {
222
+ if (e.length > 0) {
223
+ const [{ id, desc }] = e;
224
+ const sortOrder = desc ? "DESC" : "ASC";
225
+ const sortBy = id;
226
+
227
+ if (!(formState.tableForm.sortBy === sortBy && formState.tableForm.sortOrder === sortOrder)) {
228
+ dispatch({
229
+ action: "mutateTableForm",
230
+ data: {
231
+ ...formState.tableForm,
232
+ sortBy: id,
233
+ sortOrder: desc ? "DESC" : "ASC",
234
+ },
235
+ checkPathName,
236
+ });
237
+ }
238
+ }
239
+ };
240
+
241
+ const propsForInboxTable = SupervisorInboxTableConfig({
242
+ ...{
243
+ onPageSizeChange,
244
+ formState,
245
+ totalCount: totalRecords,
246
+ table: filteredData,
247
+ dispatch,
248
+ onSortingByData,
249
+ tenantId,
250
+ checkPathName,
251
+ inboxStyles: { overflowX: "scroll", overflowY: "hidden" },
252
+ tableStyle: { width: "70%" },
253
+ },
254
+ });
255
+
256
+ const isInboxLoading = isLoading || isSearchLoading;
257
+
258
+ const cards = [
259
+ {
260
+ label: "TOTAL_EKYC_APPLICATIONS",
261
+ count: 364,
262
+ color: "#0B2559",
263
+ filter: null,
264
+ active: true,
265
+ },
266
+ {
267
+ label: "UNASSIGNED_APPLICATIONS",
268
+ count: 28,
269
+ color: "#F59E0B",
270
+ filter: ["UNASSIGNED"],
271
+ },
272
+ {
273
+ label: "ASSIGNED_TO_SURVEYOR",
274
+ count: 120,
275
+ color: "#3B82F6",
276
+ filter: ["ASSIGNED"],
277
+ },
278
+ {
279
+ label: "IN_PROGRESS",
280
+ count: 54,
281
+ color: "#A855F7",
282
+ filter: ["IN_PROGRESS"],
283
+ },
284
+ {
285
+ label: "EKYC_COMPLETED",
286
+ count: 140,
287
+ color: "#10B981",
288
+ filter: ["COMPLETED"],
289
+ },
290
+ {
291
+ label: "REJECTED_APPLICATIONS",
292
+ count: 22,
293
+ color: "#EF4444",
294
+ filter: ["REJECTED"],
295
+ },
296
+ ];
297
+ return (
298
+ <div className="app-container">
299
+ <InboxComposer
300
+ {...{
301
+ isInboxLoading,
302
+ PropsForInboxLinks,
303
+ ...propsForSearchForm,
304
+ ...propsForFilterForm,
305
+ // ...propsForMobileSortForm,
306
+ propsForInboxTable,
307
+ // propsForInboxMobileCards,
308
+ formState,
309
+ countData: dashboardData?.dashboardInfo,
310
+ cards,
311
+ }}
312
+ />
313
+ </div>
314
+ );
315
+ };
316
+
317
+ export default AssignEkyc;
@@ -0,0 +1,362 @@
1
+ import React, { useMemo, useState } from "react";
2
+ import { Modal, Close } from "@djb25/digit-ui-react-components";
3
+
4
+ const AssignEkycModal = ({ surveyor, closeModal }) => {
5
+ const [selectedKnos, setSelectedKnos] = useState([]);
6
+
7
+ const [filters, setFilters] = useState({
8
+ pincode: "",
9
+ locality: "",
10
+ status: "",
11
+ route: "",
12
+ search: "",
13
+ });
14
+
15
+ // eslint-disable-next-line react-hooks/exhaustive-deps
16
+ const knoList = [
17
+ {
18
+ kno: "1029384756",
19
+ consumerName: "Rahul Sharma",
20
+ locality: "Rohini",
21
+ pincode: "110085",
22
+ status: "PENDING",
23
+ route: "R1",
24
+ },
25
+ {
26
+ kno: "9283746555",
27
+ consumerName: "Amit Kumar",
28
+ locality: "Pitampura",
29
+ pincode: "110034",
30
+ status: "VERIFIED",
31
+ route: "R2",
32
+ },
33
+ {
34
+ kno: "8473625147",
35
+ consumerName: "Neha Verma",
36
+ locality: "Dwarka",
37
+ pincode: "110075",
38
+ status: "PENDING",
39
+ route: "R3",
40
+ },
41
+ {
42
+ kno: "5647382910",
43
+ consumerName: "Sanjay Singh",
44
+ locality: "Janakpuri",
45
+ pincode: "110058",
46
+ status: "ASSIGNED",
47
+ route: "R1",
48
+ },
49
+ {
50
+ kno: "9182736450",
51
+ consumerName: "Priya Mehta",
52
+ locality: "Laxmi Nagar",
53
+ pincode: "110092",
54
+ status: "PENDING",
55
+ route: "R4",
56
+ },
57
+ {
58
+ kno: "7463829105",
59
+ consumerName: "Vikas Gupta",
60
+ locality: "Karol Bagh",
61
+ pincode: "110005",
62
+ status: "VERIFIED",
63
+ route: "R2",
64
+ },
65
+ {
66
+ kno: "1122334455",
67
+ consumerName: "Anjali Kapoor",
68
+ locality: "Saket",
69
+ pincode: "110017",
70
+ status: "PENDING",
71
+ route: "R5",
72
+ },
73
+ {
74
+ kno: "6677889900",
75
+ consumerName: "Rohit Yadav",
76
+ locality: "Uttam Nagar",
77
+ pincode: "110059",
78
+ status: "ASSIGNED",
79
+ route: "R3",
80
+ },
81
+ {
82
+ kno: "8899776655",
83
+ consumerName: "Deepak Chauhan",
84
+ locality: "Burari",
85
+ pincode: "110084",
86
+ status: "PENDING",
87
+ route: "R6",
88
+ },
89
+ {
90
+ kno: "5544332211",
91
+ consumerName: "Sneha Arora",
92
+ locality: "Shahdara",
93
+ pincode: "110032",
94
+ status: "VERIFIED",
95
+ route: "R4",
96
+ },
97
+ {
98
+ kno: "3344556677",
99
+ consumerName: "Karan Malhotra",
100
+ locality: "Mayur Vihar",
101
+ pincode: "110091",
102
+ status: "PENDING",
103
+ route: "R7",
104
+ },
105
+ {
106
+ kno: "9988776654",
107
+ consumerName: "Pooja Bansal",
108
+ locality: "Patel Nagar",
109
+ pincode: "110008",
110
+ status: "ASSIGNED",
111
+ route: "R5",
112
+ },
113
+ {
114
+ kno: "7766554433",
115
+ consumerName: "Harsh Jain",
116
+ locality: "Punjabi Bagh",
117
+ pincode: "110026",
118
+ status: "PENDING",
119
+ route: "R8",
120
+ },
121
+ {
122
+ kno: "2233445566",
123
+ consumerName: "Nitin Sharma",
124
+ locality: "Rajouri Garden",
125
+ pincode: "110027",
126
+ status: "VERIFIED",
127
+ route: "R1",
128
+ },
129
+ {
130
+ kno: "4433221100",
131
+ consumerName: "Megha Sethi",
132
+ locality: "Ashok Vihar",
133
+ pincode: "110052",
134
+ status: "PENDING",
135
+ route: "R9",
136
+ },
137
+ {
138
+ kno: "1010101010",
139
+ consumerName: "Aditya Rana",
140
+ locality: "Model Town",
141
+ pincode: "110009",
142
+ status: "ASSIGNED",
143
+ route: "R10",
144
+ },
145
+ {
146
+ kno: "2020202020",
147
+ consumerName: "Simran Kaur",
148
+ locality: "Tilak Nagar",
149
+ pincode: "110018",
150
+ status: "PENDING",
151
+ route: "R11",
152
+ },
153
+ {
154
+ kno: "3030303030",
155
+ consumerName: "Mohit Saini",
156
+ locality: "Narela",
157
+ pincode: "110040",
158
+ status: "VERIFIED",
159
+ route: "R6",
160
+ },
161
+ {
162
+ kno: "4040404040",
163
+ consumerName: "Ritika Sharma",
164
+ locality: "Bawana",
165
+ pincode: "110039",
166
+ status: "PENDING",
167
+ route: "R7",
168
+ },
169
+ {
170
+ kno: "5050505050",
171
+ consumerName: "Yash Aggarwal",
172
+ locality: "Okhla",
173
+ pincode: "110020",
174
+ status: "ASSIGNED",
175
+ route: "R8",
176
+ },
177
+ ];
178
+
179
+ const filteredKnos = useMemo(() => {
180
+ return knoList.filter((item) => {
181
+ const matchesPincode = filters.pincode ? item.pincode.includes(filters.pincode) : true;
182
+
183
+ const matchesLocality = filters.locality ? item.locality.toLowerCase().includes(filters.locality.toLowerCase()) : true;
184
+
185
+ const matchesStatus = filters.status ? item.status === filters.status : true;
186
+
187
+ const matchesRoute = filters.route ? item.route.toLowerCase().includes(filters.route.toLowerCase()) : true;
188
+
189
+ const matchesSearch = filters.search
190
+ ? item.kno.includes(filters.search) || item.consumerName.toLowerCase().includes(filters.search.toLowerCase())
191
+ : true;
192
+
193
+ return matchesPincode && matchesLocality && matchesStatus && matchesRoute && matchesSearch;
194
+ });
195
+ }, [filters, knoList]);
196
+
197
+ const handleSelect = (kno) => {
198
+ setSelectedKnos((prev) => (prev.includes(kno) ? prev.filter((item) => item !== kno) : [...prev, kno]));
199
+ };
200
+
201
+ const handleSelectAll = () => {
202
+ const visibleKnos = filteredKnos.map((item) => item.kno);
203
+
204
+ const allSelected = visibleKnos.every((kno) => selectedKnos.includes(kno));
205
+
206
+ if (allSelected) {
207
+ setSelectedKnos((prev) => prev.filter((kno) => !visibleKnos.includes(kno)));
208
+ } else {
209
+ setSelectedKnos((prev) => [...new Set([...prev, ...visibleKnos])]);
210
+ }
211
+ };
212
+
213
+ const handleAssign = () => {
214
+ const payload = {
215
+ surveyorId: surveyor?.uuid,
216
+ knos: selectedKnos,
217
+ filters,
218
+ };
219
+
220
+ console.log(payload);
221
+
222
+ closeModal();
223
+ };
224
+
225
+ return (
226
+ <Modal
227
+ headerBarMain={`Assign KNOs to ${surveyor?.name}`}
228
+ headerBarEnd={<Close onClick={closeModal} />}
229
+ actionCancelLabel="Cancel"
230
+ actionCancelOnSubmit={closeModal}
231
+ actionSaveLabel={`Assign ${selectedKnos.length} KNOs`}
232
+ actionSaveOnSubmit={handleAssign}
233
+ >
234
+ <div className="assign-knos-modal">
235
+ {/* Filters */}
236
+ <div className="filters-grid">
237
+ <input
238
+ className="form-control"
239
+ placeholder="Search by KNO / Consumer"
240
+ value={filters.search}
241
+ onChange={(e) =>
242
+ setFilters({
243
+ ...filters,
244
+ search: e.target.value,
245
+ })
246
+ }
247
+ />
248
+
249
+ <input
250
+ className="form-control"
251
+ placeholder="Pincode"
252
+ value={filters.pincode}
253
+ onChange={(e) =>
254
+ setFilters({
255
+ ...filters,
256
+ pincode: e.target.value,
257
+ })
258
+ }
259
+ />
260
+
261
+ <input
262
+ className="form-control"
263
+ placeholder="Locality"
264
+ value={filters.locality}
265
+ onChange={(e) =>
266
+ setFilters({
267
+ ...filters,
268
+ locality: e.target.value,
269
+ })
270
+ }
271
+ />
272
+
273
+ <input
274
+ className="form-control"
275
+ placeholder="Route"
276
+ value={filters.route}
277
+ onChange={(e) =>
278
+ setFilters({
279
+ ...filters,
280
+ route: e.target.value,
281
+ })
282
+ }
283
+ />
284
+
285
+ <select
286
+ className="form-control"
287
+ value={filters.status}
288
+ onChange={(e) =>
289
+ setFilters({
290
+ ...filters,
291
+ status: e.target.value,
292
+ })
293
+ }
294
+ >
295
+ <option value="">All Status</option>
296
+ <option value="PENDING">Pending</option>
297
+ <option value="VERIFIED">Verified</option>
298
+ <option value="ASSIGNED">Assigned</option>
299
+ </select>
300
+ </div>
301
+
302
+ {/* Summary */}
303
+ <div className="summary-bar">
304
+ <div>Total Records: {filteredKnos.length}</div>
305
+ <div>Selected KNOs: {selectedKnos.length}</div>
306
+ </div>
307
+
308
+ {/* Table */}
309
+ <div className="table-wrapper">
310
+ {/* Header */}
311
+ <div className="table-header">
312
+ <div>
313
+ <input
314
+ type="checkbox"
315
+ checked={filteredKnos.length > 0 && filteredKnos.every((item) => selectedKnos.includes(item.kno))}
316
+ onChange={handleSelectAll}
317
+ />
318
+ </div>
319
+
320
+ <div>KNO</div>
321
+ <div>Consumer Name</div>
322
+ <div>Locality</div>
323
+ <div>Pincode</div>
324
+ <div>Status</div>
325
+ <div>Route</div>
326
+ </div>
327
+
328
+ {/* Rows */}
329
+ <div className="table-body">
330
+ {filteredKnos.length > 0 ? (
331
+ filteredKnos.map((item, index) => (
332
+ <div key={item.kno} className={`table-row ${index % 2 === 0 ? "even" : "odd"}`}>
333
+ <div>
334
+ <input type="checkbox" checked={selectedKnos.includes(item.kno)} onChange={() => handleSelect(item.kno)} />
335
+ </div>
336
+
337
+ <div className="kno-value">{item.kno}</div>
338
+
339
+ <div>{item.consumerName}</div>
340
+ <div>{item.locality}</div>
341
+ <div>{item.pincode}</div>
342
+
343
+ <div>
344
+ <span className={`status-badge ${item.status === "PENDING" ? "pending" : item.status === "VERIFIED" ? "verified" : "assigned"}`}>
345
+ {item.status}
346
+ </span>
347
+ </div>
348
+
349
+ <div>{item.route}</div>
350
+ </div>
351
+ ))
352
+ ) : (
353
+ <div className="empty-state">No KNO records found</div>
354
+ )}
355
+ </div>
356
+ </div>
357
+ </div>
358
+ </Modal>
359
+ );
360
+ };
361
+
362
+ export default AssignEkycModal;
@@ -36,10 +36,12 @@ const EKYCCard = () => {
36
36
  label: t("CEO_M.F_DOR_FINANCE_VIEW"),
37
37
  link: `/digit-ui/employee/ekyc/ceo-dashboard`,
38
38
  },
39
- {
40
- label: t("EKYC_MAPPING"),
41
- link: `/digit-ui/employee/ekyc/mapping`,
42
- },
39
+
40
+ // {
41
+ // label: t("EKYC_MAPPING"),
42
+ // link: `/digit-ui/employee/ekyc/mapping`,
43
+ // },
44
+
43
45
  {
44
46
  label: t("EKYC_ASSIGN"),
45
47
  link: `/digit-ui/employee/ekyc/assign`,