@djb25/digit-ui-module-ekyc 1.0.11 → 1.0.13
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 +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +2884 -682
- package/dist/index.modern.js.map +1 -1
- package/package.json +1 -1
- package/src/Module.js +28 -7
- package/src/components/AadhaarVerification.js +415 -0
- package/src/components/AddressDetails.js +207 -0
- package/src/components/CeoDashboard.js +201 -0
- package/src/components/DesktopInbox.js +1 -1
- package/src/components/EKYCCard.js +4 -0
- package/src/components/MeterDetails.js +372 -0
- package/src/components/PropertyInfo.js +303 -0
- package/src/components/Review.js +572 -0
- package/src/components/analytics/charts/ClusterHeatmap.js +88 -0
- package/src/components/analytics/charts/TaskStatusChart.js +92 -0
- package/src/components/analytics/components/AnalyticsTable.js +106 -0
- package/src/components/analytics/components/DashboardLayout.js +72 -0
- package/src/components/analytics/components/EmptyState.js +27 -0
- package/src/components/analytics/components/ErrorBoundary.js +27 -0
- package/src/components/analytics/components/FilterBar.js +73 -0
- package/src/components/analytics/components/NotificationPanel.js +77 -0
- package/src/components/analytics/components/SLAWidget.js +56 -0
- package/src/components/analytics/components/SkeletonLoader.js +53 -0
- package/src/components/analytics/components/SummaryCard.js +74 -0
- package/src/components/analytics/components/WorkflowTimeline.js +55 -0
- package/src/components/analytics/styles/Dashboard.css +54 -0
- package/src/components/analytics/utils/exportUtils.js +64 -0
- package/src/components/analytics/utils/filterSerializer.js +50 -0
- package/src/config/config.js +1 -1
- package/src/pages/citizen/index.js +74 -18
- package/src/pages/employee/ConsumerDetails.js +10 -281
- package/src/pages/employee/Inbox.js +6 -4
- package/src/pages/employee/index.js +55 -8
- package/src/pages/employee/AadhaarVerification.js +0 -512
- package/src/pages/employee/AddressDetails.js +0 -548
- package/src/pages/employee/MeterDetails.js +0 -496
- package/src/pages/employee/PropertyInfo.js +0 -489
- package/src/pages/employee/Review.js +0 -314
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Timeline component for workflow stage monitoring.
|
|
6
|
+
*/
|
|
7
|
+
const WorkflowTimeline = ({ stages }) => {
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
|
|
10
|
+
if (!stages || stages.length === 0) return null;
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="workflow-timeline-card glass-card" style={{ padding: "28px" }}>
|
|
14
|
+
<h3 style={{ fontSize: "18px", fontWeight: "800", color: "#111827", marginBottom: "28px", letterSpacing: "-0.025em" }}>{t("EKYC_WORKFLOW_BOTTLENECK_ANALYSIS")}</h3>
|
|
15
|
+
|
|
16
|
+
<div className="timeline-container" style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
17
|
+
{stages.map((stage, idx) => (
|
|
18
|
+
<div key={idx} style={{ display: "flex", gap: "16px", alignItems: "flex-start" }}>
|
|
19
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
|
|
20
|
+
<div style={{
|
|
21
|
+
width: "12px",
|
|
22
|
+
height: "12px",
|
|
23
|
+
borderRadius: "50%",
|
|
24
|
+
background: stage.avgDurationHours > 20 ? "#EF4444" : "#10B981",
|
|
25
|
+
marginTop: "4px"
|
|
26
|
+
}} />
|
|
27
|
+
{idx !== stages.length - 1 && <div style={{ width: "2px", height: "40px", background: "#E5E7EB" }} />}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div style={{ flex: 1 }}>
|
|
31
|
+
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: "4px" }}>
|
|
32
|
+
<span style={{ fontSize: "14px", fontWeight: "600", color: "#374151" }}>{t(stage.stageName)}</span>
|
|
33
|
+
<span style={{ fontSize: "12px", fontWeight: "700", color: stage.avgDurationHours > 20 ? "#EF4444" : "#10B981" }}>
|
|
34
|
+
{stage.avgDurationHours}h {t("EKYC_AVG")}
|
|
35
|
+
</span>
|
|
36
|
+
</div>
|
|
37
|
+
<div style={{ width: "100%", height: "6px", background: "#F3F4F6", borderRadius: "3px", overflow: "hidden" }}>
|
|
38
|
+
<div style={{
|
|
39
|
+
width: `${Math.min(100, (stage.count / 1000) * 100)}%`,
|
|
40
|
+
height: "100%",
|
|
41
|
+
background: stage.avgDurationHours > 20 ? "#EF444480" : "#10B98180"
|
|
42
|
+
}} />
|
|
43
|
+
</div>
|
|
44
|
+
<div style={{ fontSize: "12px", color: "#6B7280", marginTop: "4px" }}>
|
|
45
|
+
{stage.count} {t("EKYC_APPLICATIONS_IN_STAGE")}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default WorkflowTimeline;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* Premium Dashboard Design System */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--glass-bg: rgba(255, 255, 255, 0.7);
|
|
5
|
+
--glass-border: rgba(255, 255, 255, 0.3);
|
|
6
|
+
--glass-blur: blur(12px);
|
|
7
|
+
|
|
8
|
+
--primary-gradient: linear-gradient(135deg, #6366f1 0%, #a855f7 100%);
|
|
9
|
+
--success-gradient: linear-gradient(135deg, #10b981 0%, #34d399 100%);
|
|
10
|
+
--warning-gradient: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
|
|
11
|
+
--danger-gradient: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
|
|
12
|
+
|
|
13
|
+
--card-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.02);
|
|
14
|
+
--card-hover-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.glass-card {
|
|
18
|
+
background: var(--glass-bg);
|
|
19
|
+
backdrop-filter: var(--glass-blur);
|
|
20
|
+
-webkit-backdrop-filter: var(--glass-blur);
|
|
21
|
+
border: 1px solid var(--glass-border);
|
|
22
|
+
border-radius: 20px;
|
|
23
|
+
box-shadow: var(--card-shadow);
|
|
24
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.glass-card:hover {
|
|
28
|
+
transform: translateY(-5px);
|
|
29
|
+
box-shadow: var(--card-hover-shadow);
|
|
30
|
+
border-color: rgba(255, 255, 255, 0.5);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.gradient-text {
|
|
34
|
+
background: var(--primary-gradient);
|
|
35
|
+
-webkit-background-clip: text;
|
|
36
|
+
-webkit-text-fill-color: transparent;
|
|
37
|
+
font-weight: 800;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@keyframes fadeInScale {
|
|
41
|
+
from {
|
|
42
|
+
opacity: 0;
|
|
43
|
+
transform: scale(0.95) translateY(10px);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
to {
|
|
47
|
+
opacity: 1;
|
|
48
|
+
transform: scale(1) translateY(0);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.animate-fade-in {
|
|
53
|
+
animation: fadeInScale 0.6s ease-out forwards;
|
|
54
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file exportUtils.js
|
|
3
|
+
* @description Provides utilities to export dashboard table analytics to CSV/Excel formats and invoke native printing layouts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const ExportUtils = {
|
|
7
|
+
/**
|
|
8
|
+
* Generates and triggers download of a client-side CSV file from array records
|
|
9
|
+
*/
|
|
10
|
+
exportToCsv: (data, filename = "dashboard_export.csv", columns = []) => {
|
|
11
|
+
if (!data || !data.length) return;
|
|
12
|
+
|
|
13
|
+
// Determine column keys if not provided explicitly
|
|
14
|
+
const keys = columns.length ? columns.map(c => c.id) : Object.keys(data[0]);
|
|
15
|
+
const headers = columns.length ? columns.map(c => c.label) : keys;
|
|
16
|
+
|
|
17
|
+
const csvRows = [];
|
|
18
|
+
// Header row
|
|
19
|
+
csvRows.push(headers.map(h => `"${String(h).replace(/"/g, '""')}"`).join(","));
|
|
20
|
+
|
|
21
|
+
// Data rows
|
|
22
|
+
data.forEach(row => {
|
|
23
|
+
const values = keys.map(k => {
|
|
24
|
+
const val = row[k] !== undefined && row[k] !== null ? row[k] : "";
|
|
25
|
+
return `"${String(val).replace(/"/g, '""')}"`;
|
|
26
|
+
});
|
|
27
|
+
csvRows.push(values.join(","));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const csvString = csvRows.join("\n");
|
|
31
|
+
const blob = new Blob([csvString], { type: "text/csv;charset=utf-8;" });
|
|
32
|
+
|
|
33
|
+
// Trigger download anchor
|
|
34
|
+
const link = document.createElement("a");
|
|
35
|
+
if (link.download !== undefined) {
|
|
36
|
+
const url = URL.createObjectURL(blob);
|
|
37
|
+
link.setAttribute("href", url);
|
|
38
|
+
link.setAttribute("download", filename);
|
|
39
|
+
link.style.visibility = "hidden";
|
|
40
|
+
document.body.appendChild(link);
|
|
41
|
+
link.click();
|
|
42
|
+
document.body.removeChild(link);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Triggers an Excel formatted download simulation
|
|
48
|
+
*/
|
|
49
|
+
exportToExcel: (data, filename = "dashboard_report.xlsx", columns = []) => {
|
|
50
|
+
// Uses standard CSV export format mapped to an excel extension compatible layer
|
|
51
|
+
ExportUtils.exportToCsv(data, filename.replace(".xlsx", ".csv"), columns);
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Optimizes current interface display rules and opens native browser print dialogue
|
|
56
|
+
*/
|
|
57
|
+
printDashboard: () => {
|
|
58
|
+
if (typeof window !== "undefined") {
|
|
59
|
+
window.print();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default ExportUtils;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file filterSerializer.js
|
|
3
|
+
* @description Translates dashboard state criteria into encoded URL search parameters and deserializes query params back to state objects.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const FilterSerializer = {
|
|
7
|
+
/**
|
|
8
|
+
* Serializes a plain filter state object into a URL query parameter string
|
|
9
|
+
*/
|
|
10
|
+
serialize: (filters) => {
|
|
11
|
+
if (!filters || typeof filters !== "object") return "";
|
|
12
|
+
const params = new URLSearchParams();
|
|
13
|
+
|
|
14
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
15
|
+
if (value !== undefined && value !== null && value !== "" && value !== "ALL") {
|
|
16
|
+
params.append(key, String(value));
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const queryString = params.toString();
|
|
21
|
+
return queryString ? `?${queryString}` : "";
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Deserializes current window search string parameters back into a flat state object
|
|
26
|
+
*/
|
|
27
|
+
deserialize: (searchString, defaultFilters = {}) => {
|
|
28
|
+
if (!searchString) return { ...defaultFilters };
|
|
29
|
+
const params = new URLSearchParams(searchString);
|
|
30
|
+
const filters = { ...defaultFilters };
|
|
31
|
+
|
|
32
|
+
params.forEach((value, key) => {
|
|
33
|
+
filters[key] = value;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return filters;
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Updates browser history state smoothly without triggering full reload navigations
|
|
41
|
+
*/
|
|
42
|
+
persistToUrl: (filters) => {
|
|
43
|
+
if (typeof window === "undefined") return;
|
|
44
|
+
const serialized = FilterSerializer.serialize(filters);
|
|
45
|
+
const newUrl = `${window.location.pathname}${serialized}`;
|
|
46
|
+
window.history.replaceState({ path: newUrl }, "", newUrl);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default FilterSerializer;
|
package/src/config/config.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { AppContainer, PrivateRoute, ModuleHeader, ArrowLeft, HomeIcon } from "@djb25/digit-ui-react-components";
|
|
2
1
|
import React from "react";
|
|
2
|
+
import { PrivateRoute, ModuleHeader, ArrowLeft, HomeIcon, LayoutWrapper } from "@djb25/digit-ui-react-components";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { Switch, useLocation, useRouteMatch } from "react-router-dom";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import MeterDetails from "../employee/MeterDetails";
|
|
10
|
-
import Review from "../employee/Review";
|
|
5
|
+
import AadhaarVerification from "../../components/AadhaarVerification";
|
|
6
|
+
import PropertyInfo from "../../components/PropertyInfo";
|
|
7
|
+
import MeterDetails from "../../components/MeterDetails";
|
|
8
|
+
import Review from "../../components/Review";
|
|
11
9
|
import Home from "./Home";
|
|
12
10
|
import Dashboard from "../../components/Dashboard";
|
|
13
11
|
import Inbox from "./Inbox";
|
|
12
|
+
import AddressDetails from "../../components/AddressDetails";
|
|
14
13
|
|
|
15
14
|
const CitizenApp = () => {
|
|
16
15
|
const { t } = useTranslation();
|
|
@@ -33,8 +32,8 @@ const CitizenApp = () => {
|
|
|
33
32
|
const breadcrumbs = [{ icon: HomeIcon, path: "/digit-ui/citizen" }, { label: t(getBreadcrumbLabel()) }];
|
|
34
33
|
|
|
35
34
|
return (
|
|
36
|
-
<
|
|
37
|
-
<div className="ground-container
|
|
35
|
+
<React.Fragment>
|
|
36
|
+
<div className="ground-container form-container">
|
|
38
37
|
<ModuleHeader
|
|
39
38
|
leftContent={
|
|
40
39
|
<React.Fragment>
|
|
@@ -47,22 +46,79 @@ const CitizenApp = () => {
|
|
|
47
46
|
/>
|
|
48
47
|
|
|
49
48
|
<Switch>
|
|
50
|
-
<PrivateRoute
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
<PrivateRoute
|
|
50
|
+
exact
|
|
51
|
+
path={`${path}`}
|
|
52
|
+
component={() => (
|
|
53
|
+
<LayoutWrapper layoutClass="normal">
|
|
54
|
+
<Home />
|
|
55
|
+
</LayoutWrapper>
|
|
56
|
+
)}
|
|
57
|
+
/>
|
|
58
|
+
<PrivateRoute
|
|
59
|
+
path={`${path}/dashboard`}
|
|
60
|
+
component={() => (
|
|
61
|
+
<LayoutWrapper layoutClass="normal">
|
|
62
|
+
<Dashboard />
|
|
63
|
+
</LayoutWrapper>
|
|
64
|
+
)}
|
|
65
|
+
/>
|
|
66
|
+
<PrivateRoute
|
|
67
|
+
path={`${path}/inbox`}
|
|
68
|
+
component={() => (
|
|
69
|
+
<LayoutWrapper layoutClass="normal">
|
|
70
|
+
<Inbox />
|
|
71
|
+
</LayoutWrapper>
|
|
72
|
+
)}
|
|
73
|
+
/>
|
|
53
74
|
|
|
54
|
-
<PrivateRoute
|
|
75
|
+
<PrivateRoute
|
|
76
|
+
path={`${path}/aadhaar-verification`}
|
|
77
|
+
component={() => (
|
|
78
|
+
<LayoutWrapper layoutClass="normal">
|
|
79
|
+
<AadhaarVerification />
|
|
80
|
+
</LayoutWrapper>
|
|
81
|
+
)}
|
|
82
|
+
/>
|
|
55
83
|
|
|
56
|
-
<PrivateRoute
|
|
84
|
+
<PrivateRoute
|
|
85
|
+
path={`${path}/address-details`}
|
|
86
|
+
component={() => (
|
|
87
|
+
<LayoutWrapper layoutClass="normal">
|
|
88
|
+
<AddressDetails />
|
|
89
|
+
</LayoutWrapper>
|
|
90
|
+
)}
|
|
91
|
+
/>
|
|
57
92
|
|
|
58
|
-
<PrivateRoute
|
|
93
|
+
<PrivateRoute
|
|
94
|
+
path={`${path}/property-info`}
|
|
95
|
+
component={() => (
|
|
96
|
+
<LayoutWrapper layoutClass="normal">
|
|
97
|
+
<PropertyInfo />
|
|
98
|
+
</LayoutWrapper>
|
|
99
|
+
)}
|
|
100
|
+
/>
|
|
59
101
|
|
|
60
|
-
<PrivateRoute
|
|
102
|
+
<PrivateRoute
|
|
103
|
+
path={`${path}/meter-details`}
|
|
104
|
+
component={() => (
|
|
105
|
+
<LayoutWrapper layoutClass="normal">
|
|
106
|
+
<MeterDetails />
|
|
107
|
+
</LayoutWrapper>
|
|
108
|
+
)}
|
|
109
|
+
/>
|
|
61
110
|
|
|
62
|
-
<PrivateRoute
|
|
111
|
+
<PrivateRoute
|
|
112
|
+
path={`${path}/review`}
|
|
113
|
+
component={() => (
|
|
114
|
+
<LayoutWrapper layoutClass="normal">
|
|
115
|
+
<Review />
|
|
116
|
+
</LayoutWrapper>
|
|
117
|
+
)}
|
|
118
|
+
/>
|
|
63
119
|
</Switch>
|
|
64
120
|
</div>
|
|
65
|
-
</
|
|
121
|
+
</React.Fragment>
|
|
66
122
|
);
|
|
67
123
|
};
|
|
68
124
|
|