@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,201 @@
1
+ import React, { useState, useMemo } from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import "./analytics/styles/Dashboard.css";
4
+
5
+ // Components
6
+ import DashboardLayout from "./analytics/components/DashboardLayout";
7
+ import FilterBar from "./analytics/components/FilterBar";
8
+ import SummaryCard from "./analytics/components/SummaryCard";
9
+ import TaskStatusChart from "./analytics/charts/TaskStatusChart";
10
+ import ClusterHeatmap from "./analytics/charts/ClusterHeatmap";
11
+ import AnalyticsTable from "./analytics/components/AnalyticsTable";
12
+ import SLAWidget from "./analytics/components/SLAWidget";
13
+ import WorkflowTimeline from "./analytics/components/WorkflowTimeline";
14
+ import NotificationPanel from "./analytics/components/NotificationPanel";
15
+ import SkeletonLoader from "./analytics/components/SkeletonLoader";
16
+ import ErrorBoundary from "./analytics/components/ErrorBoundary";
17
+ import EmptyState from "./analytics/components/EmptyState";
18
+
19
+ const CeoDashboard = () => {
20
+ const { t } = useTranslation();
21
+ const { routeToInbox } = Digit.Hooks.ekyc.useInboxRouting();
22
+
23
+ // 1. Dashboard State
24
+ const [activeRole, setActiveRole] = useState("CEO");
25
+ const [isNotificationOpen, setIsNotificationOpen] = useState(false);
26
+ const [filters, setFilters] = useState({
27
+ financialYear: "2025-26",
28
+ clusterId: "ALL",
29
+ agencyId: "ALL"
30
+ });
31
+
32
+ // 2. Fetch Config & Data
33
+ const { config, tenantId } = Digit.Hooks.ekyc.useEkycDashboardConfigs(activeRole);
34
+ const {
35
+ summary: kpiData, agencies: agencyData, heatmap: clusterData, workflow: workflowData,
36
+ isLoading, isError
37
+ } = Digit.Hooks.ekyc.useEkycDashboardData(activeRole, filters);
38
+
39
+ // 3. Handlers
40
+ const handleFilterChange = (id, value) => {
41
+ setFilters(prev => ({ ...prev, [id]: value }));
42
+ };
43
+
44
+ const handleReset = () => {
45
+ setFilters({ financialYear: "2025-26", clusterId: "ALL", agencyId: "ALL" });
46
+ };
47
+
48
+ const handleKpiClick = (kpi) => {
49
+ routeToInbox(kpi.targetRoute, { ...filters, status: kpi.status });
50
+ };
51
+
52
+ // Mock Notifications
53
+ const notifications = [
54
+ { title: "EKYC_SLA_BREACH_ALERT", message: "EKYC_ALERT_DESC_1", priority: "HIGH", time: "10m ago" },
55
+ { title: "EKYC_SYSTEM_UPDATE", message: "EKYC_ALERT_DESC_3", priority: "NORMAL", time: "5h ago" }
56
+ ];
57
+
58
+ // 4. Render Logic
59
+ if (isError) return <EmptyState message="EKYC_ERROR_FETCHING_DATA" />;
60
+
61
+ return (
62
+ <DashboardLayout
63
+ header={config.title}
64
+ activeRole={activeRole}
65
+ onRoleChange={(role) => {
66
+ setActiveRole(role);
67
+ handleReset();
68
+ }}
69
+ onNotificationClick={() => setIsNotificationOpen(true)}
70
+ filters={
71
+ <FilterBar
72
+ filters={filters}
73
+ config={config.globalFilters}
74
+ onFilterChange={handleFilterChange}
75
+ onReset={handleReset}
76
+ />
77
+ }
78
+ >
79
+ <NotificationPanel
80
+ isOpen={isNotificationOpen}
81
+ onClose={() => setIsNotificationOpen(false)}
82
+ notifications={notifications}
83
+ />
84
+
85
+ {/* KPI Section */}
86
+ <section style={{ marginBottom: "32px" }}>
87
+ {isLoading ? (
88
+ <SkeletonLoader type="card" count={4} />
89
+ ) : (
90
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))", gap: "24px" }}>
91
+ {config?.widgets?.summary?.map((kpiKey, idx) => {
92
+ const kpiMeta = config?.kpis?.[kpiKey];
93
+ if (!kpiMeta) return null;
94
+
95
+ const value = kpiData?.[kpiKey] || 0;
96
+ return (
97
+ <ErrorBoundary key={kpiKey}>
98
+ <div className="animate-fade-in" style={{ animationDelay: `${idx * 0.1}s` }}>
99
+ <SummaryCard
100
+ label={kpiMeta.label}
101
+ value={value}
102
+ color={kpiMeta.color}
103
+ icon={kpiMeta.icon}
104
+ trend={kpiData?.[`${kpiKey}Trend`]}
105
+ onClick={() => handleKpiClick(kpiMeta)}
106
+ />
107
+ </div>
108
+ </ErrorBoundary>
109
+ );
110
+ })}
111
+ </div>
112
+ )}
113
+ </section>
114
+
115
+ {/* Main Analytics Grid */}
116
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap: "24px" }}>
117
+
118
+
119
+
120
+ {/* Workflow Distribution */}
121
+ <div style={{ gridColumn: "span 12", background: "#FFF", padding: "24px", borderRadius: "16px", boxShadow: "0 1px 3px rgba(0,0,0,0.1)" }}>
122
+ {isLoading ? (
123
+ <SkeletonLoader type="chart" />
124
+ ) : (
125
+ <ErrorBoundary>
126
+ <TaskStatusChart
127
+ title="EKYC_APPLICATION_STATUS"
128
+ data={workflowData?.stageBreakdown || []}
129
+ />
130
+ </ErrorBoundary>
131
+ )}
132
+ </div>
133
+
134
+ {/* SLA & Timeline Bottlenecks */}
135
+ <div style={{ gridColumn: "span 4" }}>
136
+ {isLoading ? (
137
+ <SkeletonLoader type="chart" />
138
+ ) : (
139
+ <ErrorBoundary>
140
+ <SLAWidget
141
+ slaPercentage={workflowData?.slaCompliance || 0}
142
+ avgTime={workflowData?.avgProcessingTimeHours || 0}
143
+ breachedCount={workflowData?.breachCount || 0}
144
+ />
145
+ </ErrorBoundary>
146
+ )}
147
+ </div>
148
+
149
+ <div style={{ gridColumn: "span 8" }}>
150
+ {isLoading ? (
151
+ <SkeletonLoader type="chart" />
152
+ ) : (
153
+ <ErrorBoundary>
154
+ <WorkflowTimeline stages={workflowData?.stageBreakdown || []} />
155
+ </ErrorBoundary>
156
+ )}
157
+ </div>
158
+
159
+ {/* Spatial Cluster Analysis */}
160
+ <div style={{ gridColumn: "span 12", background: "#FFF", padding: "24px", borderRadius: "16px", boxShadow: "0 1px 3px rgba(0,0,0,0.1)" }}>
161
+ {isLoading ? (
162
+ <SkeletonLoader type="chart" />
163
+ ) : (
164
+ <ErrorBoundary>
165
+ <ClusterHeatmap
166
+ title="EKYC_CLUSTER_WORKLOAD_HEATMAP"
167
+ data={clusterData || []}
168
+ onDrillDown={(cluster) => handleFilterChange("clusterId", cluster.clusterId)}
169
+ />
170
+ </ErrorBoundary>
171
+ )}
172
+ </div>
173
+
174
+ {/* Agency Performance Table */}
175
+ <div style={{ gridColumn: "span 12" }}>
176
+ {isLoading ? (
177
+ <SkeletonLoader type="table" />
178
+ ) : (
179
+ <ErrorBoundary>
180
+ <AnalyticsTable
181
+ title="EKYC_AGENCY_PERFORMANCE_METRICS"
182
+ filename="agency_performance_report.csv"
183
+ data={agencyData || []}
184
+ columns={[
185
+ { id: "agencyName", label: "EKYC_AGENCY_NAME" },
186
+ { id: "totalAssigned", label: "EKYC_TOTAL_ASSIGNED" },
187
+ { id: "totalCompleted", label: "EKYC_TOTAL_COMPLETED" },
188
+ { id: "pendingCount", label: "EKYC_PENDING" },
189
+ { id: "slaCompliance", label: "EKYC_SLA_COMPLIANCE", isPercentage: true }
190
+ ]}
191
+ />
192
+ </ErrorBoundary>
193
+ )}
194
+ </div>
195
+
196
+ </div>
197
+ </DashboardLayout>
198
+ );
199
+ };
200
+
201
+ export default CeoDashboard;
@@ -98,7 +98,7 @@ const DesktopInbox = ({ tableConfig, filterComponent, ...props }) => {
98
98
  );
99
99
 
100
100
  const tableData = useMemo(() => {
101
- return data?.items || [];
101
+ return data?.items || (Array.isArray(data) ? data : []);
102
102
  }, [data]);
103
103
 
104
104
  return (
@@ -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;