@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.
Files changed (40) hide show
  1. package/dist/index.css +1 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.modern.js +2884 -682
  5. package/dist/index.modern.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/Module.js +28 -7
  8. package/src/components/AadhaarVerification.js +415 -0
  9. package/src/components/AddressDetails.js +207 -0
  10. package/src/components/CeoDashboard.js +201 -0
  11. package/src/components/DesktopInbox.js +1 -1
  12. package/src/components/EKYCCard.js +4 -0
  13. package/src/components/MeterDetails.js +372 -0
  14. package/src/components/PropertyInfo.js +303 -0
  15. package/src/components/Review.js +572 -0
  16. package/src/components/analytics/charts/ClusterHeatmap.js +88 -0
  17. package/src/components/analytics/charts/TaskStatusChart.js +92 -0
  18. package/src/components/analytics/components/AnalyticsTable.js +106 -0
  19. package/src/components/analytics/components/DashboardLayout.js +72 -0
  20. package/src/components/analytics/components/EmptyState.js +27 -0
  21. package/src/components/analytics/components/ErrorBoundary.js +27 -0
  22. package/src/components/analytics/components/FilterBar.js +73 -0
  23. package/src/components/analytics/components/NotificationPanel.js +77 -0
  24. package/src/components/analytics/components/SLAWidget.js +56 -0
  25. package/src/components/analytics/components/SkeletonLoader.js +53 -0
  26. package/src/components/analytics/components/SummaryCard.js +74 -0
  27. package/src/components/analytics/components/WorkflowTimeline.js +55 -0
  28. package/src/components/analytics/styles/Dashboard.css +54 -0
  29. package/src/components/analytics/utils/exportUtils.js +64 -0
  30. package/src/components/analytics/utils/filterSerializer.js +50 -0
  31. package/src/config/config.js +1 -1
  32. package/src/pages/citizen/index.js +74 -18
  33. package/src/pages/employee/ConsumerDetails.js +10 -281
  34. package/src/pages/employee/Inbox.js +6 -4
  35. package/src/pages/employee/index.js +55 -8
  36. package/src/pages/employee/AadhaarVerification.js +0 -512
  37. package/src/pages/employee/AddressDetails.js +0 -548
  38. package/src/pages/employee/MeterDetails.js +0 -496
  39. package/src/pages/employee/PropertyInfo.js +0 -489
  40. 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;
@@ -1,4 +1,4 @@
1
- import AadhaarVerification from "../pages/employee/AadhaarVerification";
1
+ import AadhaarVerification from "../components/AadhaarVerification";
2
2
 
3
3
  export const ekycConfig = [
4
4
  {
@@ -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 Create from "../employee/Create";
6
- import AadhaarVerification from "../employee/AadhaarVerification";
7
- import AddressDetails from "../employee/AddressDetails";
8
- import PropertyInfo from "../employee/PropertyInfo";
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
- <AppContainer>
37
- <div className="ground-container employee-app-container form-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 exact path={`${path}`} component={() => <Home />} />
51
- <PrivateRoute path={`${path}/dashboard`} component={() => <Dashboard />} />
52
- <PrivateRoute path={`${path}/inbox`} component={() => <Inbox />} />
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 path={`${path}/aadhaar-verification`} component={() => <AadhaarVerification />} />
75
+ <PrivateRoute
76
+ path={`${path}/aadhaar-verification`}
77
+ component={() => (
78
+ <LayoutWrapper layoutClass="normal">
79
+ <AadhaarVerification />
80
+ </LayoutWrapper>
81
+ )}
82
+ />
55
83
 
56
- <PrivateRoute path={`${path}/address-details`} component={() => <AddressDetails />} />
84
+ <PrivateRoute
85
+ path={`${path}/address-details`}
86
+ component={() => (
87
+ <LayoutWrapper layoutClass="normal">
88
+ <AddressDetails />
89
+ </LayoutWrapper>
90
+ )}
91
+ />
57
92
 
58
- <PrivateRoute path={`${path}/property-info`} component={() => <PropertyInfo />} />
93
+ <PrivateRoute
94
+ path={`${path}/property-info`}
95
+ component={() => (
96
+ <LayoutWrapper layoutClass="normal">
97
+ <PropertyInfo />
98
+ </LayoutWrapper>
99
+ )}
100
+ />
59
101
 
60
- <PrivateRoute path={`${path}/meter-details`} component={() => <MeterDetails />} />
102
+ <PrivateRoute
103
+ path={`${path}/meter-details`}
104
+ component={() => (
105
+ <LayoutWrapper layoutClass="normal">
106
+ <MeterDetails />
107
+ </LayoutWrapper>
108
+ )}
109
+ />
61
110
 
62
- <PrivateRoute path={`${path}/review`} component={() => <Review />} />
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
- </AppContainer>
121
+ </React.Fragment>
66
122
  );
67
123
  };
68
124