From 5a82a717d8eaff52731b387e84854f55eb911119 Mon Sep 17 00:00:00 2001 From: Esteban Date: Mon, 22 Dec 2025 01:16:15 -0600 Subject: [PATCH] Meters project selection logic fix --- src/api/meters.ts | 82 ++-- src/pages/concentrators/ConcentratorsPage.tsx | 2 +- src/pages/meters/MeterPage.tsx | 425 ++++++------------ 3 files changed, 186 insertions(+), 323 deletions(-) diff --git a/src/api/meters.ts b/src/api/meters.ts index 29b3468..b5f880b 100644 --- a/src/api/meters.ts +++ b/src/api/meters.ts @@ -64,10 +64,11 @@ export interface Meter { } export const fetchMeters = async (): Promise => { + const pageSize = 9999; try { - const response = await fetch(METERS_API_URL, { + const response = await fetch(`${METERS_API_URL}?pageSize=${pageSize}`, { method: "GET", - headers: getAuthHeaders(), + headers: getAuthHeaders() }); if (!response.ok) { @@ -97,7 +98,6 @@ export const fetchMeters = async (): Promise => { usageAnalysisType: r.fields["Usage Analysis Type"] || "", installedTime: r.fields["Installed Time"] || "", })); - console.log ("ans", ans); return ans; } catch (error) { @@ -139,7 +139,9 @@ export const createMeter = async ( }); if (!response.ok) { - throw new Error(`Failed to create meter: ${response.status} ${response.statusText}`); + throw new Error( + `Failed to create meter: ${response.status} ${response.statusText}` + ); } const data = await response.json(); @@ -154,22 +156,32 @@ export const createMeter = async ( createdAt: createdRecord.fields.CreatedAt || meterData.createdAt, updatedAt: createdRecord.fields.UpdatedAt || meterData.updatedAt, areaName: createdRecord.fields["Area Name"] || meterData.areaName, - accountNumber: createdRecord.fields["Account Number"] || meterData.accountNumber, + accountNumber: + createdRecord.fields["Account Number"] || meterData.accountNumber, userName: createdRecord.fields["User Name"] || meterData.userName, - userAddress: createdRecord.fields["User Address"] || meterData.userAddress, - meterSerialNumber: createdRecord.fields["Meter S/N"] || meterData.meterSerialNumber, + userAddress: + createdRecord.fields["User Address"] || meterData.userAddress, + meterSerialNumber: + createdRecord.fields["Meter S/N"] || meterData.meterSerialNumber, meterName: createdRecord.fields["Meter Name"] || meterData.meterName, - meterStatus: createdRecord.fields["Meter Status"] || meterData.meterStatus, - protocolType: createdRecord.fields["Protocol Type"] || meterData.protocolType, + meterStatus: + createdRecord.fields["Meter Status"] || meterData.meterStatus, + protocolType: + createdRecord.fields["Protocol Type"] || meterData.protocolType, priceNo: createdRecord.fields["Price No."] || meterData.priceNo, priceName: createdRecord.fields["Price Name"] || meterData.priceName, - dmaPartition: createdRecord.fields["DMA Partition"] || meterData.dmaPartition, - supplyTypes: createdRecord.fields["Supply Types"] || meterData.supplyTypes, + dmaPartition: + createdRecord.fields["DMA Partition"] || meterData.dmaPartition, + supplyTypes: + createdRecord.fields["Supply Types"] || meterData.supplyTypes, deviceId: createdRecord.fields["Device ID"] || meterData.deviceId, deviceName: createdRecord.fields["Device Name"] || meterData.deviceName, deviceType: createdRecord.fields["Device Type"] || meterData.deviceType, - usageAnalysisType: createdRecord.fields["Usage Analysis Type"] || meterData.usageAnalysisType, - installedTime: createdRecord.fields["Installed Time"] || meterData.installedTime, + usageAnalysisType: + createdRecord.fields["Usage Analysis Type"] || + meterData.usageAnalysisType, + installedTime: + createdRecord.fields["Installed Time"] || meterData.installedTime, }; } catch (error) { console.error("Error creating meter:", error); @@ -214,9 +226,13 @@ export const updateMeter = async ( if (!response.ok) { if (response.status === 400) { const errorData = await response.json(); - throw new Error(`Bad Request: ${errorData.msg || "Invalid data provided"}`); + throw new Error( + `Bad Request: ${errorData.msg || "Invalid data provided"}` + ); } - throw new Error(`Failed to update meter: ${response.status} ${response.statusText}`); + throw new Error( + `Failed to update meter: ${response.status} ${response.statusText}` + ); } const data = await response.json(); @@ -231,22 +247,32 @@ export const updateMeter = async ( createdAt: updatedRecord.fields.CreatedAt || meterData.createdAt, updatedAt: updatedRecord.fields.UpdatedAt || meterData.updatedAt, areaName: updatedRecord.fields["Area Name"] || meterData.areaName, - accountNumber: updatedRecord.fields["Account Number"] || meterData.accountNumber, + accountNumber: + updatedRecord.fields["Account Number"] || meterData.accountNumber, userName: updatedRecord.fields["User Name"] || meterData.userName, - userAddress: updatedRecord.fields["User Address"] || meterData.userAddress, - meterSerialNumber: updatedRecord.fields["Meter S/N"] || meterData.meterSerialNumber, + userAddress: + updatedRecord.fields["User Address"] || meterData.userAddress, + meterSerialNumber: + updatedRecord.fields["Meter S/N"] || meterData.meterSerialNumber, meterName: updatedRecord.fields["Meter Name"] || meterData.meterName, - meterStatus: updatedRecord.fields["Meter Status"] || meterData.meterStatus, - protocolType: updatedRecord.fields["Protocol Type"] || meterData.protocolType, + meterStatus: + updatedRecord.fields["Meter Status"] || meterData.meterStatus, + protocolType: + updatedRecord.fields["Protocol Type"] || meterData.protocolType, priceNo: updatedRecord.fields["Price No."] || meterData.priceNo, priceName: updatedRecord.fields["Price Name"] || meterData.priceName, - dmaPartition: updatedRecord.fields["DMA Partition"] || meterData.dmaPartition, - supplyTypes: updatedRecord.fields["Supply Types"] || meterData.supplyTypes, + dmaPartition: + updatedRecord.fields["DMA Partition"] || meterData.dmaPartition, + supplyTypes: + updatedRecord.fields["Supply Types"] || meterData.supplyTypes, deviceId: updatedRecord.fields["Device ID"] || meterData.deviceId, deviceName: updatedRecord.fields["Device Name"] || meterData.deviceName, deviceType: updatedRecord.fields["Device Type"] || meterData.deviceType, - usageAnalysisType: updatedRecord.fields["Usage Analysis Type"] || meterData.usageAnalysisType, - installedTime: updatedRecord.fields["Installed Time"] || meterData.installedTime, + usageAnalysisType: + updatedRecord.fields["Usage Analysis Type"] || + meterData.usageAnalysisType, + installedTime: + updatedRecord.fields["Installed Time"] || meterData.installedTime, }; } catch (error) { console.error("Error updating meter:", error); @@ -267,9 +293,13 @@ export const deleteMeter = async (id: string): Promise => { if (!response.ok) { if (response.status === 400) { const errorData = await response.json(); - throw new Error(`Bad Request: ${errorData.msg || "Invalid data provided"}`); + throw new Error( + `Bad Request: ${errorData.msg || "Invalid data provided"}` + ); } - throw new Error(`Failed to delete meter: ${response.status} ${response.statusText}`); + throw new Error( + `Failed to delete meter: ${response.status} ${response.statusText}` + ); } } catch (error) { console.error("Error deleting meter:", error); diff --git a/src/pages/concentrators/ConcentratorsPage.tsx b/src/pages/concentrators/ConcentratorsPage.tsx index af0284c..5b00b18 100644 --- a/src/pages/concentrators/ConcentratorsPage.tsx +++ b/src/pages/concentrators/ConcentratorsPage.tsx @@ -62,7 +62,7 @@ export default function ConcentratorsPage() { setLoadingConcentrators(true); try { const data = await fetchConcentrators(); - const projectsArray = data.map((record) => record["Area Name"]); + const projectsArray = [...new Set(data.map((record) => record["Area Name"]))]; setAllProjects(projectsArray); setConcentrators(data); } catch (error) { diff --git a/src/pages/meters/MeterPage.tsx b/src/pages/meters/MeterPage.tsx index b380e67..1454bad 100644 --- a/src/pages/meters/MeterPage.tsx +++ b/src/pages/meters/MeterPage.tsx @@ -1,7 +1,6 @@ -import { useState, useEffect, useMemo } from "react"; +import { useState, useEffect } from "react"; import { Plus, Trash2, Pencil, RefreshCcw } from "lucide-react"; import MaterialTable from "@material-table/core"; -import { fetchProjectNames } from "../../api/projects"; import { fetchMeters, createMeter, @@ -10,14 +9,6 @@ import { type Meter, } from "../../api/meters"; -/* ================= TYPES ================= */ - -interface User { - name: string; - role: "SUPER_ADMIN" | "USER"; - project?: string; // asignado si no es superadmin -} - interface DeviceData { "Device ID": number; "Device EUI": string; @@ -28,51 +19,14 @@ interface DeviceData { /* ================= COMPONENT ================= */ export default function MeterManagement() { - // Simulación de usuario actual - const currentUser: User = { - name: "Admin GRH", - role: "SUPER_ADMIN", // cambiar a USER para probar otro caso - project: "CESPT", - }; - const [allProjects, setAllProjects] = useState([]); const [loadingProjects, setLoadingProjects] = useState(true); - // Proyectos visibles según el usuario - const visibleProjects = useMemo(() => - currentUser.role === "SUPER_ADMIN" - ? allProjects - : currentUser.project - ? [currentUser.project] - : [], - [allProjects, currentUser.role, currentUser.project] - ); const [selectedProject, setSelectedProject] = useState(""); - useEffect(() => { - const loadProjects = async () => { - try { - const projects = await fetchProjectNames(); - setAllProjects(projects); - } catch (error) { - console.error('Error loading projects:', error); - setAllProjects([]); - } finally { - setLoadingProjects(false); - } - }; - - loadProjects(); - }, []); - - useEffect(() => { - if (visibleProjects.length > 0 && !selectedProject) { - setSelectedProject(visibleProjects[0]); - } - }, [visibleProjects, selectedProject]); - const [meters, setMeters] = useState([]); + const [filteredMeters, setFilteredMeters] = useState([]); const [loadingMeters, setLoadingMeters] = useState(true); const [activeMeter, setActiveMeter] = useState(null); const [search, setSearch] = useState(""); @@ -109,6 +63,15 @@ export default function MeterManagement() { "AppKey": "", }; + useEffect(() => { + if (selectedProject) { + const filtered = meters.filter((meter) => meter.areaName === selectedProject); + setFilteredMeters(filtered); + } else { + setFilteredMeters(meters); + } + }, [selectedProject, meters]); + const [form, setForm] = useState>(emptyMeter); const [deviceForm, setDeviceForm] = useState(emptyDeviceData); const [errors, setErrors] = useState<{ [key: string]: boolean }>({}); @@ -117,12 +80,16 @@ export default function MeterManagement() { setLoadingMeters(true); try { const data = await fetchMeters(); + const projectsArray = [...new Set(data.map((record) => record["areaName"]))]; + setAllProjects(projectsArray); setMeters(data); } catch (error) { console.error("Error loading meters:", error); + setAllProjects([]); setMeters([]); } finally { setLoadingMeters(false); + setLoadingProjects(false); } }; @@ -144,16 +111,14 @@ export default function MeterManagement() { const validateForm = (): boolean => { const newErrors: { [key: string]: boolean } = {}; + // Required fields if (!form.meterName.trim()) newErrors["meterName"] = true; if (!form.meterSerialNumber.trim()) newErrors["meterSerialNumber"] = true; if (!form.areaName.trim()) newErrors["areaName"] = true; if (!form.deviceName.trim()) newErrors["deviceName"] = true; - if (!form.deviceType.trim()) newErrors["deviceType"] = true; if (!form.protocolType.trim()) newErrors["protocolType"] = true; - if (!form.supplyTypes.trim()) newErrors["supplyTypes"] = true; - if (!form.usageAnalysisType.trim()) newErrors["usageAnalysisType"] = true; - if (!form.installedTime) newErrors["installedTime"] = true; + // Device Configuration - Required if (!deviceForm["Device ID"] || deviceForm["Device ID"] === 0) { newErrors["Device ID"] = true; } @@ -248,15 +213,6 @@ export default function MeterManagement() { setActiveMeter(null); }; - /* ================= FILTER ================= */ - const filtered = meters.filter( - (m) => - (m.meterName.toLowerCase().includes(search.toLowerCase()) || - m.meterSerialNumber.toLowerCase().includes(search.toLowerCase()) || - m.deviceId.toLowerCase().includes(search.toLowerCase()) || - m.areaName.toLowerCase().includes(search.toLowerCase())) - ); - /* ================= UI ================= */ return (
@@ -270,22 +226,25 @@ export default function MeterManagement() { value={selectedProject} onChange={(e) => setSelectedProject(e.target.value)} className="w-full border px-3 py-2 rounded" - disabled={loadingProjects || visibleProjects.length === 0} + disabled={loadingProjects || allProjects.length === 0} > {loadingProjects ? ( - ) : visibleProjects.length === 0 ? ( + ) : meters.length === 0 ? ( ) : ( - visibleProjects.map((proj) => ( - - )) + <> + + {allProjects.map((proj) => ( + + ))} + )} - {visibleProjects.length === 0 && !loadingProjects && ( + {allProjects.length === 0 && !loadingProjects && (

No projects available. Please contact your administrator.

@@ -316,7 +275,7 @@ export default function MeterManagement() { setEditingId(null); setShowModal(true); }} - disabled={!selectedProject || visibleProjects.length === 0} + disabled={!selectedProject || allProjects.length === 0} className="flex items-center gap-2 px-4 py-2 bg-white text-[#4c5f9e] rounded-lg disabled:opacity-50 disabled:cursor-not-allowed" > Add @@ -387,32 +346,17 @@ export default function MeterManagement() { title="Meters" isLoading={loadingMeters} columns={[ - { title: "Meter Name", field: "meterName" }, - { title: "Serial Number", field: "meterSerialNumber" }, - { title: "Area", field: "areaName" }, - { title: "Device ID", field: "deviceId" }, - { title: "Device Name", field: "deviceName" }, - { - title: "Status", - field: "meterStatus", - render: (rowData) => ( - - {rowData.meterStatus} - - ), - }, - { title: "Protocol", field: "protocolType" }, - { title: "Device Type", field: "deviceType" }, - { title: "Created At", field: "createdAt", type: "datetime" }, - { title: "Updated At", field: "updatedAt", type: "datetime" }, + { title: "Area Name", field: "areaName", render: (rowData) => rowData.areaName || "-" }, + { title: "Account Number", field: "accountNumber", render: (rowData) => rowData.accountNumber || "-" }, + { title: "User Name", field: "userName", render: (rowData) => rowData.userName || "-" }, + { title: "User Address", field: "userAddress", render: (rowData) => rowData.userAddress || "-" }, + { title: "Meter S/N", field: "meterSerialNumber", render: (rowData) => rowData.meterSerialNumber || "-" }, + { title: "Meter Name", field: "meterName", render: (rowData) => rowData.meterName || "-" }, + { title: "Protocol Type", field: "protocolType", render: (rowData) => rowData.protocolType || "-" }, + { title: "Device ID", field: "deviceId", render: (rowData) => rowData.deviceId || "-" }, + { title: "Device Name", field: "deviceName", render: (rowData) => rowData.deviceName || "-" }, ]} - data={filtered} + data={filteredMeters} onRowClick={(_, rowData) => setActiveMeter(rowData as Meter)} options={{ actionsColumnIndex: -1, @@ -450,6 +394,83 @@ export default function MeterManagement() {
+
+ { + setForm({ ...form, areaName: e.target.value }); + if (errors["areaName"]) { + setErrors({ ...errors, "areaName": false }); + } + }} + required + /> + {errors["areaName"] && ( +

This field is required

+ )} +
+ +
+ + setForm({ ...form, accountNumber: e.target.value || null }) + } + /> +
+
+ +
+
+ + setForm({ ...form, userName: e.target.value || null }) + } + /> +
+ +
+ + setForm({ ...form, userAddress: e.target.value || null }) + } + /> +
+
+ +
+
+ { + setForm({ ...form, meterSerialNumber: e.target.value }); + if (errors["meterSerialNumber"]) { + setErrors({ ...errors, "meterSerialNumber": false }); + } + }} + required + /> + {errors["meterSerialNumber"] && ( +

This field is required

+ )} +
+
This field is required

)}
- -
- { - setForm({ ...form, meterSerialNumber: e.target.value }); - if (errors["meterSerialNumber"]) { - setErrors({ ...errors, "meterSerialNumber": false }); - } - }} - required - /> - {errors["meterSerialNumber"] && ( -

This field is required

- )} -
-
- -
- { - setForm({ ...form, areaName: e.target.value }); - if (errors["areaName"]) { - setErrors({ ...errors, "areaName": false }); - } - }} - required - /> - {errors["areaName"] && ( -

This field is required

- )} -
- -
-
- { - setForm({ ...form, deviceName: e.target.value }); - if (errors["deviceName"]) { - setErrors({ ...errors, "deviceName": false }); - } - }} - required - /> - {errors["deviceName"] && ( -

This field is required

- )} -
- -
- { - setForm({ ...form, deviceType: e.target.value }); - if (errors["deviceType"]) { - setErrors({ ...errors, "deviceType": false }); - } - }} - required - /> - {errors["deviceType"] && ( -

This field is required

- )} -
-
- -
-
@@ -588,129 +515,35 @@ export default function MeterManagement() {
{ - setForm({ ...form, supplyTypes: e.target.value }); - if (errors["supplyTypes"]) { - setErrors({ ...errors, "supplyTypes": false }); - } - }} - required + className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent" + placeholder="Device ID (optional)" + value={form.deviceId ?? ""} + onChange={(e) => + setForm({ ...form, deviceId: e.target.value || "" }) + } /> - {errors["supplyTypes"] && ( -

This field is required

- )}
{ - setForm({ ...form, usageAnalysisType: e.target.value }); - if (errors["usageAnalysisType"]) { - setErrors({ ...errors, "usageAnalysisType": false }); + setForm({ ...form, deviceName: e.target.value }); + if (errors["deviceName"]) { + setErrors({ ...errors, "deviceName": false }); } }} required /> - {errors["usageAnalysisType"] && ( + {errors["deviceName"] && (

This field is required

)}
- -
- { - setForm({ ...form, installedTime: new Date(e.target.value).toISOString() }); - if (errors["installedTime"]) { - setErrors({ ...errors, "installedTime": false }); - } - }} - required - /> - {errors["installedTime"] && ( -

This field is required

- )} -
- -
-

Optional Fields

- -
- - setForm({ ...form, accountNumber: e.target.value || null }) - } - /> - - - setForm({ ...form, userName: e.target.value || null }) - } - /> -
- - - setForm({ ...form, userAddress: e.target.value || null }) - } - /> - -
- setForm({ ...form, priceNo: e.target.value || null })} - /> - - - setForm({ ...form, priceName: e.target.value || null }) - } - /> - - - setForm({ ...form, dmaPartition: e.target.value || null }) - } - /> -
-