Users filter in dashboard as organismos operadores
This commit is contained in:
@@ -12,6 +12,8 @@ import {
|
|||||||
import { fetchMeters, type Meter } from "../api/meters";
|
import { fetchMeters, type Meter } from "../api/meters";
|
||||||
import { getAuditLogs, type AuditLog } from "../api/audit";
|
import { getAuditLogs, type AuditLog } from "../api/audit";
|
||||||
import { fetchNotifications, type Notification } from "../api/notifications";
|
import { fetchNotifications, type Notification } from "../api/notifications";
|
||||||
|
import { getAllUsers, type User } from "../api/users";
|
||||||
|
import { fetchProjects, type Project } from "../api/projects";
|
||||||
import { getCurrentUserRole } from "../api/auth";
|
import { getCurrentUserRole } from "../api/auth";
|
||||||
import type { Page } from "../App";
|
import type { Page } from "../App";
|
||||||
import grhWatermark from "../assets/images/grhWatermark.png";
|
import grhWatermark from "../assets/images/grhWatermark.png";
|
||||||
@@ -21,6 +23,7 @@ import grhWatermark from "../assets/images/grhWatermark.png";
|
|||||||
type OrganismStatus = "ACTIVO" | "INACTIVO";
|
type OrganismStatus = "ACTIVO" | "INACTIVO";
|
||||||
|
|
||||||
type Organism = {
|
type Organism = {
|
||||||
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
region: string;
|
region: string;
|
||||||
projects: number;
|
projects: number;
|
||||||
@@ -29,6 +32,7 @@ type Organism = {
|
|||||||
lastSync: string;
|
lastSync: string;
|
||||||
contact: string;
|
contact: string;
|
||||||
status: OrganismStatus;
|
status: OrganismStatus;
|
||||||
|
projectId: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AlertItem = { company: string; type: string; time: string };
|
type AlertItem = { company: string; type: string; time: string };
|
||||||
@@ -52,47 +56,7 @@ export default function Home({
|
|||||||
|
|
||||||
const userRole = useMemo(() => getCurrentUserRole(), []);
|
const userRole = useMemo(() => getCurrentUserRole(), []);
|
||||||
const isOperator = userRole?.toUpperCase() === 'OPERATOR';
|
const isOperator = userRole?.toUpperCase() === 'OPERATOR';
|
||||||
|
const isAdmin = userRole?.toUpperCase() === 'ADMIN';
|
||||||
/* ================= ORGANISMS (MOCK) ================= */
|
|
||||||
|
|
||||||
const organismsData: Organism[] = [
|
|
||||||
{
|
|
||||||
name: "CESPT TIJUANA",
|
|
||||||
region: "Tijuana, BC",
|
|
||||||
projects: 6,
|
|
||||||
meters: 128,
|
|
||||||
activeAlerts: 0,
|
|
||||||
lastSync: "Hace 12 min",
|
|
||||||
contact: "Operaciones CESPT",
|
|
||||||
status: "ACTIVO",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CESPT TECATE",
|
|
||||||
region: "Tecate, BC",
|
|
||||||
projects: 3,
|
|
||||||
meters: 54,
|
|
||||||
activeAlerts: 1,
|
|
||||||
lastSync: "Hace 40 min",
|
|
||||||
contact: "Mantenimiento",
|
|
||||||
status: "ACTIVO",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "CESPT MEXICALI",
|
|
||||||
region: "Mexicali, BC",
|
|
||||||
projects: 4,
|
|
||||||
meters: 92,
|
|
||||||
activeAlerts: 0,
|
|
||||||
lastSync: "Hace 1 h",
|
|
||||||
contact: "Supervisión",
|
|
||||||
status: "ACTIVO",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const [selectedOrganism, setSelectedOrganism] = useState<string>(
|
|
||||||
organismsData[0]?.name ?? "CESPT TIJUANA"
|
|
||||||
);
|
|
||||||
const [showOrganisms, setShowOrganisms] = useState(false);
|
|
||||||
const [organismQuery, setOrganismQuery] = useState("");
|
|
||||||
|
|
||||||
/* ================= METERS ================= */
|
/* ================= METERS ================= */
|
||||||
|
|
||||||
@@ -112,12 +76,72 @@ export default function Home({
|
|||||||
loadMeters();
|
loadMeters();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// TODO: Reemplazar cuando el backend mande el organismo real (ej: meter.organismName)
|
const [projects, setProjects] = useState<Project[]>([]);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const getOrganismFromMeter = (_m: Meter): string => {
|
const loadProjects = async () => {
|
||||||
return "CESPT TIJUANA";
|
try {
|
||||||
|
const data = await fetchProjects();
|
||||||
|
setProjects(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error loading projects:", err);
|
||||||
|
setProjects([]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadProjects();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [users, setUsers] = useState<User[]>([]);
|
||||||
|
const [loadingUsers, setLoadingUsers] = useState(false);
|
||||||
|
const [selectedOrganism, setSelectedOrganism] = useState<string>("Todos");
|
||||||
|
const [showOrganisms, setShowOrganisms] = useState(false);
|
||||||
|
const [organismQuery, setOrganismQuery] = useState("");
|
||||||
|
|
||||||
|
const loadUsers = async () => {
|
||||||
|
setLoadingUsers(true);
|
||||||
|
try {
|
||||||
|
const response = await getAllUsers({ is_active: true });
|
||||||
|
setUsers(response.data);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error loading users:", err);
|
||||||
|
setUsers([]);
|
||||||
|
} finally {
|
||||||
|
setLoadingUsers(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOperator) {
|
||||||
|
loadUsers();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const organismsData: Organism[] = useMemo(() => {
|
||||||
|
return users.map(user => {
|
||||||
|
const userMeters = user.project_id
|
||||||
|
? meters.filter(m => m.projectId === user.project_id).length
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
const userProjects = user.project_id ? 1 : 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
region: user.email,
|
||||||
|
projects: userProjects,
|
||||||
|
meters: userMeters,
|
||||||
|
activeAlerts: 0,
|
||||||
|
lastSync: user.last_login ? `Último acceso: ${new Date(user.last_login).toLocaleDateString()}` : "Nunca",
|
||||||
|
contact: user.role?.name || "N/A",
|
||||||
|
status: user.is_active ? "ACTIVO" : "INACTIVO",
|
||||||
|
projectId: user.project_id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [users, meters]);
|
||||||
|
|
||||||
const [auditLogs, setAuditLogs] = useState<AuditLog[]>([]);
|
const [auditLogs, setAuditLogs] = useState<AuditLog[]>([]);
|
||||||
const [loadingAuditLogs, setLoadingAuditLogs] = useState(false);
|
const [loadingAuditLogs, setLoadingAuditLogs] = useState(false);
|
||||||
|
|
||||||
@@ -141,25 +165,53 @@ export default function Home({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const filteredMeters = useMemo(
|
const filteredMeters = useMemo(() => {
|
||||||
() => meters.filter((m) => getOrganismFromMeter(m) === selectedOrganism),
|
if (selectedOrganism === "Todos") {
|
||||||
[meters, selectedOrganism]
|
return meters;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
const selectedUser = users.find(u => u.id === selectedOrganism);
|
||||||
|
if (!selectedUser || !selectedUser.project_id) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return meters.filter((m) => m.projectId === selectedUser.project_id);
|
||||||
|
}, [meters, selectedOrganism, users]);
|
||||||
|
|
||||||
const filteredProjects = useMemo(
|
const filteredProjects = useMemo(
|
||||||
() => [...new Set(filteredMeters.map((m) => m.projectName))].filter(Boolean) as string[],
|
() => [...new Set(filteredMeters.map((m) => m.projectName))].filter(Boolean) as string[],
|
||||||
[filteredMeters]
|
[filteredMeters]
|
||||||
);
|
);
|
||||||
|
|
||||||
const chartData = useMemo(
|
const selectedUserProjectName = useMemo(() => {
|
||||||
() =>
|
if (selectedOrganism === "Todos") return null;
|
||||||
filteredProjects.map((projectName) => ({
|
|
||||||
|
const selectedUser = users.find(u => u.id === selectedOrganism);
|
||||||
|
if (!selectedUser || !selectedUser.project_id) return null;
|
||||||
|
|
||||||
|
const project = projects.find(p => p.id === selectedUser.project_id);
|
||||||
|
return project?.name || null;
|
||||||
|
}, [selectedOrganism, users, projects]);
|
||||||
|
|
||||||
|
const chartData = useMemo(() => {
|
||||||
|
if (selectedOrganism === "Todos") {
|
||||||
|
return filteredProjects.map((projectName) => ({
|
||||||
name: projectName,
|
name: projectName,
|
||||||
meterCount: filteredMeters.filter((m) => m.projectName === projectName)
|
meterCount: filteredMeters.filter((m) => m.projectName === projectName).length,
|
||||||
.length,
|
}));
|
||||||
})),
|
}
|
||||||
[filteredProjects, filteredMeters]
|
|
||||||
);
|
if (selectedUserProjectName) {
|
||||||
|
const meterCount = filteredMeters.length;
|
||||||
|
|
||||||
|
return [{
|
||||||
|
name: selectedUserProjectName,
|
||||||
|
meterCount: meterCount,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}, [selectedOrganism, filteredProjects, filteredMeters, selectedUserProjectName]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const handleBarClick = (data: any) => {
|
const handleBarClick = (data: any) => {
|
||||||
@@ -174,7 +226,7 @@ export default function Home({
|
|||||||
const q = organismQuery.trim().toLowerCase();
|
const q = organismQuery.trim().toLowerCase();
|
||||||
if (!q) return organismsData;
|
if (!q) return organismsData;
|
||||||
return organismsData.filter((o) => o.name.toLowerCase().includes(q));
|
return organismsData.filter((o) => o.name.toLowerCase().includes(q));
|
||||||
}, [organismQuery]);
|
}, [organismQuery, organismsData]);
|
||||||
|
|
||||||
const [notifications, setNotifications] = useState<Notification[]>([]);
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
||||||
const [loadingNotifications, setLoadingNotifications] = useState(false);
|
const [loadingNotifications, setLoadingNotifications] = useState(false);
|
||||||
@@ -362,28 +414,32 @@ export default function Home({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Organismos Operadores */}
|
{isAdmin && (
|
||||||
<div className="bg-white rounded-xl shadow p-4">
|
<div className="bg-white rounded-xl shadow p-4">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-500">Organismos Operadores</p>
|
<p className="text-sm text-gray-500">Organismos Operadores</p>
|
||||||
<p className="text-xs text-gray-400">
|
<p className="text-xs text-gray-400">
|
||||||
Seleccionado:{" "}
|
Seleccionado:{" "}
|
||||||
<span className="font-semibold">{selectedOrganism}</span>
|
<span className="font-semibold">
|
||||||
</p>
|
{selectedOrganism === "Todos"
|
||||||
|
? "Todos"
|
||||||
|
: organismsData.find(o => o.id === selectedOrganism)?.name || "Ninguno"}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center justify-center rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow hover:bg-blue-700 transition"
|
||||||
|
onClick={() => setShowOrganisms(true)}
|
||||||
|
>
|
||||||
|
Organismos Operadores
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
{showOrganisms && (
|
||||||
type="button"
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||||
className="inline-flex items-center justify-center rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow hover:bg-blue-700 transition"
|
|
||||||
onClick={() => setShowOrganisms(true)}
|
|
||||||
>
|
|
||||||
Organismos Operadores
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showOrganisms && (
|
|
||||||
<div className="fixed inset-0 z-30">
|
|
||||||
{/* Overlay */}
|
{/* Overlay */}
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0 bg-black/40"
|
className="absolute inset-0 bg-black/40"
|
||||||
@@ -394,7 +450,7 @@ export default function Home({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Panel */}
|
{/* Panel */}
|
||||||
<div className="absolute right-0 top-0 h-full w-full sm:w-[520px] bg-white shadow-2xl flex flex-col">
|
<div className="relative w-full max-w-2xl max-h-[90vh] bg-white rounded-xl shadow-2xl flex flex-col">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="p-5 border-b flex items-start justify-between gap-3">
|
<div className="p-5 border-b flex items-start justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
@@ -402,8 +458,7 @@ export default function Home({
|
|||||||
Organismos Operadores
|
Organismos Operadores
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
Selecciona un organismo para filtrar la información del
|
Selecciona un organismo para filtrar la información del dashboard
|
||||||
dashboard.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -431,12 +486,59 @@ export default function Home({
|
|||||||
|
|
||||||
{/* List */}
|
{/* List */}
|
||||||
<div className="p-5 overflow-y-auto flex-1 space-y-3">
|
<div className="p-5 overflow-y-auto flex-1 space-y-3">
|
||||||
{filteredOrganisms.map((o) => {
|
{loadingUsers ? (
|
||||||
const active = o.name === selectedOrganism;
|
<div className="flex items-center justify-center py-10">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
"rounded-xl border p-4 transition",
|
||||||
|
selectedOrganism === "Todos"
|
||||||
|
? "border-blue-600 bg-blue-50/40"
|
||||||
|
: "border-gray-200 bg-white hover:bg-gray-50",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-3">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-gray-800">
|
||||||
|
Todos los Organismos
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-500">Ver todos los datos del sistema</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className="text-xs font-semibold px-2 py-1 rounded-full bg-blue-100 text-blue-700">
|
||||||
|
TODOS
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 flex items-center justify-end gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={[
|
||||||
|
"rounded-lg px-3 py-2 text-sm font-semibold shadow transition",
|
||||||
|
selectedOrganism === "Todos"
|
||||||
|
? "bg-blue-600 text-white hover:bg-blue-700"
|
||||||
|
: "bg-gray-900 text-white hover:bg-gray-800",
|
||||||
|
].join(" ")}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedOrganism("Todos");
|
||||||
|
setShowOrganisms(false);
|
||||||
|
setOrganismQuery("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{selectedOrganism === "Todos" ? "Seleccionado" : "Seleccionar"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{filteredOrganisms.map((o) => {
|
||||||
|
const active = o.id === selectedOrganism;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={o.name}
|
key={o.id}
|
||||||
className={[
|
className={[
|
||||||
"rounded-xl border p-4 transition",
|
"rounded-xl border p-4 transition",
|
||||||
active
|
active
|
||||||
@@ -464,7 +566,21 @@ export default function Home({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3 grid grid-cols-2 gap-2 text-xs">
|
<div className="mt-3 space-y-2 text-xs">
|
||||||
|
<div className="flex justify-between gap-2">
|
||||||
|
<span className="text-gray-500">Rol</span>
|
||||||
|
<span className="font-medium text-gray-800">
|
||||||
|
{o.contact}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between gap-2">
|
||||||
|
<span className="text-gray-500">Email</span>
|
||||||
|
<span className="font-medium text-gray-800 truncate max-w-[200px]">
|
||||||
|
{o.region}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between gap-2">
|
<div className="flex justify-between gap-2">
|
||||||
<span className="text-gray-500">Proyectos</span>
|
<span className="text-gray-500">Proyectos</span>
|
||||||
<span className="font-medium text-gray-800">
|
<span className="font-medium text-gray-800">
|
||||||
@@ -480,25 +596,11 @@ export default function Home({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between gap-2">
|
<div className="flex justify-between gap-2">
|
||||||
<span className="text-gray-500">Alertas activas</span>
|
<span className="text-gray-500">Último acceso</span>
|
||||||
<span className="font-medium text-gray-800">
|
|
||||||
{o.activeAlerts}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-between gap-2">
|
|
||||||
<span className="text-gray-500">Última sync</span>
|
|
||||||
<span className="font-medium text-gray-800">
|
<span className="font-medium text-gray-800">
|
||||||
{o.lastSync}
|
{o.lastSync}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-span-2 flex justify-between gap-2">
|
|
||||||
<span className="text-gray-500">Responsable</span>
|
|
||||||
<span className="font-medium text-gray-800">
|
|
||||||
{o.contact}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 flex items-center justify-end gap-2">
|
<div className="mt-4 flex items-center justify-end gap-2">
|
||||||
@@ -511,7 +613,7 @@ export default function Home({
|
|||||||
: "bg-gray-900 text-white hover:bg-gray-800",
|
: "bg-gray-900 text-white hover:bg-gray-800",
|
||||||
].join(" ")}
|
].join(" ")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedOrganism(o.name);
|
setSelectedOrganism(o.id);
|
||||||
setShowOrganisms(false);
|
setShowOrganisms(false);
|
||||||
setOrganismQuery("");
|
setOrganismQuery("");
|
||||||
}}
|
}}
|
||||||
@@ -522,8 +624,10 @@ export default function Home({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{filteredOrganisms.length === 0 && (
|
{!loadingUsers && filteredOrganisms.length === 0 && (
|
||||||
<div className="text-sm text-gray-500 text-center py-10">
|
<div className="text-sm text-gray-500 text-center py-10">
|
||||||
No se encontraron organismos.
|
No se encontraron organismos.
|
||||||
</div>
|
</div>
|
||||||
@@ -532,13 +636,13 @@ export default function Home({
|
|||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="p-5 border-t text-xs text-gray-500">
|
<div className="p-5 border-t text-xs text-gray-500">
|
||||||
Nota: Las propiedades están en modo demostración hasta integrar
|
Mostrando {filteredOrganisms.length} organismo{filteredOrganisms.length !== 1 ? 's' : ''} de {users.length} total{users.length !== 1 ? 'es' : ''}
|
||||||
backend.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Gráfica */}
|
{/* Gráfica */}
|
||||||
@@ -552,21 +656,57 @@ export default function Home({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="h-60">
|
{chartData.length === 0 && selectedOrganism !== "Todos" ? (
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<div className="h-60 flex flex-col items-center justify-center">
|
||||||
<BarChart
|
<p className="text-sm text-gray-500 mb-2">
|
||||||
data={chartData}
|
{selectedUserProjectName
|
||||||
margin={{ top: 5, right: 20, left: 0, bottom: 5 }}
|
? "Este organismo no tiene medidores registrados"
|
||||||
onClick={handleBarClick}
|
: "Este organismo no tiene un proyecto asignado"}
|
||||||
>
|
</p>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
{selectedUserProjectName && (
|
||||||
<XAxis dataKey="name" />
|
<p className="text-xs text-gray-400">
|
||||||
<YAxis />
|
Proyecto asignado: <span className="font-semibold">{selectedUserProjectName}</span>
|
||||||
<Tooltip />
|
</p>
|
||||||
<Bar dataKey="meterCount" fill="#4c5f9e" cursor="pointer" />
|
)}
|
||||||
</BarChart>
|
</div>
|
||||||
</ResponsiveContainer>
|
) : chartData.length === 0 ? (
|
||||||
</div>
|
<div className="h-60 flex items-center justify-center">
|
||||||
|
<p className="text-sm text-gray-500">No hay datos disponibles</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="h-60">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<BarChart
|
||||||
|
data={chartData}
|
||||||
|
margin={{ top: 5, right: 20, left: 0, bottom: 5 }}
|
||||||
|
onClick={handleBarClick}
|
||||||
|
>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
|
<XAxis dataKey="name" />
|
||||||
|
<YAxis />
|
||||||
|
<Tooltip />
|
||||||
|
<Bar dataKey="meterCount" fill="#4c5f9e" cursor="pointer" />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedOrganism !== "Todos" && selectedUserProjectName && (
|
||||||
|
<div className="mt-4 pt-4 border-t border-gray-200">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">Proyecto del organismo:</span>
|
||||||
|
<span className="ml-2 font-semibold text-gray-800">{selectedUserProjectName}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">Total de medidores:</span>
|
||||||
|
<span className="ml-2 font-semibold text-blue-600">{filteredMeters.length}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isOperator && (
|
{!isOperator && (
|
||||||
|
|||||||
Reference in New Issue
Block a user