@djb25/digit-ui-module-ekyc 1.0.13 → 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/dist/index.css +54 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +3334 -3345
- package/dist/index.modern.js.map +1 -1
- package/package.json +34 -29
- package/src/Module.js +2 -5
- package/src/components/AssignEkyc.js +317 -0
- package/src/components/AssignEkycModal.js +362 -0
- package/src/components/CeoDashboard.js +255 -192
- package/src/components/CeoDashboard.jsx +334 -0
- package/src/components/DesktopInbox.js +8 -0
- package/src/components/EKYCCard.js +9 -3
- package/src/components/Review.js +155 -88
- package/src/components/StatusCards.js +6 -17
- package/src/components/SurveyorDetailsCard.js +282 -0
- package/src/components/VendorDetails.jsx +231 -0
- package/src/components/analytics/charts/ExecutiveBarChart.jsx +30 -0
- package/src/components/analytics/charts/ExecutiveLineChart.jsx +159 -0
- package/src/components/analytics/charts/ExecutivePieChart.jsx +24 -0
- package/src/components/analytics/charts/TaskStatusChart.js +1 -3
- package/src/components/analytics/components/DashboardLayout.js +26 -46
- package/src/components/mockData.js +772 -0
- package/src/hook/SupervisorInboxTableConfig.js +138 -0
- package/src/hook/useInboxTableConfig.js +12 -3
- package/src/pages/citizen/Home.js +27 -14
- package/src/pages/citizen/index.js +48 -1
- package/src/pages/employee/Mapping.js +162 -449
- package/src/pages/employee/index.js +96 -70
- package/src/components/analytics/styles/Dashboard.css +0 -54
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
|
+
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid, PieChart, Pie, Cell, Legend } from "recharts";
|
|
3
|
+
|
|
4
|
+
import { Card, SubmitBar, ActionBar, Menu, Loader, Table } from "@djb25/digit-ui-react-components";
|
|
5
|
+
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
7
|
+
import { useParams } from "react-router-dom";
|
|
8
|
+
|
|
9
|
+
import AssignEkycModal from "./AssignEkycModal";
|
|
10
|
+
|
|
11
|
+
const SurveyorDetailsDashboard = () => {
|
|
12
|
+
const tenantId = Digit.ULBService.getCurrentTenantId();
|
|
13
|
+
|
|
14
|
+
const [showModal, setShowModal] = useState(false);
|
|
15
|
+
const [showOptions, setShowOptions] = useState(false);
|
|
16
|
+
|
|
17
|
+
const { id: surveyorId } = useParams();
|
|
18
|
+
const ownerIds = Digit.SessionStorage.get("User")?.info?.uuid;
|
|
19
|
+
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
|
|
22
|
+
const searchParams = surveyorId ? { ids: surveyorId } : { ownerIds };
|
|
23
|
+
|
|
24
|
+
const { data: surveyorSearchResponse, isLoading } = Digit.Hooks.fsm.useSurveyorSearch(tenantId, searchParams, { staleTime: Infinity });
|
|
25
|
+
|
|
26
|
+
const roles = Digit.SessionStorage.get("User")?.info?.roles.map((ele) => ele.code);
|
|
27
|
+
|
|
28
|
+
const surveyor = useMemo(() => {
|
|
29
|
+
return surveyorSearchResponse?.surveyors?.[0] || null;
|
|
30
|
+
}, [surveyorSearchResponse]);
|
|
31
|
+
|
|
32
|
+
const knoColumns = React.useMemo(
|
|
33
|
+
() => [
|
|
34
|
+
{
|
|
35
|
+
Header: t("KNO"),
|
|
36
|
+
accessor: "kno",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
Header: t("CONSUMER_NAME"),
|
|
40
|
+
accessor: "consumerName",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
Header: t("MOBILE_NO"),
|
|
44
|
+
accessor: "mobileNumber",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
Header: t("STATUS"),
|
|
48
|
+
accessor: "status",
|
|
49
|
+
Cell: ({ value }) => <span className={`status-badge ${value?.toLowerCase() || ""}`}>{value}</span>,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
Header: t("ASSIGNED_DATE"),
|
|
53
|
+
accessor: "assignedDate",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
Header: t("COMPLETED_DATE"),
|
|
57
|
+
accessor: "completedDate",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
[t]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (isLoading) {
|
|
64
|
+
return <Loader />;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!surveyor) {
|
|
68
|
+
return (
|
|
69
|
+
<Card>
|
|
70
|
+
<div style={{ padding: "24px" }}>{t("NO_SURVEYOR_FOUND")}</div>
|
|
71
|
+
</Card>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const assignedKnos = [
|
|
75
|
+
{
|
|
76
|
+
kno: "123456789",
|
|
77
|
+
consumerName: "Rahul Sharma",
|
|
78
|
+
mobileNumber: "9876543210",
|
|
79
|
+
status: "Completed",
|
|
80
|
+
assignedDate: "01-06-2026",
|
|
81
|
+
completedDate: "02-06-2026",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
kno: "123456790",
|
|
85
|
+
consumerName: "Amit Kumar",
|
|
86
|
+
mobileNumber: "9876543211",
|
|
87
|
+
status: "Pending",
|
|
88
|
+
assignedDate: "03-06-2026",
|
|
89
|
+
completedDate: "-",
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
const weeklyData = [
|
|
94
|
+
{ day: "Mon", completed: 4 },
|
|
95
|
+
{ day: "Tue", completed: 6 },
|
|
96
|
+
{ day: "Wed", completed: 3 },
|
|
97
|
+
{ day: "Thu", completed: 8 },
|
|
98
|
+
{ day: "Fri", completed: 5 },
|
|
99
|
+
{ day: "Sat", completed: 7 },
|
|
100
|
+
{ day: "Sun", completed: 2 },
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
const statusData = [
|
|
104
|
+
{
|
|
105
|
+
name: "Completed",
|
|
106
|
+
value: surveyor?.completedCases || 0,
|
|
107
|
+
color: "#10B981",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "Pending",
|
|
111
|
+
value: surveyor?.pendingCases || 0,
|
|
112
|
+
color: "#F59E0B",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "Rejected",
|
|
116
|
+
value: surveyor?.rejectedCases || 0,
|
|
117
|
+
color: "#EF4444",
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
const StatCard = ({ title, value, type }) => (
|
|
122
|
+
<div className={`stat-card ${type}`}>
|
|
123
|
+
<div className="stat-title">{title}</div>
|
|
124
|
+
|
|
125
|
+
<div className="stat-value">{value}</div>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const options = [{ action: "Assign" }];
|
|
130
|
+
|
|
131
|
+
const fullName = surveyor?.owner?.name || surveyor?.name || "N/A";
|
|
132
|
+
|
|
133
|
+
const employeeId = surveyor?.employeeId || surveyor?.owner?.uuid || surveyor?.id;
|
|
134
|
+
|
|
135
|
+
const handleMenuSelect = (option) => {
|
|
136
|
+
setShowOptions(false); // close menu
|
|
137
|
+
setShowModal(true);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Card className="surveyor-dashboard">
|
|
142
|
+
{/* Header */}
|
|
143
|
+
<div className="ekyc-dashboard-section">
|
|
144
|
+
<div className="ekyc-dashboard-header">
|
|
145
|
+
<div className="avatar">{fullName?.charAt(0)?.toUpperCase()}</div>
|
|
146
|
+
|
|
147
|
+
<div className="header-content">
|
|
148
|
+
<h2 className="name">{fullName}</h2>
|
|
149
|
+
|
|
150
|
+
<div className="designation">{surveyor?.description || t("FIELD_SURVEYOR")}</div>
|
|
151
|
+
|
|
152
|
+
<div className="employee-id">
|
|
153
|
+
{t("EMPLOYEE_ID")}: {employeeId}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
{/* Stats */}
|
|
160
|
+
<div className="stats-wrapper">
|
|
161
|
+
<StatCard title={t("TODAYS_EKYC")} value={surveyor?.todayCompleted || 0} type="today" />
|
|
162
|
+
|
|
163
|
+
<StatCard title={t("THIS_WEEK")} value={surveyor?.weekCompleted || 0} type="week" />
|
|
164
|
+
|
|
165
|
+
<StatCard title={t("THIS_MONTH")} value={surveyor?.monthCompleted || 0} type="month" />
|
|
166
|
+
|
|
167
|
+
<StatCard title={t("PENDING_CASES")} value={surveyor?.pendingCases || 0} type="pending" />
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{/* Charts */}
|
|
171
|
+
<div className="charts-wrapper">
|
|
172
|
+
{/* Weekly Chart */}
|
|
173
|
+
<div className="chart-card">
|
|
174
|
+
<h3 className="chart-title">{t("WEEKLY_SURVEY_PROGRESS")}</h3>
|
|
175
|
+
|
|
176
|
+
<ResponsiveContainer width="100%" height={300}>
|
|
177
|
+
<BarChart data={weeklyData}>
|
|
178
|
+
<CartesianGrid strokeDasharray="3 3" />
|
|
179
|
+
|
|
180
|
+
<XAxis dataKey="day" />
|
|
181
|
+
|
|
182
|
+
<YAxis />
|
|
183
|
+
|
|
184
|
+
<Tooltip />
|
|
185
|
+
|
|
186
|
+
<Bar dataKey="completed" fill="#0B2559" radius={[6, 6, 0, 0]} />
|
|
187
|
+
</BarChart>
|
|
188
|
+
</ResponsiveContainer>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
{/* Pie Chart */}
|
|
192
|
+
<div className="chart-card">
|
|
193
|
+
<h3 className="chart-title">{t("CASE_DISTRIBUTION")}</h3>
|
|
194
|
+
|
|
195
|
+
<ResponsiveContainer width="100%" height={300}>
|
|
196
|
+
<PieChart>
|
|
197
|
+
<Pie data={statusData} dataKey="value" nameKey="name" outerRadius={90} label>
|
|
198
|
+
{statusData.map((entry, index) => (
|
|
199
|
+
<Cell key={index} fill={entry.color} />
|
|
200
|
+
))}
|
|
201
|
+
</Pie>
|
|
202
|
+
|
|
203
|
+
<Tooltip />
|
|
204
|
+
|
|
205
|
+
<Legend />
|
|
206
|
+
</PieChart>
|
|
207
|
+
</ResponsiveContainer>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
{/* Details */}
|
|
212
|
+
<div className="ekyc-dashboard-section">
|
|
213
|
+
<div className="details-grid">
|
|
214
|
+
<div className="detail-item">
|
|
215
|
+
<span className="label">{t("MOBILE")}:</span>
|
|
216
|
+
<span className="value">{surveyor?.owner?.mobileNumber || surveyor?.mobileNo || "N/A"}</span>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<div className="detail-item">
|
|
220
|
+
<span className="label">{t("EMAIL")}:</span>
|
|
221
|
+
<span className="value">{surveyor?.owner?.emailId || "N/A"}</span>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<div className="detail-item">
|
|
225
|
+
<span className="label">{t("GENDER")}:</span>
|
|
226
|
+
<span className="value">{surveyor?.owner?.gender || "N/A"}</span>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<div className="detail-item">
|
|
230
|
+
<span className="label">{t("STATUS")}:</span>
|
|
231
|
+
<span className="value">{surveyor?.status || "N/A"}</span>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<div className="detail-item">
|
|
235
|
+
<span className="label">{t("SERVICE_TYPE")}:</span>
|
|
236
|
+
<span className="value">{surveyor?.additionalDetails?.serviceType || "N/A"}</span>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div className="detail-item">
|
|
240
|
+
<span className="label">{t("VENDOR_ID")}:</span>
|
|
241
|
+
<span className="value">{surveyor?.vendorId || "N/A"}</span>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<Table
|
|
247
|
+
t={t}
|
|
248
|
+
tableTitle={t("ASSIGNED_KNOS")}
|
|
249
|
+
data={assignedKnos}
|
|
250
|
+
columns={knoColumns}
|
|
251
|
+
totalRecords={assignedKnos?.length}
|
|
252
|
+
isPaginationRequired={true}
|
|
253
|
+
pageSizeLimit={10}
|
|
254
|
+
manualPagination={false}
|
|
255
|
+
/>
|
|
256
|
+
|
|
257
|
+
{/* Actions */}
|
|
258
|
+
{(!roles.includes("EKYC_SURVEYOR") || roles.includes("EMPLOYEE")) && (
|
|
259
|
+
<ActionBar>
|
|
260
|
+
<SubmitBar label={t("EKYC_ASSIGN_KNOS")} onSubmit={() => setShowOptions((prev) => !prev)} />
|
|
261
|
+
|
|
262
|
+
{showOptions && (
|
|
263
|
+
<Menu
|
|
264
|
+
options={options}
|
|
265
|
+
optionKey={"action"}
|
|
266
|
+
t={t}
|
|
267
|
+
onSelect={handleMenuSelect}
|
|
268
|
+
style={{
|
|
269
|
+
color: "#FFFFFF",
|
|
270
|
+
fontSize: "18px",
|
|
271
|
+
}}
|
|
272
|
+
/>
|
|
273
|
+
)}
|
|
274
|
+
</ActionBar>
|
|
275
|
+
)}
|
|
276
|
+
|
|
277
|
+
{showModal && <AssignEkycModal surveyor={surveyor} closeModal={() => setShowModal(false)} />}
|
|
278
|
+
</Card>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
export default SurveyorDetailsDashboard;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { useHistory, useParams } from "react-router-dom";
|
|
3
|
+
import { FaArrowLeft, FaUsers, FaCheckCircle, FaClock, FaExclamationTriangle, FaMapMarkedAlt } from "react-icons/fa";
|
|
4
|
+
import { ekycMockData } from "./mockData";
|
|
5
|
+
import ExecutiveLineChart from "./analytics/charts/ExecutiveLineChart";
|
|
6
|
+
import ExecutiveBarChart from "./analytics/charts/ExecutiveBarChart";
|
|
7
|
+
import ExecutivePieChart from "./analytics/charts/ExecutivePieChart";
|
|
8
|
+
import { Loader } from "@djb25/digit-ui-react-components";
|
|
9
|
+
|
|
10
|
+
const VendorDetails = () => {
|
|
11
|
+
const history = useHistory();
|
|
12
|
+
const { vendorId = 1 } = useParams();
|
|
13
|
+
const vendor = useMemo(() => ekycMockData.vendors.find((item) => item.id === Number(vendorId)), [vendorId]);
|
|
14
|
+
|
|
15
|
+
const tenantId = Digit.ULBService.getCurrentTenantId();
|
|
16
|
+
const { isLoading, data: dashboardData } = Digit.Hooks.ekyc.useEkycSurveyorDashboard(
|
|
17
|
+
{},
|
|
18
|
+
{
|
|
19
|
+
tenantId,
|
|
20
|
+
offset: 0,
|
|
21
|
+
limit: 10,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
enabled: !!tenantId,
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (!vendor) {
|
|
29
|
+
return (
|
|
30
|
+
<div className="ekyc-dashboard-wrapper">
|
|
31
|
+
<div className="dashboard-shell">
|
|
32
|
+
<div className="detail-header">
|
|
33
|
+
<button className="back-button" onClick={() => history.goBack()}>
|
|
34
|
+
<FaArrowLeft /> Back
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="empty-state">
|
|
38
|
+
<h2>Vendor analytics not found</h2>
|
|
39
|
+
<p>The selected vendor does not exist in the current report. Please return to the dashboard.</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const zoneRows = vendor.zones.slice(0, 8);
|
|
47
|
+
|
|
48
|
+
const performanceData = vendor.dailyPerformance.map((row) => ({
|
|
49
|
+
day: row.day,
|
|
50
|
+
completed: row.completed,
|
|
51
|
+
pending: row.pending,
|
|
52
|
+
rejected: row.rejected,
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
console.log(dashboardData);
|
|
56
|
+
if (isLoading) <Loader />;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className="ekyc-dashboard-wrapper">
|
|
60
|
+
<div className="dashboard-shell detail-shell">
|
|
61
|
+
<section className="kpi-grid detail-kpi-grid">
|
|
62
|
+
{[
|
|
63
|
+
{
|
|
64
|
+
label: "Assigned Connections",
|
|
65
|
+
value: vendor.assignedConnections,
|
|
66
|
+
icon: <FaUsers />,
|
|
67
|
+
variant: "primary",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
label: "Agency eKYC",
|
|
71
|
+
value: vendor.completedEkyc,
|
|
72
|
+
icon: <FaCheckCircle />,
|
|
73
|
+
variant: "success",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
label: "Self eKYC",
|
|
77
|
+
value: vendor.selfEkyc,
|
|
78
|
+
icon: <FaUsers />,
|
|
79
|
+
variant: "info",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
label: "Pending",
|
|
83
|
+
value: vendor.pending,
|
|
84
|
+
icon: <FaClock />,
|
|
85
|
+
variant: "warning",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: "Rejected",
|
|
89
|
+
value: vendor.rejected,
|
|
90
|
+
icon: <FaExclamationTriangle />,
|
|
91
|
+
variant: "danger",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: "Success Rate",
|
|
95
|
+
value: `${vendor.successRate}%`,
|
|
96
|
+
icon: <FaCheckCircle />,
|
|
97
|
+
variant: "success",
|
|
98
|
+
},
|
|
99
|
+
].map((item) => (
|
|
100
|
+
<article key={item.label} className={`kpi-card card-${item.variant}`}>
|
|
101
|
+
<div className="kpi-icon">{item.icon}</div>
|
|
102
|
+
<div>
|
|
103
|
+
<p className="kpi-title">{item.label}</p>
|
|
104
|
+
<h2>{item.value?.toLocaleString?.() ?? item.value}</h2>
|
|
105
|
+
</div>
|
|
106
|
+
</article>
|
|
107
|
+
))}
|
|
108
|
+
</section>
|
|
109
|
+
|
|
110
|
+
<section className="analytics-grid detail-analytics-grid">
|
|
111
|
+
<div className="analytics-panel glass-card">
|
|
112
|
+
<div className="section-header">
|
|
113
|
+
<h2>Daily Completion Trend</h2>
|
|
114
|
+
<span className="badge success">Performance</span>
|
|
115
|
+
</div>
|
|
116
|
+
<ExecutiveLineChart data={performanceData} dataKey="completed" name="Completed" />
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className="analytics-panel glass-card">
|
|
120
|
+
<div className="section-header">
|
|
121
|
+
<h2>Pending vs Completed</h2>
|
|
122
|
+
<span className="badge warning">Workload</span>
|
|
123
|
+
</div>
|
|
124
|
+
<ExecutiveBarChart
|
|
125
|
+
data={performanceData}
|
|
126
|
+
categories={[
|
|
127
|
+
{ key: "completed", name: "Completed" },
|
|
128
|
+
{ key: "pending", name: "Pending" },
|
|
129
|
+
]}
|
|
130
|
+
xKey="day"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div className="analytics-panel glass-card">
|
|
135
|
+
<div className="section-header">
|
|
136
|
+
<h2>Self vs Agency Split</h2>
|
|
137
|
+
<span className="badge info">Adoption</span>
|
|
138
|
+
</div>
|
|
139
|
+
<ExecutivePieChart
|
|
140
|
+
data={[
|
|
141
|
+
{ label: "Agency eKYC", value: vendor.completedEkyc },
|
|
142
|
+
{ label: "Citizen Self eKYC", value: vendor.selfEkyc },
|
|
143
|
+
]}
|
|
144
|
+
dataKey="value"
|
|
145
|
+
labelKey="label"
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
</section>
|
|
149
|
+
|
|
150
|
+
<section className="zone-section detail-zone-section">
|
|
151
|
+
<div className="section-header">
|
|
152
|
+
<div>
|
|
153
|
+
<h2>
|
|
154
|
+
<FaMapMarkedAlt /> Zone-wise Jurisdiction Analytics
|
|
155
|
+
</h2>
|
|
156
|
+
<p>Drill into the current operational load and heatmap intensity for this vendor.</p>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
<div className="table-wrapper glass-card">
|
|
160
|
+
<table>
|
|
161
|
+
<thead>
|
|
162
|
+
<tr>
|
|
163
|
+
<th>Zone</th>
|
|
164
|
+
<th>Cluster</th>
|
|
165
|
+
<th>Assigned</th>
|
|
166
|
+
<th>Agency eKYC</th>
|
|
167
|
+
<th>Pending</th>
|
|
168
|
+
<th>Activity Score</th>
|
|
169
|
+
</tr>
|
|
170
|
+
</thead>
|
|
171
|
+
<tbody>
|
|
172
|
+
{zoneRows.map((row, index) => (
|
|
173
|
+
<tr key={`${row.location}-${index}`}>
|
|
174
|
+
<td>{row.location}</td>
|
|
175
|
+
<td>{row.cluster || row.district}</td>
|
|
176
|
+
<td>{row.activeDemand?.toLocaleString() ?? "—"}</td>
|
|
177
|
+
<td>{row.pppZones?.toLocaleString() ?? "—"}</td>
|
|
178
|
+
<td>{row.inactiveDemand?.toLocaleString() ?? "—"}</td>
|
|
179
|
+
<td>
|
|
180
|
+
<span className={`status-badge ${row.intensityScore > 65 ? "danger" : row.intensityScore > 45 ? "warning" : "success"}`}>
|
|
181
|
+
{row.intensityScore}%
|
|
182
|
+
</span>
|
|
183
|
+
</td>
|
|
184
|
+
</tr>
|
|
185
|
+
))}
|
|
186
|
+
</tbody>
|
|
187
|
+
</table>
|
|
188
|
+
</div>
|
|
189
|
+
</section>
|
|
190
|
+
|
|
191
|
+
<section className="bottom-grid detail-bottom-grid">
|
|
192
|
+
<div className="cards-summary glass-card">
|
|
193
|
+
<h2>Operational Efficiency</h2>
|
|
194
|
+
<div className="efficiency-grid">
|
|
195
|
+
<div className="efficiency-box">
|
|
196
|
+
<h3>{vendor.activeSurveyors}</h3>
|
|
197
|
+
<p>Active Surveyors</p>
|
|
198
|
+
</div>
|
|
199
|
+
<div className="efficiency-box">
|
|
200
|
+
<h3>{vendor.supervisors}</h3>
|
|
201
|
+
<p>Supervisors</p>
|
|
202
|
+
</div>
|
|
203
|
+
<div className="efficiency-box">
|
|
204
|
+
<h3>{vendor.progress}%</h3>
|
|
205
|
+
<p>Execution Rate</p>
|
|
206
|
+
</div>
|
|
207
|
+
<div className="efficiency-box">
|
|
208
|
+
<h3>{vendor.dailyPerformance.slice(-1)[0]?.completed?.toLocaleString()}</h3>
|
|
209
|
+
<p>Latest Day Completed</p>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<div className="cards-summary glass-card">
|
|
215
|
+
<h2>Supervisor Pulse</h2>
|
|
216
|
+
<div className="pulse-list">
|
|
217
|
+
{vendor.jurisdictions.map((zone) => (
|
|
218
|
+
<div key={zone} className="pulse-row">
|
|
219
|
+
<span>{zone}</span>
|
|
220
|
+
<span>{Math.max(12, Math.round(Math.random() * 38))} active</span>
|
|
221
|
+
</div>
|
|
222
|
+
))}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</section>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export default VendorDetails;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid, Legend } from "recharts";
|
|
3
|
+
|
|
4
|
+
const ExecutiveBarChart = ({ data = [], categories = [], xKey = "label" }) => {
|
|
5
|
+
return (
|
|
6
|
+
<div className="chart-card" style={{ minHeight: 320 }}>
|
|
7
|
+
<ResponsiveContainer width="100%" height={320}>
|
|
8
|
+
<BarChart data={data} margin={{ top: 12, right: 16, left: 0, bottom: 0 }}>
|
|
9
|
+
<CartesianGrid stroke="rgba(148, 163, 184, 0.12)" strokeDasharray="4 4" />
|
|
10
|
+
<XAxis dataKey={xKey} axisLine={false} tickLine={false} tick={{ fill: "#cbd5e1" }} />
|
|
11
|
+
<YAxis axisLine={false} tickLine={false} tick={{ fill: "#cbd5e1" }} />
|
|
12
|
+
<Tooltip contentStyle={{ background: "#0f172a", border: "none", borderRadius: 14 }} itemStyle={{ color: "#f8fafc" }} />
|
|
13
|
+
<Legend verticalAlign="top" align="right" iconType="circle" wrapperStyle={{ color: "#cbd5e1" }} />
|
|
14
|
+
{categories.map((series, index) => (
|
|
15
|
+
<Bar
|
|
16
|
+
key={series.key}
|
|
17
|
+
dataKey={series.key}
|
|
18
|
+
name={series.name}
|
|
19
|
+
fill={index === 0 ? "#34d399" : "#60a5fa"}
|
|
20
|
+
radius={[12, 12, 0, 0]}
|
|
21
|
+
barSize={24}
|
|
22
|
+
/>
|
|
23
|
+
))}
|
|
24
|
+
</BarChart>
|
|
25
|
+
</ResponsiveContainer>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default ExecutiveBarChart;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// import React from "react";
|
|
2
|
+
// import { ResponsiveContainer, LineChart, Line, XAxis, YAxis, Tooltip, CartesianGrid, Legend } from "recharts";
|
|
3
|
+
|
|
4
|
+
// const ExecutiveLineChart = ({ data = [], dataKey = "completed", name = "Value" }) => {
|
|
5
|
+
// return (
|
|
6
|
+
// <div className="chart-card" style={{ minHeight: 320 }}>
|
|
7
|
+
// <ResponsiveContainer width="100%" height={320}>
|
|
8
|
+
// <LineChart data={data} margin={{ top: 12, right: 16, left: 0, bottom: 0 }}>
|
|
9
|
+
// <CartesianGrid stroke="rgba(148, 163, 184, 0.12)" strokeDasharray="4 4" />
|
|
10
|
+
// <XAxis dataKey="label" axisLine={false} tickLine={false} tick={{ fill: "#cbd5e1" }} />
|
|
11
|
+
// <YAxis axisLine={false} tickLine={false} tick={{ fill: "#cbd5e1" }} />
|
|
12
|
+
// <Tooltip contentStyle={{ background: "#0f172a", border: "none", borderRadius: 14, color: "#f8fafc" }} itemStyle={{ color: "#f8fafc" }} />
|
|
13
|
+
// <Legend verticalAlign="top" align="right" iconType="circle" wrapperStyle={{ color: "#cbd5e1" }} />
|
|
14
|
+
// <Line
|
|
15
|
+
// type="monotone"
|
|
16
|
+
// dataKey={dataKey}
|
|
17
|
+
// name={name}
|
|
18
|
+
// stroke="#38bdf8"
|
|
19
|
+
// strokeWidth={3}
|
|
20
|
+
// dot={{ r: 4 }}
|
|
21
|
+
// activeDot={{ r: 6, strokeWidth: 2, fill: "#60a5fa" }}
|
|
22
|
+
// />
|
|
23
|
+
// </LineChart>
|
|
24
|
+
// </ResponsiveContainer>
|
|
25
|
+
// </div>
|
|
26
|
+
// );
|
|
27
|
+
// };
|
|
28
|
+
|
|
29
|
+
// export default ExecutiveLineChart;
|
|
30
|
+
|
|
31
|
+
import React from "react";
|
|
32
|
+
import { ResponsiveContainer, LineChart, Line, XAxis, YAxis, Tooltip, CartesianGrid, Legend } from "recharts";
|
|
33
|
+
|
|
34
|
+
const ExecutiveLineChart = ({ data = [] }) => {
|
|
35
|
+
const formattedData = data.map((item) => ({
|
|
36
|
+
...item,
|
|
37
|
+
|
|
38
|
+
agencyKyc: item.completed || 0,
|
|
39
|
+
|
|
40
|
+
selfKyc: Math.round((item.completed || 0) * 0.42),
|
|
41
|
+
|
|
42
|
+
totalKyc: (item.completed || 0) + Math.round((item.completed || 0) * 0.42),
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className="chart-card executive-line-chart">
|
|
47
|
+
<ResponsiveContainer width="100%" height={340}>
|
|
48
|
+
<LineChart
|
|
49
|
+
data={formattedData}
|
|
50
|
+
margin={{
|
|
51
|
+
top: 20,
|
|
52
|
+
right: 12,
|
|
53
|
+
left: 0,
|
|
54
|
+
bottom: 0,
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
<CartesianGrid stroke="#e2e8f0" strokeDasharray="3 3" vertical={false} />
|
|
58
|
+
|
|
59
|
+
<XAxis
|
|
60
|
+
dataKey="label"
|
|
61
|
+
axisLine={false}
|
|
62
|
+
tickLine={false}
|
|
63
|
+
tick={{
|
|
64
|
+
fill: "#64748b",
|
|
65
|
+
fontSize: 12,
|
|
66
|
+
fontWeight: 600,
|
|
67
|
+
}}
|
|
68
|
+
/>
|
|
69
|
+
|
|
70
|
+
<YAxis
|
|
71
|
+
axisLine={false}
|
|
72
|
+
tickLine={false}
|
|
73
|
+
tick={{
|
|
74
|
+
fill: "#64748b",
|
|
75
|
+
fontSize: 12,
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
<Tooltip
|
|
80
|
+
cursor={{
|
|
81
|
+
stroke: "#cbd5e1",
|
|
82
|
+
strokeWidth: 1,
|
|
83
|
+
}}
|
|
84
|
+
contentStyle={{
|
|
85
|
+
background: "#ffffff",
|
|
86
|
+
border: "1px solid #e2e8f0",
|
|
87
|
+
borderRadius: 18,
|
|
88
|
+
boxShadow: "0 10px 30px rgba(15,23,42,0.08)",
|
|
89
|
+
}}
|
|
90
|
+
labelStyle={{
|
|
91
|
+
color: "#0f172a",
|
|
92
|
+
fontWeight: 700,
|
|
93
|
+
marginBottom: 10,
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
<Legend
|
|
98
|
+
verticalAlign="top"
|
|
99
|
+
align="right"
|
|
100
|
+
iconType="circle"
|
|
101
|
+
wrapperStyle={{
|
|
102
|
+
paddingBottom: 18,
|
|
103
|
+
color: "#475569",
|
|
104
|
+
fontWeight: 600,
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
107
|
+
|
|
108
|
+
{/* TOTAL */}
|
|
109
|
+
|
|
110
|
+
<Line
|
|
111
|
+
type="monotone"
|
|
112
|
+
dataKey="totalKyc"
|
|
113
|
+
name="Total eKYC"
|
|
114
|
+
stroke="#0f172a"
|
|
115
|
+
strokeWidth={4}
|
|
116
|
+
dot={false}
|
|
117
|
+
activeDot={{
|
|
118
|
+
r: 6,
|
|
119
|
+
}}
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
{/* AGENCY */}
|
|
123
|
+
|
|
124
|
+
<Line
|
|
125
|
+
type="monotone"
|
|
126
|
+
dataKey="agencyKyc"
|
|
127
|
+
name="Agency eKYC"
|
|
128
|
+
stroke="#3b82f6"
|
|
129
|
+
strokeWidth={3}
|
|
130
|
+
dot={{
|
|
131
|
+
r: 3,
|
|
132
|
+
}}
|
|
133
|
+
activeDot={{
|
|
134
|
+
r: 6,
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
137
|
+
|
|
138
|
+
{/* SELF */}
|
|
139
|
+
|
|
140
|
+
<Line
|
|
141
|
+
type="monotone"
|
|
142
|
+
dataKey="selfKyc"
|
|
143
|
+
name="Self eKYC"
|
|
144
|
+
stroke="#10b981"
|
|
145
|
+
strokeWidth={3}
|
|
146
|
+
dot={{
|
|
147
|
+
r: 3,
|
|
148
|
+
}}
|
|
149
|
+
activeDot={{
|
|
150
|
+
r: 6,
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
153
|
+
</LineChart>
|
|
154
|
+
</ResponsiveContainer>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export default ExecutiveLineChart;
|