@djb25/digit-ui-module-ekyc 1.0.11 → 1.0.12
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.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +1338 -591
- package/dist/index.modern.js.map +1 -1
- package/package.json +1 -1
- package/src/Module.js +25 -7
- package/src/components/AadhaarVerification.js +415 -0
- package/src/components/AddressDetails.js +207 -0
- package/src/components/CeoDashboard.js +205 -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 +44 -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,205 @@
|
|
|
1
|
+
import React, { useState, useMemo } from "react";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
import "./analytics/styles/Dashboard.css";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// Hooks
|
|
7
|
+
import useInboxRouting from "./analytics/hooks/useInboxRouting";
|
|
8
|
+
|
|
9
|
+
// Components
|
|
10
|
+
import DashboardLayout from "./analytics/components/DashboardLayout";
|
|
11
|
+
import FilterBar from "./analytics/components/FilterBar";
|
|
12
|
+
import SummaryCard from "./analytics/components/SummaryCard";
|
|
13
|
+
import TaskStatusChart from "./analytics/charts/TaskStatusChart";
|
|
14
|
+
import ClusterHeatmap from "./analytics/charts/ClusterHeatmap";
|
|
15
|
+
import AnalyticsTable from "./analytics/components/AnalyticsTable";
|
|
16
|
+
import SLAWidget from "./analytics/components/SLAWidget";
|
|
17
|
+
import WorkflowTimeline from "./analytics/components/WorkflowTimeline";
|
|
18
|
+
import NotificationPanel from "./analytics/components/NotificationPanel";
|
|
19
|
+
import SkeletonLoader from "./analytics/components/SkeletonLoader";
|
|
20
|
+
import ErrorBoundary from "./analytics/components/ErrorBoundary";
|
|
21
|
+
import EmptyState from "./analytics/components/EmptyState";
|
|
22
|
+
|
|
23
|
+
const CeoDashboard = () => {
|
|
24
|
+
const { t } = useTranslation();
|
|
25
|
+
const { routeToInbox } = useInboxRouting();
|
|
26
|
+
|
|
27
|
+
// 1. Dashboard State
|
|
28
|
+
const [activeRole, setActiveRole] = useState("CEO");
|
|
29
|
+
const [isNotificationOpen, setIsNotificationOpen] = useState(false);
|
|
30
|
+
const [filters, setFilters] = useState({
|
|
31
|
+
financialYear: "2025-26",
|
|
32
|
+
clusterId: "ALL",
|
|
33
|
+
agencyId: "ALL"
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 2. Fetch Config & Data
|
|
37
|
+
const { config, tenantId } = Digit.Hooks.ekyc.useEkycDashboardConfigs(activeRole);
|
|
38
|
+
const {
|
|
39
|
+
summary: kpiData, agencies: agencyData, heatmap: clusterData, workflow: workflowData,
|
|
40
|
+
isLoading, isError
|
|
41
|
+
} = Digit.Hooks.ekyc.useEkycDashboardData(activeRole, filters);
|
|
42
|
+
|
|
43
|
+
// 3. Handlers
|
|
44
|
+
const handleFilterChange = (id, value) => {
|
|
45
|
+
setFilters(prev => ({ ...prev, [id]: value }));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const handleReset = () => {
|
|
49
|
+
setFilters({ financialYear: "2025-26", clusterId: "ALL", agencyId: "ALL" });
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const handleKpiClick = (kpi) => {
|
|
53
|
+
routeToInbox(kpi.targetRoute, { ...filters, status: kpi.status });
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Mock Notifications
|
|
57
|
+
const notifications = [
|
|
58
|
+
{ title: "EKYC_SLA_BREACH_ALERT", message: "EKYC_ALERT_DESC_1", priority: "HIGH", time: "10m ago" },
|
|
59
|
+
{ title: "EKYC_SYSTEM_UPDATE", message: "EKYC_ALERT_DESC_3", priority: "NORMAL", time: "5h ago" }
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
// 4. Render Logic
|
|
63
|
+
if (isError) return <EmptyState message="EKYC_ERROR_FETCHING_DATA" />;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<DashboardLayout
|
|
67
|
+
header={config.title}
|
|
68
|
+
activeRole={activeRole}
|
|
69
|
+
onRoleChange={(role) => {
|
|
70
|
+
setActiveRole(role);
|
|
71
|
+
handleReset();
|
|
72
|
+
}}
|
|
73
|
+
onNotificationClick={() => setIsNotificationOpen(true)}
|
|
74
|
+
filters={
|
|
75
|
+
<FilterBar
|
|
76
|
+
filters={filters}
|
|
77
|
+
config={config.globalFilters}
|
|
78
|
+
onFilterChange={handleFilterChange}
|
|
79
|
+
onReset={handleReset}
|
|
80
|
+
/>
|
|
81
|
+
}
|
|
82
|
+
>
|
|
83
|
+
<NotificationPanel
|
|
84
|
+
isOpen={isNotificationOpen}
|
|
85
|
+
onClose={() => setIsNotificationOpen(false)}
|
|
86
|
+
notifications={notifications}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
{/* KPI Section */}
|
|
90
|
+
<section style={{ marginBottom: "32px" }}>
|
|
91
|
+
{isLoading ? (
|
|
92
|
+
<SkeletonLoader type="card" count={4} />
|
|
93
|
+
) : (
|
|
94
|
+
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))", gap: "24px" }}>
|
|
95
|
+
{config?.widgets?.summary?.map((kpiKey, idx) => {
|
|
96
|
+
const kpiMeta = config?.kpis?.[kpiKey];
|
|
97
|
+
if (!kpiMeta) return null;
|
|
98
|
+
|
|
99
|
+
const value = kpiData?.[kpiKey] || 0;
|
|
100
|
+
return (
|
|
101
|
+
<ErrorBoundary key={kpiKey}>
|
|
102
|
+
<div className="animate-fade-in" style={{ animationDelay: `${idx * 0.1}s` }}>
|
|
103
|
+
<SummaryCard
|
|
104
|
+
label={kpiMeta.label}
|
|
105
|
+
value={value}
|
|
106
|
+
color={kpiMeta.color}
|
|
107
|
+
icon={kpiMeta.icon}
|
|
108
|
+
trend={kpiData?.[`${kpiKey}Trend`]}
|
|
109
|
+
onClick={() => handleKpiClick(kpiMeta)}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
</ErrorBoundary>
|
|
113
|
+
);
|
|
114
|
+
})}
|
|
115
|
+
</div>
|
|
116
|
+
)}
|
|
117
|
+
</section>
|
|
118
|
+
|
|
119
|
+
{/* Main Analytics Grid */}
|
|
120
|
+
<div style={{ display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap: "24px" }}>
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
{/* Workflow Distribution */}
|
|
125
|
+
<div style={{ gridColumn: "span 12", background: "#FFF", padding: "24px", borderRadius: "16px", boxShadow: "0 1px 3px rgba(0,0,0,0.1)" }}>
|
|
126
|
+
{isLoading ? (
|
|
127
|
+
<SkeletonLoader type="chart" />
|
|
128
|
+
) : (
|
|
129
|
+
<ErrorBoundary>
|
|
130
|
+
<TaskStatusChart
|
|
131
|
+
title="EKYC_APPLICATION_STATUS"
|
|
132
|
+
data={workflowData?.stageBreakdown || []}
|
|
133
|
+
/>
|
|
134
|
+
</ErrorBoundary>
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{/* SLA & Timeline Bottlenecks */}
|
|
139
|
+
<div style={{ gridColumn: "span 4" }}>
|
|
140
|
+
{isLoading ? (
|
|
141
|
+
<SkeletonLoader type="chart" />
|
|
142
|
+
) : (
|
|
143
|
+
<ErrorBoundary>
|
|
144
|
+
<SLAWidget
|
|
145
|
+
slaPercentage={workflowData?.slaCompliance || 0}
|
|
146
|
+
avgTime={workflowData?.avgProcessingTimeHours || 0}
|
|
147
|
+
breachedCount={workflowData?.breachCount || 0}
|
|
148
|
+
/>
|
|
149
|
+
</ErrorBoundary>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div style={{ gridColumn: "span 8" }}>
|
|
154
|
+
{isLoading ? (
|
|
155
|
+
<SkeletonLoader type="chart" />
|
|
156
|
+
) : (
|
|
157
|
+
<ErrorBoundary>
|
|
158
|
+
<WorkflowTimeline stages={workflowData?.stageBreakdown || []} />
|
|
159
|
+
</ErrorBoundary>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
{/* Spatial Cluster Analysis */}
|
|
164
|
+
<div style={{ gridColumn: "span 12", background: "#FFF", padding: "24px", borderRadius: "16px", boxShadow: "0 1px 3px rgba(0,0,0,0.1)" }}>
|
|
165
|
+
{isLoading ? (
|
|
166
|
+
<SkeletonLoader type="chart" />
|
|
167
|
+
) : (
|
|
168
|
+
<ErrorBoundary>
|
|
169
|
+
<ClusterHeatmap
|
|
170
|
+
title="EKYC_CLUSTER_WORKLOAD_HEATMAP"
|
|
171
|
+
data={clusterData || []}
|
|
172
|
+
onDrillDown={(cluster) => handleFilterChange("clusterId", cluster.clusterId)}
|
|
173
|
+
/>
|
|
174
|
+
</ErrorBoundary>
|
|
175
|
+
)}
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
{/* Agency Performance Table */}
|
|
179
|
+
<div style={{ gridColumn: "span 12" }}>
|
|
180
|
+
{isLoading ? (
|
|
181
|
+
<SkeletonLoader type="table" />
|
|
182
|
+
) : (
|
|
183
|
+
<ErrorBoundary>
|
|
184
|
+
<AnalyticsTable
|
|
185
|
+
title="EKYC_AGENCY_PERFORMANCE_METRICS"
|
|
186
|
+
filename="agency_performance_report.csv"
|
|
187
|
+
data={agencyData || []}
|
|
188
|
+
columns={[
|
|
189
|
+
{ id: "agencyName", label: "EKYC_AGENCY_NAME" },
|
|
190
|
+
{ id: "totalAssigned", label: "EKYC_TOTAL_ASSIGNED" },
|
|
191
|
+
{ id: "totalCompleted", label: "EKYC_TOTAL_COMPLETED" },
|
|
192
|
+
{ id: "pendingCount", label: "EKYC_PENDING" },
|
|
193
|
+
{ id: "slaCompliance", label: "EKYC_SLA_COMPLIANCE", isPercentage: true }
|
|
194
|
+
]}
|
|
195
|
+
/>
|
|
196
|
+
</ErrorBoundary>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
</div>
|
|
201
|
+
</DashboardLayout>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export default CeoDashboard;
|
|
@@ -32,6 +32,10 @@ const EKYCCard = () => {
|
|
|
32
32
|
// label: t("EKYC_UPDATE_KYC"),
|
|
33
33
|
// link: `/digit-ui/employee/ekyc/update-kyc`
|
|
34
34
|
// },
|
|
35
|
+
{
|
|
36
|
+
label: t("CEO_M.F_DOR_FINANCE_VIEW"),
|
|
37
|
+
link: `/digit-ui/employee/ekyc/ceo-dashboard`
|
|
38
|
+
},
|
|
35
39
|
{
|
|
36
40
|
label: t("EKYC_MAPPING"),
|
|
37
41
|
link: `/digit-ui/employee/ekyc/mapping`,
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import React, { useState, Fragment, useEffect } from "react";
|
|
2
|
+
import { useLocation } from "react-router-dom";
|
|
3
|
+
import {
|
|
4
|
+
CardLabel,
|
|
5
|
+
TextInput,
|
|
6
|
+
Dropdown,
|
|
7
|
+
UploadFile,
|
|
8
|
+
Toast,
|
|
9
|
+
FormStep,
|
|
10
|
+
Loader
|
|
11
|
+
} from "@djb25/digit-ui-react-components";
|
|
12
|
+
|
|
13
|
+
const MeterDetails = ({ config, onSelect, formData }) => {
|
|
14
|
+
const location = useLocation();
|
|
15
|
+
const flowState = location.state || {};
|
|
16
|
+
const tenantId = Digit.ULBService.getCurrentTenantId();
|
|
17
|
+
|
|
18
|
+
const searchKno = flowState?.kNumber || flowState?.kno || formData?.kNumber || formData?.kno || sessionStorage.getItem("EKYC_K_NUMBER");
|
|
19
|
+
|
|
20
|
+
const { isLoading, data: searchData } = Digit.Hooks.ekyc.useSearchConnection(
|
|
21
|
+
{ tenantId, details: { kno: searchKno } },
|
|
22
|
+
{ enabled: !!searchKno, cacheTime: 0 }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const updateMutation = Digit.Hooks.ekyc.useEkycUpdate(tenantId);
|
|
26
|
+
|
|
27
|
+
const savedData = formData?.meterDetails || {};
|
|
28
|
+
|
|
29
|
+
// 🔹 STATES
|
|
30
|
+
const [connectionCategory, setConnectionCategory] = useState(savedData.connectionCategory || "");
|
|
31
|
+
const [saType, setSaType] = useState(savedData.saType || "");
|
|
32
|
+
const [status, setStatus] = useState(savedData.status || "");
|
|
33
|
+
|
|
34
|
+
const [mrCode, setMrCode] = useState(savedData.mrCode || "");
|
|
35
|
+
const [areaCode, setAreaCode] = useState(savedData.areaCode || "");
|
|
36
|
+
const [mrKey, setMrKey] = useState(savedData.mrKey || "");
|
|
37
|
+
|
|
38
|
+
const [meterNumber, setMeterNumber] = useState(savedData.meterNumber || "");
|
|
39
|
+
const [meterMaker, setMeterMaker] = useState(savedData.meterMaker || "");
|
|
40
|
+
|
|
41
|
+
const [meterStatus, setMeterStatus] = useState(savedData.meterStatus ? { name: savedData.meterStatus } : null);
|
|
42
|
+
const [meterCondition, setMeterCondition] = useState(savedData.meterCondition ? { name: savedData.meterCondition } : null);
|
|
43
|
+
const [meterLocation, setMeterLocation] = useState(savedData.meterLocation ? { name: savedData.meterLocation } : null);
|
|
44
|
+
|
|
45
|
+
const [lastBillReceived, setLastBillReceived] = useState(savedData.lastBillReceived ? { name: savedData.lastBillReceived } : null);
|
|
46
|
+
const [billMonthYear, setBillMonthYear] = useState(savedData.billMonthYear ? { name: savedData.billMonthYear } : null);
|
|
47
|
+
const [reason, setReason] = useState(savedData.reason || "");
|
|
48
|
+
|
|
49
|
+
const [accessToMeter, setAccessToMeter] = useState(savedData.accessToMeter ? { name: savedData.accessToMeter } : null);
|
|
50
|
+
const [sewerConnection, setSewerConnection] = useState(savedData.sewerConnection ? { name: savedData.sewerConnection } : null);
|
|
51
|
+
const [septicTank, setSepticTank] = useState(savedData.septicTank ? { name: savedData.septicTank } : null);
|
|
52
|
+
|
|
53
|
+
const [meterPhoto, setMeterPhoto] = useState(null);
|
|
54
|
+
const [meterPhotoId, setMeterPhotoId] = useState(savedData.meterPhotoId || null);
|
|
55
|
+
|
|
56
|
+
const [toast, setToast] = useState(null);
|
|
57
|
+
|
|
58
|
+
// 🔹 OPTIONS
|
|
59
|
+
const yesNo = [{ name: "Yes" }, { name: "No" }];
|
|
60
|
+
|
|
61
|
+
const meterStatusOptions = [
|
|
62
|
+
{ name: "Metered" },
|
|
63
|
+
{ name: "Unmetered" },
|
|
64
|
+
{ name: "Can not be identified" },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const meterConditionOptions = [
|
|
68
|
+
{ name: "Damaged" },
|
|
69
|
+
{ name: "Not-Damaged" },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const meterLocationOptions = [
|
|
73
|
+
{ name: "Inside" },
|
|
74
|
+
{ name: "Outside" },
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// 🔹 MONTH-YEAR OPTIONS (1998–2026)
|
|
78
|
+
const monthYearOptions = [];
|
|
79
|
+
for (let y = 1998; y <= 2026; y++) {
|
|
80
|
+
for (let m = 1; m <= 12; m++) {
|
|
81
|
+
monthYearOptions.push({ name: `${m}/${y}` });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const rawData = searchData || formData?.connectionDetails;
|
|
87
|
+
const apiMeter = rawData?.meterDetails || rawData || {};
|
|
88
|
+
|
|
89
|
+
if (apiMeter && Object.keys(apiMeter).length > 0 && !savedData.connectionCategory) {
|
|
90
|
+
if (apiMeter.connectionCategory) setConnectionCategory(apiMeter.connectionCategory);
|
|
91
|
+
if (apiMeter.saType) setSaType(apiMeter.saType);
|
|
92
|
+
if (apiMeter.statusFlag) setStatus(apiMeter.statusFlag);
|
|
93
|
+
|
|
94
|
+
if (apiMeter.mrcode) setMrCode(String(apiMeter.mrcode));
|
|
95
|
+
if (apiMeter.areacode) setAreaCode(String(apiMeter.areacode));
|
|
96
|
+
if (apiMeter.mrkey) setMrKey(String(apiMeter.mrkey));
|
|
97
|
+
|
|
98
|
+
if (apiMeter.meterNumber) setMeterNumber(apiMeter.meterNumber);
|
|
99
|
+
if (apiMeter.meterMake) setMeterMaker(apiMeter.meterMake);
|
|
100
|
+
|
|
101
|
+
if (apiMeter.meterStatus) {
|
|
102
|
+
const matchingStatus = meterStatusOptions.find(o => o.name.toLowerCase() === apiMeter.meterStatus.toLowerCase());
|
|
103
|
+
if (matchingStatus) setMeterStatus(matchingStatus);
|
|
104
|
+
else setMeterStatus({ name: apiMeter.meterStatus });
|
|
105
|
+
} else if (apiMeter.metered !== undefined) {
|
|
106
|
+
setMeterStatus({ name: apiMeter.metered ? "Metered" : "Unmetered" });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (apiMeter.meterCondition) {
|
|
110
|
+
const matchingCond = meterConditionOptions.find(o => o.name.toLowerCase() === apiMeter.meterCondition.toLowerCase());
|
|
111
|
+
if (matchingCond) setMeterCondition(matchingCond);
|
|
112
|
+
else setMeterCondition({ name: apiMeter.meterCondition });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (apiMeter.meterLocation) {
|
|
116
|
+
const matchingLoc = meterLocationOptions.find(o => o.name.toLowerCase() === apiMeter.meterLocation.toLowerCase() || apiMeter.meterLocation.toLowerCase().includes(o.name.toLowerCase()));
|
|
117
|
+
if (matchingLoc) setMeterLocation(matchingLoc);
|
|
118
|
+
else setMeterLocation({ name: apiMeter.meterLocation });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (apiMeter.lastBillRaised !== undefined && apiMeter.lastBillRaised !== null) {
|
|
122
|
+
const strVal = String(apiMeter.lastBillRaised).toLowerCase();
|
|
123
|
+
if (strVal === "true" || strVal === "yes") setLastBillReceived({ name: "Yes" });
|
|
124
|
+
else setLastBillReceived({ name: "No" });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (apiMeter.lastBillReceivedDate) {
|
|
128
|
+
const formatted = apiMeter.lastBillReceivedDate.replace("-", "/");
|
|
129
|
+
const parsedParts = formatted.split("/");
|
|
130
|
+
if (parsedParts.length === 2) {
|
|
131
|
+
const mon = parseInt(parsedParts[0], 10);
|
|
132
|
+
const yr = parseInt(parsedParts[1], 10);
|
|
133
|
+
setBillMonthYear({ name: `${mon}/${yr}` });
|
|
134
|
+
} else {
|
|
135
|
+
setBillMonthYear({ name: apiMeter.lastBillReceivedDate });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (apiMeter.lastBillNotRaisedReason) setReason(apiMeter.lastBillNotRaisedReason);
|
|
140
|
+
|
|
141
|
+
if (apiMeter.accessToMeter !== undefined && apiMeter.accessToMeter !== null) {
|
|
142
|
+
const strVal = String(apiMeter.accessToMeter).toLowerCase();
|
|
143
|
+
if (strVal === "true" || strVal === "yes") setAccessToMeter({ name: "Yes" });
|
|
144
|
+
else setAccessToMeter({ name: "No" });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (apiMeter.sewerConnection !== undefined && apiMeter.sewerConnection !== null) {
|
|
148
|
+
const strVal = String(apiMeter.sewerConnection).toLowerCase();
|
|
149
|
+
if (strVal === "true" || strVal === "yes") setSewerConnection({ name: "Yes" });
|
|
150
|
+
else setSewerConnection({ name: "No" });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (apiMeter.septicTank !== undefined && apiMeter.septicTank !== null) {
|
|
154
|
+
const strVal = String(apiMeter.septicTank).toLowerCase();
|
|
155
|
+
if (strVal === "true" || strVal === "yes") setSepticTank({ name: "Yes" });
|
|
156
|
+
else setSepticTank({ name: "No" });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (apiMeter.meterPhotoFileStoreId) setMeterPhotoId(apiMeter.meterPhotoFileStoreId);
|
|
160
|
+
}
|
|
161
|
+
}, [searchData, formData?.connectionDetails]);
|
|
162
|
+
|
|
163
|
+
// 🔹 FREEZE LOGIC
|
|
164
|
+
const isFrozen = meterStatus?.name === "Can not be identified";
|
|
165
|
+
|
|
166
|
+
// 🔹 FILE UPLOAD
|
|
167
|
+
const uploadPhoto = async (e) => {
|
|
168
|
+
const file = e.target.files[0];
|
|
169
|
+
if (!file) return;
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const res = await Digit.UploadServices.Filestorage("EKYC", file, tenantId);
|
|
173
|
+
const id = res?.data?.files?.[0]?.fileStoreId;
|
|
174
|
+
|
|
175
|
+
if (id) {
|
|
176
|
+
setMeterPhotoId(id);
|
|
177
|
+
|
|
178
|
+
const reader = new FileReader();
|
|
179
|
+
reader.onloadend = () => setMeterPhoto(reader.result);
|
|
180
|
+
reader.readAsDataURL(file);
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
setToast({ type: "error", message: "Upload failed" });
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// 🔹 VALIDATION
|
|
188
|
+
const isValid = () => {
|
|
189
|
+
if (!connectionCategory) return false;
|
|
190
|
+
if (!meterStatus) return false;
|
|
191
|
+
if (!meterLocation) return false;
|
|
192
|
+
if (!lastBillReceived) return false;
|
|
193
|
+
if (!sewerConnection) return false;
|
|
194
|
+
|
|
195
|
+
if (meterStatus?.name === "Metered" && !meterPhotoId) return false;
|
|
196
|
+
|
|
197
|
+
if (lastBillReceived?.name === "No" && !reason) return false;
|
|
198
|
+
if (lastBillReceived?.name === "Yes" && !billMonthYear) return false;
|
|
199
|
+
|
|
200
|
+
if (sewerConnection?.name === "No" && !septicTank) return false;
|
|
201
|
+
|
|
202
|
+
return true;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// 🔹 SUBMIT
|
|
206
|
+
const onStepSelect = async () => {
|
|
207
|
+
/*
|
|
208
|
+
if (!isValid()) {
|
|
209
|
+
setToast({ type: "error", message: "Fill all mandatory fields" });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
*/
|
|
213
|
+
|
|
214
|
+
const data = {
|
|
215
|
+
connectionCategory,
|
|
216
|
+
saType,
|
|
217
|
+
status,
|
|
218
|
+
mrCode,
|
|
219
|
+
areaCode,
|
|
220
|
+
mrKey,
|
|
221
|
+
meterNumber,
|
|
222
|
+
meterMaker,
|
|
223
|
+
meterStatus: meterStatus?.name,
|
|
224
|
+
meterCondition: meterCondition?.name,
|
|
225
|
+
meterLocation: meterLocation?.name,
|
|
226
|
+
lastBillReceived: lastBillReceived?.name,
|
|
227
|
+
billMonthYear: billMonthYear?.name,
|
|
228
|
+
reason,
|
|
229
|
+
accessToMeter: accessToMeter?.name,
|
|
230
|
+
sewerConnection: sewerConnection?.name,
|
|
231
|
+
septicTank: septicTank?.name,
|
|
232
|
+
meterPhotoId,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
await updateMutation.mutateAsync({
|
|
237
|
+
RequestInfo: {},
|
|
238
|
+
updateType: "METER",
|
|
239
|
+
kno: searchKno,
|
|
240
|
+
...data,
|
|
241
|
+
});
|
|
242
|
+
setToast({ type: "success", message: "Meter details updated successfully!" });
|
|
243
|
+
onSelect(config.key, data);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
setToast({ type: "error", message: "Failed to update meter details" });
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
if (isLoading) {
|
|
250
|
+
return <Loader />;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<Fragment>
|
|
255
|
+
<FormStep onSelect={onStepSelect} config={config} isDisabled={!isValid()}>
|
|
256
|
+
<div>
|
|
257
|
+
<CardLabel>Connection Category *</CardLabel>
|
|
258
|
+
<TextInput value={connectionCategory} onChange={(e) => setConnectionCategory(e.target.value)} />
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<div>
|
|
262
|
+
<CardLabel>SA Type</CardLabel>
|
|
263
|
+
<TextInput value={saType} onChange={(e) => setSaType(e.target.value)} />
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div>
|
|
267
|
+
<CardLabel>Status</CardLabel>
|
|
268
|
+
<TextInput value={status} onChange={(e) => setStatus(e.target.value)} />
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div>
|
|
272
|
+
<CardLabel>MR Code</CardLabel>
|
|
273
|
+
<TextInput value={mrCode} onChange={(e) => setMrCode(e.target.value)} />
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div>
|
|
277
|
+
<CardLabel>Area Code</CardLabel>
|
|
278
|
+
<TextInput value={areaCode} onChange={(e) => setAreaCode(e.target.value)} />
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<div>
|
|
282
|
+
<CardLabel>MR Key</CardLabel>
|
|
283
|
+
<TextInput value={mrKey} onChange={(e) => setMrKey(e.target.value)} />
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
{!isFrozen && (
|
|
287
|
+
<Fragment>
|
|
288
|
+
<div>
|
|
289
|
+
<CardLabel>Meter Number</CardLabel>
|
|
290
|
+
<TextInput value={meterNumber} onChange={(e) => setMeterNumber(e.target.value)} />
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<div>
|
|
294
|
+
<CardLabel>Meter Maker</CardLabel>
|
|
295
|
+
<TextInput value={meterMaker} onChange={(e) => setMeterMaker(e.target.value)} />
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div>
|
|
299
|
+
<CardLabel>Meter Condition</CardLabel>
|
|
300
|
+
<Dropdown option={meterConditionOptions} selected={meterCondition} select={setMeterCondition} />
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
{meterStatus?.name === "Metered" && (
|
|
304
|
+
<Fragment>
|
|
305
|
+
<div>
|
|
306
|
+
<CardLabel>Meter Photo *</CardLabel>
|
|
307
|
+
<UploadFile onUpload={uploadPhoto} message={meterPhotoId ? "Uploaded" : "No file"} />
|
|
308
|
+
</div>
|
|
309
|
+
{meterPhoto && (
|
|
310
|
+
<div style={{ gridColumn: "span 2" }}>
|
|
311
|
+
<img src={meterPhoto} style={{ width: "100%" }} />
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
</Fragment>
|
|
315
|
+
)}
|
|
316
|
+
</Fragment>
|
|
317
|
+
)}
|
|
318
|
+
|
|
319
|
+
<div>
|
|
320
|
+
<CardLabel>Meter Status *</CardLabel>
|
|
321
|
+
<Dropdown option={meterStatusOptions} selected={meterStatus} select={setMeterStatus} />
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
<div>
|
|
325
|
+
<CardLabel>Meter Location *</CardLabel>
|
|
326
|
+
<Dropdown option={meterLocationOptions} selected={meterLocation} select={setMeterLocation} />
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div>
|
|
330
|
+
<CardLabel>Last Bill Received *</CardLabel>
|
|
331
|
+
<Dropdown option={yesNo} selected={lastBillReceived} select={setLastBillReceived} />
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
{lastBillReceived?.name === "Yes" && (
|
|
335
|
+
<div>
|
|
336
|
+
<CardLabel>When was the last bill received *</CardLabel>
|
|
337
|
+
<Dropdown option={monthYearOptions} selected={billMonthYear} select={setBillMonthYear} />
|
|
338
|
+
</div>
|
|
339
|
+
)}
|
|
340
|
+
|
|
341
|
+
{lastBillReceived?.name === "No" && (
|
|
342
|
+
<div>
|
|
343
|
+
<CardLabel>Reason *</CardLabel>
|
|
344
|
+
<TextInput value={reason} onChange={(e) => setReason(e.target.value)} />
|
|
345
|
+
</div>
|
|
346
|
+
)}
|
|
347
|
+
|
|
348
|
+
<div>
|
|
349
|
+
<CardLabel>Access to Meter</CardLabel>
|
|
350
|
+
<Dropdown option={yesNo} selected={accessToMeter} select={setAccessToMeter} />
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<div>
|
|
354
|
+
<CardLabel>Sewer Connection *</CardLabel>
|
|
355
|
+
<Dropdown option={yesNo} selected={sewerConnection} select={setSewerConnection} />
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
{sewerConnection?.name === "No" && (
|
|
359
|
+
<div>
|
|
360
|
+
<CardLabel>Septic Tank *</CardLabel>
|
|
361
|
+
<Dropdown option={yesNo} selected={septicTank} select={setSepticTank} />
|
|
362
|
+
</div>
|
|
363
|
+
)}
|
|
364
|
+
|
|
365
|
+
{toast && <Toast label={toast.message} error={toast.type === "error"} onClose={() => setToast(null)} />}
|
|
366
|
+
|
|
367
|
+
</FormStep>
|
|
368
|
+
</Fragment>
|
|
369
|
+
);
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export default MeterDetails;
|