diff --git a/src/pages/concentrators/ConcentratorsSidebar.tsx b/src/pages/concentrators/ConcentratorsSidebar.tsx
index e956e2d..aadd7b7 100644
--- a/src/pages/concentrators/ConcentratorsSidebar.tsx
+++ b/src/pages/concentrators/ConcentratorsSidebar.tsx
@@ -65,8 +65,6 @@ export default function ConcentratorsSidebar({
Proyectos
- Tipo: {sampleViewLabel}
- {" • "}
Seleccionado:{" "}
{projects.find((p) => p.id === selectedProject)?.name || "—"}
@@ -142,10 +140,8 @@ export default function ConcentratorsSidebar({
) : projects.length === 0 ? (
{selectedMeterTypeId
- ? `No hay proyectos con el tipo de toma seleccionado y concentradores ${sampleViewLabel}.`
- : sampleView === "GENERAL"
- ? "No hay proyectos con concentradores disponibles."
- : `No hay proyectos con concentradores ${sampleViewLabel}.`
+ ? `No hay proyectos con el tipo de toma seleccionado.`
+ : "No hay proyectos disponibles."
}
) : (
diff --git a/src/pages/concentrators/useConcentrators.ts b/src/pages/concentrators/useConcentrators.ts
index cdf30fd..cd18897 100644
--- a/src/pages/concentrators/useConcentrators.ts
+++ b/src/pages/concentrators/useConcentrators.ts
@@ -183,11 +183,6 @@ export function useConcentrators() {
let filteredProjects = visibleProjects;
- filteredProjects = filteredProjects.filter((projectId) => {
- const count = counts[projectId] ?? 0;
- return count > 0;
- });
-
if (selectedMeterTypeId) {
filteredProjects = filteredProjects.filter((projectId) => {
const project = projects.find((p) => p.id === projectId);
diff --git a/src/pages/consumption/ConsumptionPage.tsx b/src/pages/consumption/ConsumptionPage.tsx
index cf32148..a0ce67d 100644
--- a/src/pages/consumption/ConsumptionPage.tsx
+++ b/src/pages/consumption/ConsumptionPage.tsx
@@ -22,9 +22,14 @@ import {
type Pagination,
} from "../../api/readings";
import { fetchProjects, type Project } from "../../api/projects";
+import { getCurrentUserRole, getCurrentUserProjectId } from "../../api/auth";
import ReadingsBulkUploadModal from "./ReadingsBulkUploadModal";
export default function ConsumptionPage() {
+ const userRole = useMemo(() => getCurrentUserRole(), []);
+ const userProjectId = useMemo(() => getCurrentUserProjectId(), []);
+ const isOperator = userRole?.toUpperCase() === 'OPERATOR';
+
const [readings, setReadings] = useState([]);
const [summary, setSummary] = useState(null);
const [projects, setProjects] = useState([]);
@@ -49,12 +54,23 @@ export default function ConsumptionPage() {
const loadProjects = async () => {
try {
const data = await fetchProjects();
- setProjects(data);
+
+ let visibleProjects = data;
+ if (isOperator && userProjectId) {
+ visibleProjects = data.filter(p => p.id === userProjectId);
+
+ if (visibleProjects.length > 0) {
+ setSelectedProject(visibleProjects[0].id);
+ }
+ }
+
+ setProjects(visibleProjects);
} catch (error) {
console.error("Error loading projects:", error);
}
};
loadProjects();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const loadData = async (page = 1) => {
@@ -167,7 +183,9 @@ export default function ConsumptionPage() {
};
const clearFilters = () => {
- setSelectedProject("");
+ if (!isOperator) {
+ setSelectedProject("");
+ }
setStartDate("");
setEndDate("");
setSearch("");
@@ -342,8 +360,9 @@ export default function ConsumptionPage() {
value={selectedProject}
onChange={(e) => setSelectedProject(e.target.value)}
className="px-3 py-1.5 text-sm bg-white border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20"
+ disabled={isOperator}
>
- Todos
+ {!isOperator && Todos }
{projects.map((p) => (
{p.name}
diff --git a/src/pages/meters/MeterPage.tsx b/src/pages/meters/MeterPage.tsx
index 358cf86..7bd1595 100644
--- a/src/pages/meters/MeterPage.tsx
+++ b/src/pages/meters/MeterPage.tsx
@@ -66,23 +66,22 @@ export default function MetersPage({
const [form, setForm] = useState(emptyForm);
const [errors, setErrors] = useState>({});
- // Projects cards (from real data)
const projectsDataReal: ProjectCard[] = useMemo(() => {
const baseRegion = "Baja California";
const baseContact = "Operaciones";
const baseLastSync = "Hace 1 h";
- return m.allProjects.map((name) => ({
- name,
+ return m.filteredProjects.map((project) => ({
+ name: project.name,
region: baseRegion,
projects: 1,
- meters: m.projectsCounts[name] ?? 0,
+ meters: m.projectsCounts[project.name] ?? 0,
activeAlerts: 0,
lastSync: baseLastSync,
contact: baseContact,
status: "ACTIVO" as ProjectStatus,
}));
- }, [m.allProjects, m.projectsCounts]);
+ }, [m.filteredProjects, m.projectsCounts]);
const sidebarProjects = projectsDataReal;
@@ -217,8 +216,17 @@ export default function MetersPage({
projects={sidebarProjects}
onRefresh={handleRefresh}
refreshDisabled={false}
- allProjects={m.allProjects}
+ allProjects={m.allProjects.map(p => p.name)}
onResetSelection={resetSelection}
+ meterTypes={m.meterTypes}
+ selectedMeterTypeId={m.selectedMeterTypeId}
+ onSelectMeterTypeId={(id: string) => {
+ m.setSelectedMeterTypeId(id);
+ m.setSelectedProject("");
+ setActiveMeter(null);
+ setSearch("");
+ }}
+ loadingMeterTypes={m.loadingMeterTypes}
/>
{/* MAIN */}
diff --git a/src/pages/meters/MetersSidebar.tsx b/src/pages/meters/MetersSidebar.tsx
index 62ba916..8d0b363 100644
--- a/src/pages/meters/MetersSidebar.tsx
+++ b/src/pages/meters/MetersSidebar.tsx
@@ -1,8 +1,9 @@
// src/pages/meters/MetersSidebar.tsx
import { useEffect, useMemo, useRef, useState } from "react";
-import { ChevronDown, RefreshCcw, Check } from "lucide-react";
+import { RefreshCcw, Check } from "lucide-react";
import type React from "react";
import type { ProjectCard, TakeType } from "./MeterPage";
+import type { MeterType } from "../../api/meterTypes";
type Props = {
loadingProjects: boolean;
@@ -21,6 +22,11 @@ type Props = {
allProjects: string[];
onResetSelection?: () => void;
+
+ meterTypes: MeterType[];
+ selectedMeterTypeId: string;
+ onSelectMeterTypeId: (id: string) => void;
+ loadingMeterTypes: boolean;
};
type TakeTypeOption = { key: TakeType; label: string };
@@ -44,6 +50,10 @@ export default function MetersSidebar({
refreshDisabled,
allProjects,
onResetSelection,
+ meterTypes,
+ selectedMeterTypeId,
+ onSelectMeterTypeId,
+ loadingMeterTypes,
}: Props) {
const [typesMenuOpen, setTypesMenuOpen] = useState(false);
@@ -74,8 +84,6 @@ export default function MetersSidebar({
Proyectos
- Tipo: {takeTypeLabel}
- {" • "}
Seleccionado:{" "}
{selectedProject || "—"}
@@ -96,24 +104,6 @@ export default function MetersSidebar({
{/* ✅ Tipos de tomas (dropdown) — mismo UI que Concentrators */}
-
setTypesMenuOpen((v) => !v)}
- disabled={loadingProjects}
- className="w-full inline-flex items-center justify-between rounded-xl border border-gray-200 bg-white px-3 py-2 text-sm font-semibold text-gray-700 shadow-sm hover:bg-gray-50 disabled:opacity-60"
- >
-
- Tipos de tomas
-
- ({takeTypeLabel})
-
-
-
-
-
{typesMenuOpen && (
@@ -155,13 +145,35 @@ export default function MetersSidebar({
)}
+
+
+ Filtrar por Tipo de Toma del Proyecto
+
+ onSelectMeterTypeId(e.target.value)}
+ disabled={loadingMeterTypes}
+ className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 disabled:opacity-60 disabled:cursor-not-allowed"
+ >
+ Todos los tipos de toma
+ {meterTypes.map((type) => (
+
+ {type.name}
+
+ ))}
+
+
+
{/* List */}
{loadingProjects ? (
Cargando proyectos...
) : projects.length === 0 ? (
- No se encontraron proyectos.
+ {selectedMeterTypeId
+ ? "No hay proyectos con el tipo de toma seleccionado."
+ : "No se encontraron proyectos."
+ }
) : (
projects.map((p) => {
diff --git a/src/pages/meters/useMeters.ts b/src/pages/meters/useMeters.ts
index d079801..7840766 100644
--- a/src/pages/meters/useMeters.ts
+++ b/src/pages/meters/useMeters.ts
@@ -1,7 +1,8 @@
import { useEffect, useMemo, useState } from "react";
import { fetchMeters, type Meter } from "../../api/meters";
-import { fetchProjects } from "../../api/projects";
+import { fetchProjects, type Project } from "../../api/projects";
import { getCurrentUserRole, getCurrentUserProjectId } from "../../api/auth";
+import { fetchMeterTypes, type MeterType } from "../../api/meterTypes";
type UseMetersArgs = {
initialProject?: string;
@@ -11,7 +12,8 @@ export function useMeters({ initialProject }: UseMetersArgs) {
const userRole = getCurrentUserRole();
const userProjectId = getCurrentUserProjectId();
const isAdmin = userRole?.toUpperCase() === 'ADMIN';
- const [allProjects, setAllProjects] = useState
([]);
+
+ const [allProjects, setAllProjects] = useState([]);
const [loadingProjects, setLoadingProjects] = useState(true);
const [selectedProject, setSelectedProject] = useState(initialProject || "");
@@ -20,12 +22,21 @@ export function useMeters({ initialProject }: UseMetersArgs) {
const [filteredMeters, setFilteredMeters] = useState([]);
const [loadingMeters, setLoadingMeters] = useState(true);
+ const [meterTypes, setMeterTypes] = useState([]);
+ const [selectedMeterTypeId, setSelectedMeterTypeId] = useState("");
+ const [loadingMeterTypes, setLoadingMeterTypes] = useState(true);
+
const loadProjects = async () => {
setLoadingProjects(true);
try {
const projects = await fetchProjects();
- const projectNames = projects.map((p) => p.name);
- setAllProjects(projectNames);
+
+ let visibleProjects = projects;
+ if (!isAdmin && userProjectId) {
+ visibleProjects = projects.filter(p => p.id === userProjectId);
+ }
+
+ setAllProjects(visibleProjects);
setSelectedProject((prev) => {
if (prev) return prev;
@@ -36,6 +47,7 @@ export function useMeters({ initialProject }: UseMetersArgs) {
if (userProject) return userProject.name;
}
+ const projectNames = visibleProjects.map((p) => p.name);
return projectNames[0] ?? "";
});
} catch (error) {
@@ -46,6 +58,19 @@ export function useMeters({ initialProject }: UseMetersArgs) {
}
};
+ const loadMeterTypes = async () => {
+ setLoadingMeterTypes(true);
+ try {
+ const types = await fetchMeterTypes();
+ setMeterTypes(types);
+ } catch (error) {
+ console.error("Error loading meter types:", error);
+ setMeterTypes([]);
+ } finally {
+ setLoadingMeterTypes(false);
+ }
+ };
+
const loadMeters = async () => {
setLoadingMeters(true);
@@ -60,10 +85,10 @@ export function useMeters({ initialProject }: UseMetersArgs) {
}
};
- // init - load projects and meters
useEffect(() => {
loadProjects();
loadMeters();
+ loadMeterTypes();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -80,6 +105,13 @@ export function useMeters({ initialProject }: UseMetersArgs) {
setFilteredMeters(meters.filter((m) => m.projectName === selectedProject));
}, [selectedProject, meters]);
+ const filteredProjects = useMemo(() => {
+ if (!selectedMeterTypeId) {
+ return allProjects;
+ }
+ return allProjects.filter(p => p.meterTypeId === selectedMeterTypeId);
+ }, [allProjects, selectedMeterTypeId]);
+
const projectsCounts = useMemo(() => {
return meters.reduce>((acc, m) => {
const project = m.projectName ?? "SIN PROYECTO";
@@ -92,13 +124,19 @@ export function useMeters({ initialProject }: UseMetersArgs) {
// loading
loadingProjects,
loadingMeters,
+ loadingMeterTypes,
// projects
allProjects,
+ filteredProjects,
projectsCounts,
selectedProject,
setSelectedProject,
+ meterTypes,
+ selectedMeterTypeId,
+ setSelectedMeterTypeId,
+
// data
meters,
setMeters,