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

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.5",
3
+ "version": "1.0.7",
4
4
  "description": "Digit UI Module for Ekyc",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.modern.js",
@@ -23,6 +23,8 @@
23
23
  "react": "17.0.2",
24
24
  "react-dom": "17.0.2",
25
25
  "react-i18next": "11.16.2",
26
- "react-router-dom": "5.3.0"
26
+ "react-icons": "^5.6.0",
27
+ "react-router-dom": "5.3.0",
28
+ "chart.js": "^4.4.1"
27
29
  }
28
30
  }
@@ -1,6 +1,6 @@
1
- import React, { useMemo } from "react";
1
+ import React, { useMemo, useState } from "react";
2
2
  import { useTranslation } from "react-i18next";
3
- import { Table, SubmitBar, Header, Card, HomeIcon, PersonIcon } from "@djb25/digit-ui-react-components";
3
+ import { Table, SubmitBar, Header, Card, HomeIcon, PersonIcon, Modal, Loader } from "@djb25/digit-ui-react-components";
4
4
  import { Link } from "react-router-dom";
5
5
  import StatusCards from "./StatusCards";
6
6
 
@@ -23,19 +23,148 @@ const DesktopInbox = ({ tableConfig, filterComponent, ...props }) => {
23
23
  searchFields,
24
24
  } = props;
25
25
  const { t } = useTranslation();
26
+ const tenantId = Digit.ULBService.getCurrentTenantId();
26
27
  const [FilterComponent, setComp] = React.useState(() => Digit.ComponentRegistryService?.getComponent(filterComponent));
27
28
 
