@djb25/digit-ui-module-ekyc 1.0.30 → 1.0.32

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.
Files changed (63) hide show
  1. package/dist/index.js +8911 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.modern.js +3295 -2039
  4. package/dist/index.modern.js.map +1 -1
  5. package/package.json +4 -1
  6. package/dist/index.css +0 -54
  7. package/src/Module.js +0 -78
  8. package/src/components/AadhaarVerification.js +0 -315
  9. package/src/components/AssignEkyc.js +0 -236
  10. package/src/components/AssignEkycModal.js +0 -446
  11. package/src/components/CeoDashboard.js +0 -264
  12. package/src/components/CeoDashboard.jsx +0 -334
  13. package/src/components/ConnectionDetailsView.js +0 -241
  14. package/src/components/Dashboard.js +0 -43
  15. package/src/components/DesktopInbox.js +0 -176
  16. package/src/components/EKYCCard.js +0 -53
  17. package/src/components/Filter.js +0 -55
  18. package/src/components/MeterDetails.js +0 -336
  19. package/src/components/MobileInbox.js +0 -73
  20. package/src/components/PropertyInfo.js +0 -264
  21. package/src/components/Review.js +0 -696
  22. package/src/components/SearchConsumer.js +0 -142
  23. package/src/components/SearchFormFieldsComponent.js +0 -67
  24. package/src/components/SelectEkycZones.js +0 -71
  25. package/src/components/StatusCards.js +0 -148
  26. package/src/components/SurveyorDetailsCard.js +0 -512
  27. package/src/components/VendorDetails.jsx +0 -230
  28. package/src/components/analytics/charts/ClusterHeatmap.js +0 -88
  29. package/src/components/analytics/charts/ExecutiveBarChart.jsx +0 -30
  30. package/src/components/analytics/charts/ExecutiveLineChart.jsx +0 -159
  31. package/src/components/analytics/charts/ExecutivePieChart.jsx +0 -24
  32. package/src/components/analytics/charts/TaskStatusChart.js +0 -90
  33. package/src/components/analytics/components/AnalyticsTable.js +0 -106
  34. package/src/components/analytics/components/DashboardLayout.js +0 -52
  35. package/src/components/analytics/components/EmptyState.js +0 -27
  36. package/src/components/analytics/components/ErrorBoundary.js +0 -27
  37. package/src/components/analytics/components/FilterBar.js +0 -73
  38. package/src/components/analytics/components/NotificationPanel.js +0 -77
  39. package/src/components/analytics/components/SLAWidget.js +0 -56
  40. package/src/components/analytics/components/SkeletonLoader.js +0 -53
  41. package/src/components/analytics/components/SummaryCard.js +0 -74
  42. package/src/components/analytics/components/WorkflowTimeline.js +0 -55
  43. package/src/components/analytics/utils/exportUtils.js +0 -64
  44. package/src/components/analytics/utils/filterSerializer.js +0 -50
  45. package/src/components/mockData.js +0 -772
  46. package/src/config/AadhaarVerificationConfig.js +0 -367
  47. package/src/config/MeterDetailsConfig.js +0 -297
  48. package/src/config/PropertyInfoConfig.js +0 -145
  49. package/src/config/config.js +0 -70
  50. package/src/hook/SupervisorInboxTableConfig.js +0 -80
  51. package/src/hook/useInboxTableConfig.js +0 -131
  52. package/src/pages/citizen/Home.js +0 -54
  53. package/src/pages/citizen/Inbox.js +0 -265
  54. package/src/pages/citizen/index.js +0 -199
  55. package/src/pages/employee/ConsumerDetails.js +0 -299
  56. package/src/pages/employee/Create.js +0 -94
  57. package/src/pages/employee/EKYCForm.js +0 -118
  58. package/src/pages/employee/Inbox.js +0 -266
  59. package/src/pages/employee/Mapping.js +0 -358
  60. package/src/pages/employee/Update.js +0 -9
  61. package/src/pages/employee/index.js +0 -184
  62. package/src/utils/index.js +0 -46
  63. package/src/utils/reportDownloader.js +0 -90
