From 6cc4ee090175e545cf86a9fab86df0f8afff362e Mon Sep 17 00:00:00 2001 From: Esteban Date: Mon, 2 Feb 2026 17:54:01 -0600 Subject: [PATCH] meter types --- src/api/meterTypes.ts | 77 +++++++++++++++++++++++++++++ src/api/projects.ts | 4 ++ src/pages/projects/ProjectsPage.tsx | 50 +++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 src/api/meterTypes.ts diff --git a/src/api/meterTypes.ts b/src/api/meterTypes.ts new file mode 100644 index 0000000..bb416a3 --- /dev/null +++ b/src/api/meterTypes.ts @@ -0,0 +1,77 @@ +/** + * Meter Types API + * Handles all meter type-related API operations + */ + +import { apiClient } from './client'; + +/** + * Meter Type entity + */ +export interface MeterType { + id: string; + name: string; + code: string; + description: string | null; + isActive: boolean; + createdAt: string; + updatedAt: string; +} + +/** + * Fetch all active meter types + * @returns Promise resolving to an array of meter types + */ +export async function fetchMeterTypes(): Promise { + // apiClient automatically unwraps the response and returns only the data array + const data = await apiClient.get('/api/meter-types'); + + // Transform snake_case to camelCase + return data.map((item: any) => ({ + id: item.id, + name: item.name, + code: item.code, + description: item.description, + isActive: item.is_active, + createdAt: item.created_at, + updatedAt: item.updated_at, + })); +} + +/** + * Fetch a meter type by ID + * @param id - Meter type ID + * @returns Promise resolving to a meter type + */ +export async function fetchMeterTypeById(id: string): Promise { + const item = await apiClient.get(`/api/meter-types/${id}`); + + return { + id: item.id, + name: item.name, + code: item.code, + description: item.description, + isActive: item.is_active, + createdAt: item.created_at, + updatedAt: item.updated_at, + }; +} + +/** + * Fetch a meter type by code + * @param code - Meter type code (LORA, LORAWAN, GRANDES) + * @returns Promise resolving to a meter type + */ +export async function fetchMeterTypeByCode(code: string): Promise { + const item = await apiClient.get(`/api/meter-types/code/${code}`); + + return { + id: item.id, + name: item.name, + code: item.code, + description: item.description, + isActive: item.is_active, + createdAt: item.created_at, + updatedAt: item.updated_at, + }; +} diff --git a/src/api/projects.ts b/src/api/projects.ts index 49bb5d8..9b0b83b 100644 --- a/src/api/projects.ts +++ b/src/api/projects.ts @@ -40,6 +40,7 @@ export interface Project { areaName: string; location: string | null; status: string; + meterTypeId: string | null; createdBy: string; createdAt: string; updatedAt: string; @@ -54,6 +55,7 @@ export interface ProjectInput { areaName: string; location?: string; status?: string; + meterTypeId?: string | null; } /** @@ -94,6 +96,7 @@ export async function createProject(data: ProjectInput): Promise { area_name: data.areaName, location: data.location, status: data.status, + meter_type_id: data.meterTypeId, }; const response = await apiClient.post>('/api/projects', backendData); return transformKeys(response); @@ -112,6 +115,7 @@ export async function updateProject(id: string, data: Partial): Pr if (data.areaName !== undefined) backendData.area_name = data.areaName; if (data.location !== undefined) backendData.location = data.location; if (data.status !== undefined) backendData.status = data.status; + if (data.meterTypeId !== undefined) backendData.meter_type_id = data.meterTypeId; const response = await apiClient.patch>(`/api/projects/${id}`, backendData); return transformKeys(response); diff --git a/src/pages/projects/ProjectsPage.tsx b/src/pages/projects/ProjectsPage.tsx index b7a037d..44f4abc 100644 --- a/src/pages/projects/ProjectsPage.tsx +++ b/src/pages/projects/ProjectsPage.tsx @@ -9,6 +9,7 @@ import { updateProject as apiUpdateProject, deleteProject as apiDeleteProject, } from "../../api/projects"; +import { fetchMeterTypes, type MeterType } from "../../api/meterTypes"; export default function ProjectsPage() { const [projects, setProjects] = useState([]); @@ -18,6 +19,8 @@ export default function ProjectsPage() { const [showModal, setShowModal] = useState(false); const [editingId, setEditingId] = useState(null); + + const [meterTypes, setMeterTypes] = useState([]); const emptyForm: ProjectInput = { name: "", @@ -25,6 +28,7 @@ export default function ProjectsPage() { areaName: "", location: "", status: "ACTIVE", + meterTypeId: null, }; const [form, setForm] = useState(emptyForm); @@ -42,8 +46,19 @@ export default function ProjectsPage() { } }; + const loadMeterTypesData = async () => { + try { + const types = await fetchMeterTypes(); + setMeterTypes(types); + } catch (error) { + console.error("Error loading meter types:", error); + setMeterTypes([]); + } + }; + useEffect(() => { loadProjects(); + loadMeterTypesData(); }, []); const handleSave = async () => { @@ -104,6 +119,7 @@ export default function ProjectsPage() { areaName: activeProject.areaName, location: activeProject.location ?? "", status: activeProject.status, + meterTypeId: activeProject.meterTypeId ?? null, }); setShowModal(true); }; @@ -183,6 +199,19 @@ export default function ProjectsPage() { columns={[ { title: "Nombre", field: "name" }, { title: "Area", field: "areaName" }, + { + title: "Tipo de Toma", + field: "meterTypeId", + render: (rowData: Project) => { + if (!rowData.meterTypeId) return "-"; + const meterType = meterTypes.find(mt => mt.id === rowData.meterTypeId); + return meterType ? ( + + {meterType.name} + + ) : "-"; + } + }, { title: "Descripción", field: "description", render: (rowData: Project) => rowData.description || "-" }, { title: "Ubicación", field: "location", render: (rowData: Project) => rowData.location || "-" }, { @@ -278,6 +307,27 @@ export default function ProjectsPage() { /> +
+ + + {meterTypes.length === 0 && ( +

+ No hay tipos de toma disponibles. Asegúrate de aplicar la migración SQL. +

+ )} +
+