29
+ // State for Review Modal
30
+ const [showReviewModal, setShowReviewModal] = useState(false);
31
+ const [reviewHtml, setReviewHtml] = useState("");
32
+ const [selectedKno, setSelectedKno] = useState("");
33
+
34
+ const generateReviewHtml = (info) => {
35
+ if (!info) return "<h3>No data found</h3>";
36
+
37
+ // Helper to format labels
38
+ const formatLabel = (str) => str.replace(/([A-Z])/g, ' $1').replace(/^./, (s) => s.toUpperCase());
39
+
40
+ return `
41
+ <!DOCTYPE html>
42
+ <html>
43
+ <head>
44
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
45
+ <style>
46
+ body { font-family: 'Inter', sans-serif; padding: 30px; color: #101828; line-height: 1.5; background: #fff; }
47
+ .header { display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 2px solid #185FA5; padding-bottom: 20px; margin-bottom: 30px; }
48
+ .title { margin: 0; color: #185FA5; font-size: 24px; font-weight: 700; }
49
+ .subtitle { margin: 5px 0 0; color: #667085; font-size: 14px; }
50
+ .section { margin-bottom: 30px; border: 1px solid #EAECF0; border-radius: 12px; overflow: hidden; }
51
+ .section-header { background: #F9FAFB; padding: 12px 20px; border-bottom: 1px solid #EAECF0; font-weight: 700; font-size: 14px; color: #344054; text-transform: uppercase; letter-spacing: 0.05em; }
52
+ .grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0; }
53
+ .item { padding: 16px 20px; border-bottom: 1px solid #F2F4F7; }
54
+ .item:nth-last-child(-n+2) { border-bottom: none; }
55
+ .label { font-size: 11px; color: #667085; text-transform: uppercase; font-weight: 600; letter-spacing: 0.02em; margin-bottom: 4px; }
56
+ .value { font-size: 14px; font-weight: 500; color: #1D2939; }
57
+ .badge { display: inline-block; padding: 4px 12px; border-radius: 16px; font-size: 12px; font-weight: 600; }
58
+ .badge-success { background: #ECFDF3; color: #027A48; }
59
+ .badge-warning { background: #FFFAEB; color: #B54708; }
60
+ .print-btn { background: #185FA5; color: #fff; padding: 10px 20px; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; display: block; margin: 20px auto; }
61
+ @media print { .print-btn { display: none; } body { padding: 0; } }
62
+ </style>
63
+ </head>
64
+ <body>
65
+ <div class="header">
66
+ <div>
67
+ <h1 class="title">Delhi Jal Board</h1>
68
+ <p class="subtitle">EKYC Application Review Summary</p>
69
+ </div>
70
+ <div style="text-align: right">
71
+ <span class="badge ${info.statusFlag === 'ACTIVE' ? 'badge-success' : 'badge-warning'}">${info.statusFlag || 'N/A'}</span>
72
+ <p class="subtitle" style="margin-top: 8px">Generated on: ${new Date().toLocaleDateString()}</p>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="section">
77
+ <div class="section-header">Basic Details</div>
78
+ <div class="grid">
79
+ <div class="item"><div class="label">KNO Number</div><div class="value">${info.kno || 'N/A'}</div></div>
80
+ <div class="item"><div class="label">Consumer Name</div><div class="value">${info.consumerName || 'N/A'}</div></div>
81
+ <div class="item"><div class="label">Mobile Number</div><div class="value">${info.mobileNo || 'N/A'}</div></div>
82
+ <div class="item"><div class="label">Email Address</div><div class="value">${info.email || 'N/A'}</div></div>
83
+ <div class="item"><div class="label">Connection Type</div><div class="value">${info.typeOfConnection || 'N/A'}</div></div>
84
+ <div class="item"><div class="label">Category</div><div class="value">${info.connectionCategory || 'N/A'}</div></div>
85
+ </div>
86
+ </div>
87
+
88
+ <div class="section">
89
+ <div class="section-header">Location & Property Information</div>
90
+ <div class="grid">
91
+ <div class="item"><div class="label">Address</div><div class="value">${info.addressRaw || 'N/A'}</div></div>
92
+ <div class="item"><div class="label">Locality</div><div class="value">${info.locality || 'N/A'}</div></div>
93
+ <div class="item"><div class="label">City - Pincode</div><div class="value">${info.city || 'N/A'} - ${info.pincode || 'N/A'}</div></div>
94
+ <div class="item"><div class="label">PID Number</div><div class="value">${info.pidNumber || 'N/A'}</div></div>
95
+ <div class="item"><div class="label">No. of Floors</div><div class="value">${info.noOfFloor || 'N/A'}</div></div>
96
+ <div class="item"><div class="label">Verification Status</div><div class="value"><span class="badge ${info.verificationStatus === 'SUCCESSFUL' ? 'badge-success' : 'badge-warning'}">${info.verificationStatus || 'PENDING'}</span></div></div>
97
+ </div>
98
+ </div>
99
+
100
+ <div class="section">
101
+ <div class="section-header">Meter Information</div>
102
+ <div class="grid">
103
+ <div class="item"><div class="label">Meter Number</div><div class="value">${info.meterNumber || 'N/A'}</div></div>
104
+ <div class="item"><div class="label">Meter Make</div><div class="value">${info.meterMake || 'N/A'}</div></div>
105
+ <div class="item"><div class="label">Meter Location</div><div class="value">${info.meterLocationAddress || 'N/A'}</div></div>
106
+ <div class="item"><div class="label">Working Status</div><div class="value">${info.workingStatus ? 'Working' : 'Not Working'}</div></div>
107
+ </div>
108
+ </div>
109
+
110
+ <button class="print-btn" onclick="window.print()">Print This Review</button>
111
+ </body>
112
+ </html>
113
+ `;
114
+ };
115
+
116
+ // Use the library hook if available, otherwise fallback to local definition
117
+ // This is a safety measure to handle stale library builds in dev environment
118
+ const useReviewHook = Digit.Hooks.ekyc?.useEkycApplicationReview || ((p, config) => {
119
+ return Digit.Hooks.useMutation((data) => Digit.EkycService.application_review(data, p), config);
120
+ });
121
+
122
+ const { mutate: getReview, isLoading: isReviewLoading } = useReviewHook(
123
+ { tenantId },
124
+ {
125
+ onSuccess: (res) => {
126
+ if (res?.applicationReviewInfo) {
127
+ const html = generateReviewHtml(res.applicationReviewInfo);
128
+ setReviewHtml(html);
129
+ setShowReviewModal(true);
130
+ } else {
131
+ // Fallback to URL method if the API is updated later to return a URL
132
+ const url = res?.acknowledgementURL || res?.reviewUrl || res?.url;
133
+ if (url) {
134
+ setReviewHtml(""); // Clear HTML so iframe uses URL
135
+ setReviewUrl(url);
136
+ setShowReviewModal(true);
137
+ } else {
138
+ alert(t("EKYC_REVIEW_INFO_NOT_FOUND"));
139
+ }
140
+ }
141
+ },
142
+ onError: (err) => {
143
+ alert(err?.message || t("ERR_FAILED_TO_FETCH_REVIEW"));
144
+ }
145
+ }
146
+ );
147
+
148
+ const handleReview = (kno) => {
149
+ setSelectedKno(kno);
150
+ getReview({ kno });
151
+ };
152
+
28
153
  const columns = useMemo(
29
154
  () => [
30
155
  {
31
156
  Header: t("EKYC_APPLICATION_NO"),
32
157
  accessor: "applicationNumber",
33
158
  Cell: ({ row }) => {
34
- const applicationNumber = row.original?.applicationNumber || "NA";
159
+ const kno = row.original?.kno || row.original?.applicationNumber || "NA";
35
160
  return (
36
- <Link to={`${parentRoute}/application-details/${applicationNumber}`}>
37
- <span className="ekyc-application-link">{applicationNumber}</span>
38
- </Link>
161
+ <span
162
+ className="ekyc-application-link"
163
+ style={{ color: "#add8f7", cursor: "pointer", fontWeight: "bold" }}
164
+ onClick={() => handleReview(kno)}
165
+ >
166
+ {kno}
167
+ </span>
39
168
  );
40
169
  },
41
170
  },
@@ -54,7 +183,7 @@ const DesktopInbox = ({ tableConfig, filterComponent, ...props }) => {
54
183
  accessor: "status",
55
184
  Cell: ({ row }) => {
56
185
  const status = row.original?.status || "DEFAULT";
57
- return <span className={`ekyc-status-tag ${status}`}>{t(`EKYC_STATUS_${status}`)}</span>;
186
+ return <span className={`ekyc-status-tag ${status}`}>{t(`${status}`)}</span>;
58
187
  },
59
188
  },
60
189
  ],
@@ -66,69 +195,88 @@ const DesktopInbox = ({ tableConfig, filterComponent, ...props }) => {
66
195
  }, [data]);