@@ -1,230 +0,0 @@
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
- if (isLoading) <Loader />;
56
-
57
- return (
58
- <div className="ekyc-dashboard-wrapper">
59
- <div className="dashboard-shell detail-shell">
60
- <section className="kpi-grid detail-kpi-grid">
61
- {[
62
- {
63
- label: "Assigned Connections",
64
- value: vendor.assignedConnections,
65
- icon: <FaUsers />,
66
- variant: "primary",
67
- },
68
- {
69
- label: "Agency eKYC",
70
- value: vendor.completedEkyc,
71
- icon: <FaCheckCircle />,
72
- variant: "success",
73
- },
74
- {
75
- label: "Self eKYC",
76
- value: vendor.selfEkyc,
77
- icon: <FaUsers />,
78
- variant: "info",
79
- },
80
- {
81
- label: "Pending",
82
- value: vendor.pending,
83
- icon: <FaClock />,
84
- variant: "warning",
85
- },
86
- {
87
- label: "Rejected",
88
- value: vendor.rejected,
89
- icon: <FaExclamationTriangle />,
90
- variant: "danger",
91
- },
92
- {
93
- label: "Success Rate",
94
- value: `${vendor.successRate}%`,
95
- icon: <FaCheckCircle />,
96
- variant: "success",
97
- },
98
- ].map((item) => (
99
- <article key={item.label} className={`kpi-card card-${item.variant}`}>
100
- <div className="kpi-icon">{item.icon}</div>
101
- <div>
102
- <p className="kpi-title">{item.label}</p>
103
- <h2>{item.value?.toLocaleString?.() ?? item.value}</h2>
104
- </div>
105
- </article>
106
- ))}
107
- </section>
108
-
109
- <section className="analytics-grid detail-analytics-grid">
110
- <div className="analytics-panel glass-card">
111
- <div className="section-header">
112
- <h2>Daily Completion Trend</h2>
113
- <span className="badge success">Performance</span>
114
- </div>
115
- <ExecutiveLineChart data={performanceData} dataKey="completed" name="Completed" />
116
- </div>
117
-
118
- <div className="analytics-panel glass-card">
119
- <div className="section-header">
120
- <h2>Pending vs Completed</h2>
121
- <span className="badge warning">Workload</span>
122
- </div>
123
- <ExecutiveBarChart
124
- data={performanceData}
125
- categories={[
126
- { key: "completed", name: "Completed" },
127
- { key: "pending", name: "Pending" },
128
- ]}
129
- xKey="day"
130
- />
131
- </div>
132
-
133
- <div className="analytics-panel glass-card">
134
- <div className="section-header">
135
- <h2>Self vs Agency Split</h2>
136
- <span className="badge info">Adoption</span>
137
- </div>
138
- <ExecutivePieChart
139
- data={[
140
- { label: "Agency eKYC", value: vendor.completedEkyc },
141
- { label: "Citizen Self eKYC", value: vendor.selfEkyc },
142
- ]}
143
- dataKey="value"
144
- labelKey="label"
145
- />
146
- </div>
147
- </section>
148
-
149
- <section className="zone-section detail-zone-section">
150
- <div className="section-header">
151
- <div>
152
- <h2>
153
- <FaMapMarkedAlt /> Zone-wise Jurisdiction Analytics
154
- </h2>
155
- <p>Drill into the current operational load and heatmap intensity for this vendor.</p>
156
- </div>
157
- </div>
158
- <div className="table-wrapper glass-card">
159
- <table>
160
- <thead>
161
- <tr>
162
- <th>Zone</th>
163
- <th>Cluster</th>
164
- <th>Assigned</th>
165
- <th>Agency eKYC</th>
166
- <th>Pending</th>
167
- <th>Activity Score</th>
168
- </tr>
169
- </thead>
170
- <tbody>
171
- {zoneRows.map((row, index) => (
172
- <tr key={`${row.location}-${index}`}>
173
- <td>{row.location}</td>
174
- <td>{row.cluster || row.district}</td>
175
- <td>{row.activeDemand?.toLocaleString() ?? "—"}</td>
176
- <td>{row.pppZones?.toLocaleString() ?? "—"}</td>
177
- <td>{row.inactiveDemand?.toLocaleString() ?? "—"}</td>
178
- <td>
179
- <span className={`status-badge ${row.intensityScore > 65 ? "danger" : row.intensityScore > 45 ? "warning" : "success"}`}>
180
- {row.intensityScore}%
181
- </span>
182
- </td>
183
- </tr>
184
- ))}
185
- </tbody>
186
- </table>
187
- </div>
188
- </section>
189
-
190
- <section className="bottom-grid detail-bottom-grid">
191
- <div className="cards-summary glass-card">
192
- <h2>Operational Efficiency</h2>
193
- <div className="efficiency-grid">
194
- <div className="efficiency-box">
195
- <h3>{vendor.activeSurveyors}</h3>
196
- <p>Active Surveyors</p>
197
- </div>
198
- <div className="efficiency-box">
199
- <h3>{vendor.supervisors}</h3>
200
- <p>Supervisors</p>
201
- </div>
202
- <div className="efficiency-box">
203
- <h3>{vendor.progress}%</h3>
204
- <p>Execution Rate</p>
205
- </div>
206
- <div className="efficiency-box">
207
- <h3>{vendor.dailyPerformance.slice(-1)[0]?.completed?.toLocaleString()}</h3>
208
- <p>Latest Day Completed</p>
209
- </div>
210
- </div>
211
- </div>
212
-
213
- <div className="cards-summary glass-card">
214
- <h2>Supervisor Pulse</h2>
215
- <div className="pulse-list">
216
- {vendor.jurisdictions.map((zone) => (
217
- <div key={zone} className="pulse-row">
218
- <span>{zone}</span>
219
- <span>{Math.max(12, Math.round(Math.random() * 38))} active</span>
220
- </div>
221
- ))}
222
- </div>
223
- </div>
224
- </section>
225
- </div>
226
- </div>
227
- );
228
- };
229
-
230
- export default VendorDetails;
@@ -1,88 +0,0 @@
1
- import React from "react";
2
- import { useTranslation } from "react-i18next";
3
-
4
- /**
5
- * Custom SVG-based Heatmap component for Cluster Workload monitoring.
6
- */
7
- const ClusterHeatmap = ({ data, title, onDrillDown }) => {
8
- const { t } = useTranslation();
9
-
10
- if (!data || data.length === 0) return null;
11
-
12
- const getIntensityColor = (score) => {
13
- if (score > 80) return "#EF4444"; // High - Red
14
- if (score > 50) return "#F59E0B"; // Medium - Amber
15
- return "#10B981"; // Low - Emerald
16
- };
17
-
18
- return (
19
- <div className="cluster-heatmap-container glass-card" style={{ padding: "28px" }}>
20
- {title && <h3 className="chart-title" style={{ marginBottom: "28px", fontSize: "18px", fontWeight: "800", color: "#111827", letterSpacing: "-0.025em" }}>{t(title)}</h3>}
21
- <div className="heatmap-grid" style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(240px, 1fr))", gap: "20px" }}>
22
- {data.map((cluster) => (
23
- <div
24
- key={cluster.clusterId}
25
- className="heatmap-card glass-card"
26
- onClick={() => onDrillDown?.(cluster)}
27
- style={{
28
- padding: "20px",
29
- background: "#FFFFFF",
30
- border: `1px solid ${getIntensityColor(cluster.intensityScore)}40`,
31
- cursor: "pointer",
32
- position: "relative",
33
- overflow: "hidden"
34
- }}
35
- >
36
- <div className="intensity-bar" style={{
37
- position: "absolute",
38
- top: 0,
39
- left: 0,
40
- height: "4px",
41
- width: "100%",
42
- background: getIntensityColor(cluster.intensityScore)
43
- }} />
44
-
45
- <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: "12px" }}>
46
- <span style={{ fontWeight: "600", fontSize: "14px", color: "#374151" }}>{cluster.clusterName}</span>
47
- <span style={{
48
- fontSize: "12px",
49
- fontWeight: "700",
50
- padding: "2px 8px",
51
- borderRadius: "12px",
52
- background: `${getIntensityColor(cluster.intensityScore)}20`,
53
- color: getIntensityColor(cluster.intensityScore)
54
- }}>
55
- {cluster.intensityScore}%
56
- </span>
57
- </div>
58
-
59
- <div className="cluster-stats" style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
60
- <div style={{ display: "flex", justifyContent: "space-between", fontSize: "12px" }}>
61
- <span style={{ color: "#6B7280" }}>{t("EKYC_PENDING_WORKLOAD")}</span>
62
- <span style={{ fontWeight: "600", color: "#1F2937" }}>{cluster.pendingWorkload}</span>
63
- </div>
64
- <div style={{ display: "flex", justifyContent: "space-between", fontSize: "12px" }}>
65
- <span style={{ color: "#6B7280" }}>{t("EKYC_ACTIVE_AGENCIES")}</span>
66
- <span style={{ fontWeight: "600", color: "#1F2937" }}>{cluster.activeAgencies}</span>
67
- </div>
68
- </div>
69
-
70
- <div className="mini-spark" style={{ marginTop: "12px", display: "flex", gap: "2px", alignItems: "flex-end", height: "20px" }}>
71
- {cluster.wards.map((ward, idx) => (
72
- <div key={idx} style={{
73
- flex: 1,
74
- height: `${Math.min(100, (ward.pendingCount / cluster.pendingWorkload) * 100)}%`,
75
- background: getIntensityColor(cluster.intensityScore),
76
- opacity: 0.6,
77
- borderRadius: "1px"
78
- }} />
79
- ))}
80
- </div>
81
- </div>
82
- ))}
83
- </div>
84
- </div>
85
- );
86
- };
87
-
88
- export default ClusterHeatmap;
@@ -1,30 +0,0 @@
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;
@@ -1,159 +0,0 @@
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;
@@ -1,24 +0,0 @@
1
- import React from "react";
2
- import { ResponsiveContainer, PieChart, Pie, Cell, Tooltip, Legend } from "recharts";
3
-
4
- const colors = ["#22c55e", "#38bdf8", "#f97316", "#facc15", "#f43f5e"];
5
-
6
- const ExecutivePieChart = ({ data = [], dataKey = "value", labelKey = "label" }) => {
7
- return (
8
- <div className="chart-card" style={{ minHeight: 320 }}>
9
- <ResponsiveContainer width="100%" height={320}>
10
- <PieChart>
11
- <Pie data={data} dataKey={dataKey} nameKey={labelKey} cx="50%" cy="50%" outerRadius={110} innerRadius={60} paddingAngle={3} label={{ fill: "#f8fafc", fontSize: 12 }}>
12
- {data.map((entry, index) => (
13
- <Cell key={`cell-${index}`} fill={colors[index % colors.length]} />
14
- ))}
15
- </Pie>
16
- <Tooltip contentStyle={{ background: "#0f172a", border: "none", borderRadius: 14, color: "#f8fafc" }} />
17
- <Legend verticalAlign="bottom" align="center" iconType="circle" wrapperStyle={{ color: "#cbd5e1", marginTop: 10 }} />
18
- </PieChart>
19
- </ResponsiveContainer>
20
- </div>
21
- );
22
- };
23
-
24
- export default ExecutivePieChart;
@@ -1,90 +0,0 @@
1
- import React, { useEffect, useRef } from "react";
2
- import { useTranslation } from "react-i18next";
3
- import Chart from "chart.js/auto";
4
-
5
- const TaskStatusChart = ({ data, title }) => {
6
- const { t } = useTranslation();
7
- const chartRef = useRef(null);
8
- const chartInstance = useRef(null);
9
-
10
- useEffect(() => {
11
- if (chartRef.current && data) {
12
- if (chartInstance.current) chartInstance.current.destroy();
13
- const ctx = chartRef.current.getContext("2d");
14
-
15
- // Create vibrant gradients
16
- const gradientBlue = ctx.createLinearGradient(0, 0, 0, 400);
17
- gradientBlue.addColorStop(0, '#3B82F6');
18
- gradientBlue.addColorStop(1, '#2563EB');
19
-
20
- const gradientAmber = ctx.createLinearGradient(0, 0, 0, 400);
21
- gradientAmber.addColorStop(0, '#F59E0B');
22
- gradientAmber.addColorStop(1, '#D97706');
23
-
24
- const gradientIndigo = ctx.createLinearGradient(0, 0, 0, 400);
25
- gradientIndigo.addColorStop(0, '#6366F1');
26
- gradientIndigo.addColorStop(1, '#4F46E5');
27
-
28
- const gradientEmerald = ctx.createLinearGradient(0, 0, 0, 400);
29
- gradientEmerald.addColorStop(0, '#10B981');
30
- gradientEmerald.addColorStop(1, '#059669');
31
-
32
- const colors = [gradientBlue, gradientAmber, gradientIndigo, gradientEmerald, '#EC4899', '#EF4444'];
33
-
34
- const labels = data.map(item => t(item.stageName));
35
- const values = data.map(item => item.count);
36
-
37
- const ChartConstructor = Chart.Chart || Chart;
38
- chartInstance.current = new ChartConstructor(ctx, {
39
- type: "doughnut",
40
- data: {
41
- labels,
42
- datasets: [
43
- {
44
- data: values,
45
- backgroundColor: colors.slice(0, labels.length),
46
- borderWidth: 2,
47
- borderColor: "#ffffff",
48
- hoverOffset: 10
49
- }
50
- ]
51
- },
52
- options: {
53
- responsive: true,
54
- maintainAspectRatio: false,
55
- cutout: '70%',
56
- plugins: {
57
- legend: {
58
- position: 'right',
59
- labels: {
60
- usePointStyle: true,
61
- padding: 15,
62
- font: { size: 12 }
63
- }
64
- },
65
- tooltip: {
66
- backgroundColor: '#1F2937',
67
- padding: 10,
68
- bodyFont: { size: 13 }
69
- }
70
- }
71
- }
72
- });
73
- }
74
-
75
- return () => {
76
- if (chartInstance.current) chartInstance.current.destroy();
77
- };
78
- }, [data, t]);
79
-
80
- return (
81
- <div className="task-status-chart-container glass-card" style={{ height: "320px", width: "100%", padding: "24px" }}>
82
- {title && <h3 className="chart-title" style={{ marginBottom: "24px", fontSize: "18px", fontWeight: "700", color: "#111827" }}>{t(title)}</h3>}
83
- <div style={{ height: "220px" }}>
84
- <canvas ref={chartRef} />
85
- </div>
86
- </div>
87
- );
88
- };
89
-
90
- export default TaskStatusChart;