@djb25/digit-ui-module-ekyc 1.0.6 → 1.0.8

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.
@@ -1,7 +1,6 @@
1
- import React, { useState, Fragment } from "react";
2
- import { TextInput, Card, SubmitBar, Header, HomeIcon } from "@djb25/digit-ui-react-components";
1
+ import React, { useState } from "react";
2
+ import { TextInput, Card, HomeIcon } from "@djb25/digit-ui-react-components";
3
3
  import { useTranslation } from "react-i18next";
4
- import StatusCards from "./StatusCards";
5
4
 
6
5
  const SearchConsumer = ({ onSearch, searchParams, FilterComponent, children, ...props }) => {
7
6
  const { t } = useTranslation();
@@ -23,110 +22,121 @@ const SearchConsumer = ({ onSearch, searchParams, FilterComponent, children, ...
23
22
  };
24
23
 
25
24
  return (
26
- <div className="inbox-container">
27
- <div className="filters-container">
28
- {/* Sidebar Title Card */}
29
- <Card
30
- className="sidebar-title-card"
31
- style={{ display: "flex", alignItems: "center", padding: "16px", marginBottom: "16px", borderRadius: "4px" }}
32
- >
33
- <div className="icon-container" style={{ color: "#3A8DCC", marginRight: "12px" }}>
34
- <HomeIcon style={{ width: "24px", height: "24px" }} />
35
- </div>
36
- <div style={{ fontWeight: "700", fontSize: "18px", color: "#0B0C0C" }}>{t("ACTION_CREATE_EKYC")}</div>
37
- </Card>
38
-
39
- <div>
40
- {FilterComponent && (
41
- <FilterComponent
42
- defaultSearchParams={props.defaultSearchParams}
43
- onFilterChange={props.onSearch}
44
- searchParams={searchParams}
45
- type="desktop"
46
- moduleCode="EKYC"
47
- />
48
- )}
25
+ <div className="ekyc-employee-container">
26
+ <div className="search-consumer-wrapper">
27
+ <div className="header-wrapper">
28
+ {/* Sidebar Title Card — flush top, full width */}
29
+ <Card className="sidebar-title-card">
30
+ <div className="icon-container">
31
+ <HomeIcon />
32
+ </div>
33
+ <div className="title-text">
34
+ {t("EKYC_SEARCH_CONSUMER_HEADER")}
35
+ </div>
36
+ </Card>
49
37
  </div>
50
- </div>
51
38
 
52
- <div style={{ flex: 1, marginLeft: "16px" }}>
53
- {/* <Card className="ekyc-metrics-card" style={{ marginBottom: "16px", padding: "16px" }}>
54
- <StatusCards countData={props.countData} />
55
- </Card> */}
56
-
57
- <Card
58
- className="ekyc-search-card"
59
- style={{ padding: "24px", marginBottom: "24px", borderRadius: "12px", boxShadow: "0 4px 12px rgba(0,0,0,0.05)" }}
60
- >
61
- <Header style={{ fontSize: "24px", marginBottom: "20px", fontWeight: "700", color: "#0B0C0C" }}>{t("EKYC_SEARCH_CONSUMER_HEADER")}</Header>
62
- <form onSubmit={onSubmit}>
63
- <div style={{ display: "flex", gap: "24px", alignItems: "flex-end", flexWrap: "wrap" }}>
64
- <div style={{ flex: "1", minWidth: "250px" }}>
65
- <div className="filter-label" style={{ fontWeight: "600", marginBottom: "8px", fontSize: "14px", color: "#505A5F" }}>
66
- {t("EKYC_K_NUMBER")}
67
- </div>
68
- <div style={{ position: "relative" }}>
69
- <TextInput
70
- value={_searchParams?.kNumber}
71
- onChange={(e) => onChange("kNumber", e.target.value)}
72
- placeholder={t("EKYC_K_NUMBER_PLACEHOLDER")}
73
- style={{ borderRadius: "8px", paddingLeft: "12px", height: "44px" }}
74
- />
39
+ {/* Main Content */}
40
+ <div className="main-content-wrapper">
41
+ {/* Top Row: Search Card + Stats Cards */}
42
+ <div className="search-stats-row">
43
+ {/* Identity Lookup Card */}
44
+ <div className="identity-lookup-card">
45
+ {/* Card Title */}
46
+ <div className="lookup-card-title">
47
+ <div className="lookup-icon-bg">
48
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#3A7BD5" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
49
+ <circle cx="11" cy="11" r="8" />
50
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
51
+ </svg>
75
52
  </div>
53
+ <span className="lookup-title-text">
54
+ {t("EKYC_IDENTITY_LOOKUP") || "Identity Lookup"}
55
+ </span>
76
56
  </div>
77
57
 
78
- <div style={{ flex: "1", minWidth: "250px" }}>
79
- <div className="filter-label" style={{ fontWeight: "600", marginBottom: "8px", fontSize: "14px", color: "#505A5F" }}>
80
- {t("EKYC_K_NAME")}
58
+ {/* Inputs Row */}
59
+ <form onSubmit={onSubmit}>
60
+ <div className="inputs-row">
61
+ <div className="input-group">
62
+ <label>
63
+ {t("EKYC_K_NUMBER") || "K Number"}
64
+ <span className="info-badge">i</span>
65
+ </label>
66
+ <TextInput
67
+ value={_searchParams?.kNumber}
68
+ onChange={(e) => onChange("kNumber", e.target.value)}
69
+ placeholder={t("EKYC_K_NUMBER_PLACEHOLDER") || "e.g. 8234910234"}
70
+ />
71
+ </div>
72
+
73
+ <div className="input-group">
74
+ <label>
75
+ {t("EKYC_K_NAME") || "K Name"}
76
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#3A7BD5" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
77
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
78
+ <circle cx="12" cy="7" r="4" />
79
+ </svg>
80
+ </label>
81
+ <TextInput
82
+ value={_searchParams?.kName}
83
+ onChange={(e) => onChange("kName", e.target.value)}
84
+ placeholder={t("EKYC_K_NAME_PLACEHOLDER") || "Consumer name search"}
85
+ />
86
+ </div>
81
87
  </div>
82
- <div style={{ position: "relative" }}>
83
- <TextInput
84
- value={_searchParams?.kName}
85
- onChange={(e) => onChange("kName", e.target.value)}
86
- placeholder={t("EKYC_K_NAME_PLACEHOLDER")}
87
- style={{ borderRadius: "8px", paddingLeft: "12px", height: "44px" }}
88
- />
88
+
89
+ {/* Actions Row */}
90
+ <div className="actions-row">
91
+ <button type="button" onClick={onClear} className="clear-btn">
92
+ {t("ES_COMMON_CLEAR") || "Clear"}
93
+ </button>
94
+ <button type="submit" className="search-btn">
95
+ {t("ES_COMMON_SEARCH") || "Execute Search"}
96
+ </button>
89
97
  </div>
90
- </div>
98
+ </form>
91
99
  </div>
92
- <div style={{ display: "flex", justifyContent: "flex-end", marginTop: "20px" }}>
93
- <div style={{ display: "flex", gap: "12px", alignItems: "center" }}>
94
- <button
95
- type="button"
96
- onClick={onClear}
97
- style={{
98
- background: "none",
99
- border: "none",
100
- color: "#0076f3ff",
101
- fontWeight: "600",
102
- cursor: "pointer",
103
- fontSize: "14px",
104
- padding: "10px",
105
- }}
106
- >
107
- {t("ES_COMMON_CLEAR")}
108
- </button>
109
100
 
110
- <SubmitBar
111
- label={t("ES_COMMON_SEARCH")}
112
- onSubmit={onSubmit}
113
- style={{
114
- margin: 0,
115
- borderRadius: "8px",
116
- height: "44px",
117
- padding: "0 32px",
118
- // remove this (it breaks alignment)
119
- // marginTop: "-55px"
120
- }}
121
- />
101
+ {/* Right Stats Column */}
102
+ <div className="stats-column">
103
+ {/* Today's Audits Card */}
104
+ <div className="audits-card">
105
+ <div className="audits-label">{t("EKYC_TODAYS_AUDITS") || "Today's Audits"}</div>
106
+ <div className="audits-count">{props.countData?.todaysAudits || 24}</div>
107
+ <div className="audits-change">
108
+ <span>↗</span>
109
+ {props.countData?.auditChange || "12% increase from yesterday"}
110
+ </div>
111
+ <div className="audits-watermark">M</div>
112
+ </div>
113
+
114
+ {/* Queue Status Card */}
115
+ <div className="queue-card">
116
+ <div className="queue-label">{t("EKYC_QUEUE_STATUS") || "Queue Status"}</div>
117
+ <div className="queue-content">
118
+ <div className="queue-status-text">
119
+ {props.countData?.pendingCount || 3} Pending
120
+ </div>
121
+ <div className="avatar-group">
122
+ {[0, 1].map((i) => (
123
+ <div key={i} className="avatar-item">
124
+ {String.fromCharCode(65 + i)}
125
+ </div>
126
+ ))}
127
+ <div className="avatar-more">+1</div>
128
+ </div>
129
+ </div>
122
130
  </div>
123
131
  </div>
124
- </form>
125
- </Card>
126
- {children}
132
+ </div>
133
+
134
+ {/* Children (ConnectionDetailsView or placeholder) */}
135
+ {children}
136
+ </div>
127
137
  </div>
128
138
  </div>
129
139
  );
130
140
  };
131
141
 
132
- export default SearchConsumer;
142
+ export default SearchConsumer;
@@ -1,26 +1,155 @@
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
+
12
+ const total = countData?.total || 0;
13
+ const pending = countData?.pending || 0;
14
+ const active = countData?.completed || 0;
15
+ const completed = 0;
16
+ const rejected = countData?.rejected || 0;
17
+
18
+ const actualCompleted = countData?.completed || 0;
19
+ const approved = actualCompleted;
20
+
21
+ const efficiency = total > 0 ? Math.round((actualCompleted / total) * 100) : 0;
22
+ const healthPct = total > 0 ? Math.round((approved / total) * 100) : 0;
23
+
24
+ const formatNumber = (num) => new Intl.NumberFormat("en-IN").format(num || 0);
7
25
 
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>
26
+ useEffect(() => {
27
+ if (chartRef1.current) {
28
+ if (chartInstance1.current) chartInstance1.current.destroy();
29
+ const ctx1 = chartRef1.current.getContext("2d");
30
+ chartInstance1.current = new Chart(ctx1, {
31
+ type: "doughnut",
32
+ data: {
33
+ labels: [t("EKYC_ACTIVE"), t("EKYC_PENDING"), t("EKYC_COMPLETED")],
34
+ datasets: [
35
+ {
36
+ data: [active, pending, completed],
37
+ backgroundColor: ["#0c2a52", "#77B6EA", "#c8ddf5"],
38
+ borderColor: ["#ffffff", "#ffffff", "#ffffff"],
39
+ borderWidth: 2,
40
+ hoverOffset: 4,
41
+ },
42
+ ],
43
+ },
44
+ options: {
45
+ cutout: "75%",
46
+ plugins: { legend: { display: false } },
47
+ maintainAspectRatio: false,
48
+ responsive: true,
49
+ },
50
+ });
51
+ }
52
+
53
+ return () => {
54
+ if (chartInstance1.current) chartInstance1.current.destroy();
55
+ };
56
+ }, [pending, completed, active, t]);
57
+
58
+ const legendItems = [
59
+ { color: "#0c2a52", label: t("EKYC_ACTIVE"), value: active },
60
+ { color: "#77B6EA", label: t("EKYC_PENDING"), value: pending },
61
+ { color: "#c8ddf5", label: t("EKYC_COMPLETED"), value: completed },
62
+ ];
63
+
64
+ return (
65
+ <div className="ekyc-employee-container">
66
+ <div className="status-cards-wrapper">
67
+ {/* Header */}
68
+ <div className="status-cards-header">
69
+ <div>
70
+ <div className="eyebrow">
71
+ <span className="eyebrow-dot" />
72
+ {t("EKYC_INSTITUTIONAL_OVERVIEW") || "Institutional Performance Overview"}
73
+ </div>
74
+ <h1 className="status-cards-h1">{t("EKYC_DASHBOARD_TITLE") || "eKYC Verification Dashboard"}</h1>
75
+ <p className="status-cards-subtitle">
76
+ {t("EKYC_DASHBOARD_SUBTITLE") ||
77
+ "Real-time monitoring of consumer verification workflows across all administrative zones."}
78
+ </p>
79
+ </div>
80
+ <div className="total-applications-card">
81
+ <div className="total-label">
82
+ {t("EKYC_TOTAL_APPLICATIONS") || "Total Applications Processed"}
83
+ </div>
84
+ <div className="total-number">{formatNumber(total)}</div>
85
+ <div className="total-badge">↗ +12.4% {t("EKYC_FROM_LAST_QUARTER") || "from last quarter"}</div>
86
+ </div>
87
+ </div>
88
+
89
+ {/* Panels */}
90
+ <div className="status-panels-grid">
91
+ {/* Panel 1: Status Breakdown */}
92
+ <div className="status-panel">
93
+ <div className="panel-title">{t("EKYC_STATUS_BREAKDOWN") || "Status Breakdown"}</div>
94
+ <div className="panel-subtitle">
95
+ {t("EKYC_VERIFICATION_LIFECYCLE") || "Verification lifecycle distribution"}
96
+ </div>
97
+ <div className="breakdown-body">
98
+ <div className="status-legend">
99
+ {legendItems.map((item) => (
100
+ <div key={item.label} className="legend-row">
101
+ <span className="legend-label">
102
+ <span
103
+ className="indicator-dot"
104
+ style={{ background: item.color }}
105
+ />
106
+ {item.label}
107
+ </span>
108
+ <span className="legend-value">{formatNumber(item.value)}</span>
109
+ </div>
110
+ ))}
111
+ </div>
112
+ <div className="chart-wrapper">
113
+ <canvas ref={chartRef1} style={{ width: "100%", height: "100%" }} />
114
+ <div className="chart-center">
115
+ <div className="chart-percentage">{efficiency}%</div>
116
+ <div className="chart-label">{t("EKYC_COMPLETE") || "Complete"}</div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ </div>
121
+
122
+ {/* Panel 2: Submission Health */}
123
+ <div className="status-panel">
124
+ <div className="panel-title">
125
+ {t("EKYC_SUBMISSION_HEALTH") || "Submission Health"}
126
+ <span className="optimal-badge">{t("EKYC_OPTIMAL") || "Optimal"}</span>
127
+ </div>
128
+ <div className="panel-subtitle">
129
+ {t("EKYC_PLATFORM_EFFICIENCY") || "Platform operational efficiency"}
130
+ </div>
131
+ <div className="health-metrics-row">
132
+ <div className="health-percentage">{healthPct}%</div>
133
+ <div className="health-trend">↗ +2.1%</div>
13
134
  </div>
14
- <div className="ekyc-status-card">
15
- <div className="count pending">{countData?.pending || 0}</div>
16
- <div className="label">{t("EKYC_PENDING")}</div>
135
+ <div className="status-progress-bar">
136
+ <div className="progress-fill" style={{ width: `${healthPct}%` }} />
17
137
  </div>
18
- <div className="ekyc-status-card">
19
- <div className="count completed">{countData?.completed || 0}</div>
20
- <div className="label">{t("EKYC_COMPLETED")}</div>
138
+ <div className="mini-metrics-grid">
139
+ <div className="metric-box">
140
+ <div className="metric-label">{t("EKYC_AVG_LATENCY") || "Avg Latency"}</div>
141
+ <div className="metric-value">1.2s</div>
142
+ </div>
143
+ <div className="metric-box">
144
+ <div className="metric-label">{t("EKYC_ERROR_RATE") || "Error Rate"}</div>
145
+ <div className="metric-value">0.04%</div>
146
+ </div>
21
147
  </div>
148
+ </div>
22
149
  </div>
23
- );
150
+ </div>
151
+ </div>
152
+ );
24
153
  };