67
196
 
68
197
  return (
69
- <div className="inbox-container">
70
- <div className="filters-container">
71
- {/* Sidebar Title Card */}
72
- <Card
73
- className="sidebar-title-card"
74
- style={{ display: "flex", alignItems: "center", padding: "16px", marginBottom: "16px", borderRadius: "4px" }}
75
- >
76
- <div className="icon-container" style={{ color: "#3A8DCC", marginRight: "12px" }}>
77
- <HomeIcon style={{ width: "24px", height: "24px" }} />
78
- </div>
79
- <div style={{ fontWeight: "700", fontSize: "18px", color: "#0B0C0C" }}>{t("ACTION_TEST_EKYC")}</div>
80
- </Card>
81
-
82
- <div>
83
- {FilterComponent && (
84
- <FilterComponent
85
- defaultSearchParams={props.defaultSearchParams}
86
- onFilterChange={props.onSearch}
87
- searchParams={searchParams}
88
- type="desktop"
89
- moduleCode="EKYC"
198
+ <div className="ground-container employee-app-container form-container">
199
+ <div className="inbox-container" style={{ paddingBottom: "16px" }}>
200
+ {showReviewModal && (
201
+ <Modal
202
+ headerBarMain={t("EKYC_APPLICATION_REVIEW") + (selectedKno ? ` - ${selectedKno}` : "")}
203
+ headerBarEnd={<div style={{ cursor: "pointer", padding: "5px 10px", background: "#F2F4F7", borderRadius: "4px" }} onClick={() => setShowReviewModal(false)}>{t("CLOSE")}</div>}
204
+ hideSubmit={true}
205
+ popupStyles={{ width: "90%", height: "90%", maxWidth: "1000px" }}
206
+ popupModuleMianStyles={{ height: "calc(100% - 60px)", padding: 0 }}
207
+ >
208
+ <iframe
209
+ srcDoc={reviewHtml}
210
+ src={!reviewHtml ? reviewUrl : undefined}
211
+ title="Application Review"
212
+ style={{ width: "100%", height: "100%", border: "none" }}
90
213
  />
91
- )}
214
+ </Modal>
215
+ )}
216
+ {(isLoading || isReviewLoading) && <Loader />}
217
+ <div className="filters-container">
218
+ {/* Sidebar Title Card */}
219
+ <Card
220
+ className="sidebar-title-card"
221
+ style={{ display: "flex", alignItems: "center", padding: "16px", marginBottom: "16px", borderRadius: "4px" }}
222
+ >
223
+ <div className="icon-container" style={{ color: "#3A8DCC", marginRight: "12px" }}>
224
+ <HomeIcon style={{ width: "24px", height: "24px" }} />
225
+ </div>
226
+ <div style={{ fontWeight: "700", fontSize: "18px", color: "#0B0C0C" }}>{t("ACTION_TEST_EKYC")}</div>
227
+ </Card>
228
+
229
+ <div>
230
+ {FilterComponent && (
231
+ <FilterComponent
232
+ defaultSearchParams={props.defaultSearchParams}
233
+ onFilterChange={props.onSearch}
234
+ searchParams={searchParams}
235
+ type="desktop"
236
+ moduleCode="EKYC"
237
+ />
238
+ )}
239
+ </div>
92
240
  </div>
93
- </div>
94
241
 
95
- <div style={{ flex: 1, marginLeft: "16px" }}>
96
- {/* Header Section (retaining for context/actions) */}
97
- {/* <div className="ekyc-header-container module-header" style={{ marginBottom: "16px", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
242
+ <div style={{ flex: 1, marginLeft: "16px" }}>
243
+ {/* Header Section (retaining for context/actions) */}
244
+ {/* <div className="ekyc-header-container module-header" style={{ marginBottom: "16px", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
98
245
  <Header className="title" style={{ margin: 0 }}>{t("EKYC_INBOX_HEADER")}</Header>
99
246
  <Link to={`${parentRoute}/create-kyc`}>
100
247
  <SubmitBar label={t("EKYC_CREATE_KYC")} style={{ borderRadius: "8px" }} />
101
248
  </Link>
102
249
  </div> */}
103
250
 
104
- {/* Metrics Section (The Card) */}
105
- <Card className="ekyc-metrics-card" style={{ marginBottom: "16px", padding: "16px" }}>
106
- <StatusCards countData={countData} />
107
- </Card>
108
-
109
- {/* Table Section */}
110
- <div className="result" style={{ flex: 1 }}>
111
- <Card className="ekyc-table-card" style={{ padding: 0 }}>
112
- <Table
113
- t={t}
114
- data={tableData}
115
- columns={columns}
116
- isLoading={isLoading}
117
- onSort={onSort}
118
- sortParams={sortParams}
119
- totalRecords={totalRecords}
120
- onNextPage={onNextPage}
121
- onPrevPage={onPrevPage}
122
- currentPage={currentPage}
123
- pageSizeLimit={pageSizeLimit}
124
- onPageSizeChange={onPageSizeChange}
125
- getCellProps={(cellInfo) => {
126
- return {
127
- className: "ekyc-table-cell",
128
- };
129
- }}
130
- />
251
+ {/* Metrics Section (The Card) */}
252
+ <Card className="ekyc-metrics-card" style={{ marginBottom: "16px", padding: "16px" }}>
253
+ <StatusCards countData={countData} />
131
254
  </Card>
255
+
256
+ {/* Table Section */}
257
+ <div className="result" style={{ flex: 1 }}>
258
+ <Card className="ekyc-table-card" style={{ padding: 0 }}>
259
+ <Table
260
+ t={t}
261
+ data={tableData}
262
+ columns={columns}
263
+ isLoading={isLoading}
264
+ onSort={onSort}
265
+ sortParams={sortParams}
266
+ totalRecords={totalRecords}
267
+ onNextPage={onNextPage}
268
+ onPrevPage={onPrevPage}
269
+ currentPage={currentPage}
270
+ pageSizeLimit={pageSizeLimit}
271
+ onPageSizeChange={onPageSizeChange}
272
+ getCellProps={(cellInfo) => {
273
+ return {
274
+ className: "ekyc-table-cell",
275
+ };
276
+ }}
277
+ />
278
+ </Card>
279
+ </div>
132
280
  </div>
133
281
  </div>
134
282
  </div>
@@ -25,7 +25,9 @@ const Filter = ({ searchParams, onFilterChange, defaultSearchParams, statusMap,
25
25
  };
26
26
 
27
27
  const onStatusChange = (value) => {
28
- localParamChange({ status: value });
28
+ const newParams = { ..._searchParams, status: value };
29
+ setSearchParams(newParams);
30
+ onFilterChange(newParams);
29
31
  };
30
32
 
31
33
  return (
@@ -43,9 +45,8 @@ const Filter = ({ searchParams, onFilterChange, defaultSearchParams, statusMap,
43
45
  <Dropdown
44
46
  option={[
45
47
  { label: t("EKYC_STATUS_ALL"), value: "" },
46
- { label: t("EKYC_STATUS_COMPLETED"), value: "COMPLETED" },
47
- { label: t("EKYC_STATUS_PENDING"), value: "PENDING" },
48
- { label: t("EKYC_STATUS_REJECTED"), value: "REJECTED" },
48
+ { label: t("EKYC_STATUS_ACTIVE"), value: "ACTIVE" },
49
+ { label: t("EKYC_STATUS_PENDING"), value: "PENDING START" },
49
50
  ]}
50
51
  optionKey="label"
51
52
  select={onStatusChange}
@@ -1,26 +1,177 @@
1
- import React from "react";
2
- import { Card } from "@djb25/digit-ui-react-components";
1
+ import React, { useEffect, useRef } from "react";
3
2
  import { useTranslation } from "react-i18next";
3
+ import { Chart, registerables } from "chart.js";
4
+
5
+ Chart.register(...registerables);
4
6
 
5
7
  const StatusCards = ({ countData }) => {
6
- const { t } = useTranslation();
8
+ const { t } = useTranslation();
9
+ const chartRef1 = useRef(null);
10
+ const chartInstance1 = useRef(null);
11
+ const chartRef2 = useRef(null);
12
+ const chartInstance2 = useRef(null);
13
+
14
+ const total = countData?.total || 0;
15
+ const pending = countData?.pending || 0;
16
+ const active = countData?.completed || 0; // Showing completed data as active
17
+ const completed = 0; // Forced to 0
18
+ const rejected = countData?.rejected || 0;
19
+
20
+ const actualCompleted = countData?.completed || 0;
21
+ const applied = total;
22
+ const approved = actualCompleted;
23
+
24
+ const efficiency = total > 0 ? Math.round((actualCompleted / total) * 100) : 0;
25
+
26
+ useEffect(() => {
27
+ // Chart 1: Status Breakdown
28
+ if (chartRef1.current) {
29
+ if (chartInstance1.current) chartInstance1.current.destroy();
30
+ const ctx1 = chartRef1.current.getContext("2d");
31
+ chartInstance1.current = new Chart(ctx1, {
32
+ type: "doughnut",
33
+ data: {
34
+ labels: [t("EKYC_ACTIVE"), t("EKYC_COMPLETED"), t("EKYC_PENDING")],
35
+ datasets: [{
36
+ data: [active, completed, pending],
37
+ backgroundColor: ["#1a3a6b", "#77B6EA", "#3d84ed"],
38
+ borderColor: ["#ffffff", "#ffffff", "#ffffff"],
39
+ borderWidth: 2,
40
+ hoverOffset: 4,
41
+ }],
42
+ },
43
+ options: {
44
+ cutout: "75%",
45
+ plugins: { legend: { display: false } },
46
+ maintainAspectRatio: true,
47
+ responsive: true,
48
+ aspectRatio: 1,
49
+ },
50
+ });
51
+ }
52
+
53
+ // Chart 2: Applied vs Approved
54
+ if (chartRef2.current) {
55
+ if (chartInstance2.current) chartInstance2.current.destroy();
56
+ const ctx2 = chartRef2.current.getContext("2d");
57
+ chartInstance2.current = new Chart(ctx2, {
58
+ type: "doughnut",
59
+ data: {
60
+ labels: [t("EKYC_APPROVED"), t("EKYC_OTHERS")],
61
+ datasets: [{
62
+ data: [approved, Math.max(0, applied - approved)],
63
+ backgroundColor: ["#219653", "#E0E0E0"],
64
+ borderColor: ["#ffffff", "#ffffff"],
65
+ borderWidth: 2,
66
+ hoverOffset: 4,
67
+ }],
68
+ },
69
+ options: {
70
+ cutout: "75%",
71
+ plugins: { legend: { display: false } },
72
+ maintainAspectRatio: true,
73
+ responsive: true,
74
+ aspectRatio: 1,
75
+ },
76
+ });
77
+ }
7
78
 
8
- return (
9
- <div className="ekyc-status-container">
10
- <div className="ekyc-status-card">
11
- <div className="count">{countData?.total || 0}</div>
12
- <div className="label">{t("EKYC_TOTAL")}</div>
79
+ return () => {
80
+ if (chartInstance1.current) chartInstance1.current.destroy();
81
+ if (chartInstance2.current) chartInstance2.current.destroy();
82
+ };
83
+ }, [pending, completed, active, applied, approved, t]);
84
+
85
+ const formatNumber = (num) => {
86
+ return new Intl.NumberFormat("en-IN").format(num || 0);
87
+ };
88
+
89
+ return (
90
+ <div style={{ fontFamily: "'Segoe UI', sans-serif", width: "100%" }}>
91
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", flexWrap: "wrap", gap: "24px" }}>
92
+
93
+ {/* Statistics Section */}
94
+ <div style={{ flex: "1", minWidth: "250px" }}>
95
+ <div style={{ fontSize: "11px", fontWeight: "700", color: "#888", letterSpacing: "1px", marginBottom: "16px", textTransform: "uppercase" }}>
96
+ {t("EKYC_DASHBOARD_METRICS") || "DASHBOARD METRICS"}
97
+ </div>
98
+
99
+ <div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
100
+ {/* Active */}
101
+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
102
+ <span style={{ width: "12px", height: "12px", borderRadius: "50%", backgroundColor: "#1a3a6b", flexShrink: 0 }} />
103
+ <span style={{ fontSize: "14px", color: "#333" }}>
104
+ {t("EKYC_ACTIVE")}: <strong style={{ color: "#1a3a6b" }}>{formatNumber(active)}</strong>
105
+ </span>
13
106
  </div>
14
- <div className="ekyc-status-card">
15
- <div className="count pending">{countData?.pending || 0}</div>
16
- <div className="label">{t("EKYC_PENDING")}</div>
107
+
108
+ {/* Pending */}
109
+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
110
+ <span style={{ width: "12px", height: "12px", borderRadius: "50%", backgroundColor: "#3d84ed", flexShrink: 0 }} />
111
+ <span style={{ fontSize: "14px", color: "#333" }}>
112
+ {t("EKYC_PENDING")}: <strong style={{ color: "#1a3a6b" }}>{formatNumber(pending)}</strong>
113
+ </span>
17
114
  </div>
18
- <div className="ekyc-status-card">
19
- <div className="count completed">{countData?.completed || 0}</div>
20
- <div className="label">{t("EKYC_COMPLETED")}</div>
115
+
116
+ {/* Completed */}
117
+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
118
+ <span style={{ width: "12px", height: "12px", borderRadius: "50%", backgroundColor: "#77B6EA", flexShrink: 0 }} />
119
+ <span style={{ fontSize: "14px", color: "#333" }}>
120
+ {t("EKYC_COMPLETED")}: <strong style={{ color: "#1a3a6b" }}>{formatNumber(completed)}</strong>
121
+ </span>
122
+ </div>
123
+
124
+ {/* Total Section */}
125
+ <div style={{ marginTop: "20px", padding: "16px", background: "#f8faff", borderRadius: "8px", border: "1px solid #eef2f6" }}>
126
+ <div style={{ fontSize: "32px", fontWeight: "800", color: "#1a3a6b", lineHeight: 1 }}>
127
+ {formatNumber(total)}
128
+ </div>
129
+ <div style={{ fontSize: "12px", color: "#667085", marginTop: "4px", fontWeight: "600" }}>
130
+ {t("EKYC_TOTAL_APPLICATIONS") || "Total Applications Applied"}
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ {/* Visualizations Section */}
137
+ <div style={{ display: "flex", gap: "32px", alignItems: "center", justifyContent: "flex-end", flexWrap: "wrap" }}>
138
+
139
+ {/* Chart 1: Status Breakdown */}
140
+ <div style={{ textAlign: "center" }}>
141
+ <div style={{ width: "140px", height: "140px", position: "relative" }}>
142
+ <canvas ref={chartRef1} />
143
+ <div style={{ position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", textAlign: "center", pointerEvents: "none", width: "100%" }}>
144
+ {/* <div style={{ fontSize: "16px", fontWeight: "800", color: "#1a3a6b", lineHeight: 1.2 }}>
145
+ {formatNumber(active)} / {formatNumber(total)}
146
+ </div> */}
147
+ <div style={{ fontSize: "9px", color: "#667085", fontWeight: "700", marginTop: "4px" }}>
148
+ {efficiency}%
149
+ </div>
150
+ </div>
21
151
  </div>
152
+ <div style={{ marginTop: "8px", fontSize: "11px", fontWeight: "700", color: "#888", textTransform: "uppercase" }}>{t("EKYC_STATUS_BREAKDOWN")}</div>
153
+ </div>
154
+
155
+ {/* Chart 2: Applied vs Approved */}
156
+ <div style={{ textAlign: "center" }}>
157
+ <div style={{ width: "140px", height: "140px", position: "relative" }}>
158
+ <canvas ref={chartRef2} />
159
+ <div style={{ position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", textAlign: "center", pointerEvents: "none", width: "100%" }}>
160
+ {/* <div style={{ fontSize: "16px", fontWeight: "800", color: "#219653", lineHeight: 1.2 }}>
161
+ {formatNumber(approved)} / {formatNumber(applied)}
162
+ </div> */}
163
+ <div style={{ fontSize: "9px", color: "#667085", fontWeight: "700", marginTop: "4px" }}>
164
+ {total > 0 ? Math.round((approved / total) * 100) : 0}%
165
+ </div>
166
+ </div>
167
+ </div>
168
+ <div style={{ marginTop: "8px", fontSize: "11px", fontWeight: "700", color: "#888", textTransform: "uppercase" }}>{t("EKYC_SUBMISSION_HEALTH")}</div>
169
+ </div>
170
+
22
171
  </div>
23
- );
172
+ </div>
173
+ </div>
174
+ );
24
175
  };
25
176
 
26
- export default StatusCards;
177
+ export default StatusCards;