25
154
 
26
- export default StatusCards;
155
+ export default StatusCards;
@@ -0,0 +1,90 @@
1
+ import { AppContainer, PrivateRoute, ModuleHeader, ArrowLeft, HomeIcon } from "@djb25/digit-ui-react-components";
2
+ import React from "react";
3
+ import { useTranslation } from "react-i18next";
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";
11
+
12
+ const CitizenApp = () => {
13
+ const { t } = useTranslation();
14
+ const location = useLocation();
15
+ const { path } = useRouteMatch();
16
+
17
+ sessionStorage.removeItem("revalidateddone");
18
+
19
+ const getBreadcrumbLabel = () => {
20
+ const pathname = location.pathname;
21
+ if (pathname.includes("/create-kyc")) return "EKYC_CREATE_KYC";
22
+ if (pathname.includes("/aadhaar-verification")) return "EKYC_AADHAAR_VERIFICATION";
23
+ if (pathname.includes("/address-details")) return "EKYC_ADDRESS_DETAILS";
24
+ if (pathname.includes("/property-info")) return "EKYC_PROPERTY_INFO";
25
+ if (pathname.includes("/meter-details")) return "EKYC_METER_DETAILS";
26
+ if (pathname.includes("/review")) return "EKYC_REVIEW";
27
+ return "EKYC_HOME";
28
+ };
29
+
30
+ const breadcrumbs = [
31
+ { icon: HomeIcon, path: "/digit-ui/citizen" },
32
+ { label: t(getBreadcrumbLabel()) }
33
+ ];
34
+
35
+ return (
36
+ <AppContainer>
37
+ <div className="ground-container employee-app-container form-container">
38
+ <ModuleHeader
39
+ leftContent={
40
+ <React.Fragment>
41
+ <ArrowLeft className="icon" />
42
+ {t("CS_COMMON_BACK")}
43
+ </React.Fragment>
44
+ }
45
+ onLeftClick={() => window.history.back()}
46
+ breadcrumbs={breadcrumbs}
47
+ />
48
+
49
+ <Switch>
50
+ <PrivateRoute
51
+ path={`${path}/create-kyc`}
52
+ component={() => <Create />}
53
+ />
54
+
55
+ <PrivateRoute
56
+ path={`${path}/aadhaar-verification`}
57
+ component={() => <AadhaarVerification />}
58
+ />
59
+
60
+ <PrivateRoute
61
+ path={`${path}/address-details`}
62
+ component={() => <AddressDetails />}
63
+ />
64
+
65
+ <PrivateRoute
66
+ path={`${path}/property-info`}
67
+ component={() => <PropertyInfo />}
68
+ />
69
+
70
+ <PrivateRoute
71
+ path={`${path}/meter-details`}
72
+ component={() => <MeterDetails />}
73
+ />
74
+
75
+ <PrivateRoute
76
+ path={`${path}/review`}
77
+ component={() => <Review />}
78
+ />
79
+
80
+ <PrivateRoute
81
+ path={`${path}/`}
82
+ component={() => <Create />}
83
+ />
84
+ </Switch>
85
+ </div>
86
+ </AppContainer>
87
+ );
88
+ };
89
+
90
+ export default CitizenApp;