prueba columns
This commit is contained in:
@@ -53,6 +53,21 @@ export interface Meter {
|
||||
concentratorSerial?: string;
|
||||
projectId?: string;
|
||||
projectName?: string;
|
||||
|
||||
protocol?: string | null;
|
||||
mac?: string | null;
|
||||
gateway?: string | null;
|
||||
voltage?: number | null;
|
||||
voltageRtu?: number | null;
|
||||
voltageStatus?: string | null;
|
||||
signal?: number | null;
|
||||
leakageStatus?: string | null;
|
||||
burstStatus?: string | null;
|
||||
currentFlow?: number | null;
|
||||
totalFlowReverse?: number | null;
|
||||
manufacturer?: string | null;
|
||||
latitude?: number | null;
|
||||
longitude?: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,6 +82,21 @@ export interface MeterInput {
|
||||
type?: string;
|
||||
status?: string;
|
||||
installationDate?: string;
|
||||
|
||||
protocol?: string;
|
||||
mac?: string;
|
||||
gateway?: string;
|
||||
voltage?: number;
|
||||
voltageRtu?: number;
|
||||
voltageStatus?: string;
|
||||
signal?: number;
|
||||
leakageStatus?: string;
|
||||
burstStatus?: string;
|
||||
currentFlow?: number;
|
||||
totalFlowReverse?: number;
|
||||
manufacturer?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -182,6 +182,16 @@ export default function MetersPage({
|
||||
type: activeMeter.type,
|
||||
status: activeMeter.status,
|
||||
installationDate: activeMeter.installationDate ?? "",
|
||||
protocol: activeMeter.protocol ?? undefined,
|
||||
voltage: activeMeter.voltage ?? undefined,
|
||||
signal: activeMeter.signal ?? undefined,
|
||||
leakageStatus: activeMeter.leakageStatus ?? undefined,
|
||||
burstStatus: activeMeter.burstStatus ?? undefined,
|
||||
currentFlow: activeMeter.currentFlow ?? undefined,
|
||||
totalFlowReverse: activeMeter.totalFlowReverse ?? undefined,
|
||||
manufacturer: activeMeter.manufacturer ?? undefined,
|
||||
latitude: activeMeter.latitude ?? undefined,
|
||||
longitude: activeMeter.longitude ?? undefined,
|
||||
});
|
||||
setErrors({});
|
||||
setShowModal(true);
|
||||
@@ -311,6 +321,7 @@ export default function MetersPage({
|
||||
{showModal && (
|
||||
<MetersModal
|
||||
editingId={editingId}
|
||||
selectedProject={m.selectedProject}
|
||||
form={form}
|
||||
setForm={setForm}
|
||||
errors={errors}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { fetchConcentrators, type Concentrator } from "../../api/concentrators";
|
||||
|
||||
type Props = {
|
||||
editingId: string | null;
|
||||
selectedProject?: string;
|
||||
|
||||
form: MeterInput;
|
||||
setForm: React.Dispatch<React.SetStateAction<MeterInput>>;
|
||||
@@ -18,6 +19,7 @@ type Props = {
|
||||
|
||||
export default function MetersModal({
|
||||
editingId,
|
||||
selectedProject,
|
||||
form,
|
||||
setForm,
|
||||
errors,
|
||||
@@ -28,6 +30,7 @@ export default function MetersModal({
|
||||
const title = editingId ? "Editar Medidor" : "Agregar Medidor";
|
||||
const [concentrators, setConcentrators] = useState<Concentrator[]>([]);
|
||||
const [loadingConcentrators, setLoadingConcentrators] = useState(true);
|
||||
const isPruebaProject = selectedProject === "PRUEBA";
|
||||
|
||||
// Load concentrators for the dropdown
|
||||
useEffect(() => {
|
||||
@@ -187,6 +190,145 @@ export default function MetersModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isPruebaProject && (
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-gray-700 border-b pb-2">
|
||||
Información Técnica (Proyecto PRUEBA)
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Protocol</label>
|
||||
<input
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: LoRaWAN"
|
||||
value={form.protocol ?? ""}
|
||||
onChange={(e) => setForm({ ...form, protocol: e.target.value || undefined })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Voltage (V)</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: 3.6"
|
||||
value={form.voltage ?? ""}
|
||||
onChange={(e) => setForm({ ...form, voltage: e.target.value ? parseFloat(e.target.value) : undefined })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Signal (dBm)</label>
|
||||
<input
|
||||
type="number"
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: -85"
|
||||
value={form.signal ?? ""}
|
||||
onChange={(e) => setForm({ ...form, signal: e.target.value ? parseInt(e.target.value) : undefined })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Current Flow</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.0001"
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: 12.5"
|
||||
value={form.currentFlow ?? ""}
|
||||
onChange={(e) => setForm({ ...form, currentFlow: e.target.value ? parseFloat(e.target.value) : undefined })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Total Flow Reverse</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.0001"
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: 0.0"
|
||||
value={form.totalFlowReverse ?? ""}
|
||||
onChange={(e) => setForm({ ...form, totalFlowReverse: e.target.value ? parseFloat(e.target.value) : undefined })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Leakage Status</label>
|
||||
<select
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
value={form.leakageStatus ?? ""}
|
||||
onChange={(e) => setForm({ ...form, leakageStatus: e.target.value || undefined })}
|
||||
>
|
||||
<option value="">Selecciona...</option>
|
||||
<option value="OK">OK</option>
|
||||
<option value="WARNING">Warning</option>
|
||||
<option value="ALERT">Alert</option>
|
||||
<option value="CRITICAL">Critical</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Burst Status</label>
|
||||
<select
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
value={form.burstStatus ?? ""}
|
||||
onChange={(e) => setForm({ ...form, burstStatus: e.target.value || undefined })}
|
||||
>
|
||||
<option value="">Selecciona...</option>
|
||||
<option value="OK">OK</option>
|
||||
<option value="WARNING">Warning</option>
|
||||
<option value="ALERT">Alert</option>
|
||||
<option value="CRITICAL">Critical</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Manufacturer</label>
|
||||
<input
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: Kamstrup"
|
||||
value={form.manufacturer ?? ""}
|
||||
onChange={(e) => setForm({ ...form, manufacturer: e.target.value || undefined })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Latitude</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.00000001"
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: 20.659698"
|
||||
value={form.latitude ?? ""}
|
||||
onChange={(e) => setForm({ ...form, latitude: e.target.value ? parseFloat(e.target.value) : undefined })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm text-gray-600 mb-1">Longitude</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.00000001"
|
||||
className="w-full border px-3 py-2 rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
placeholder="ej: -103.349609"
|
||||
value={form.longitude ?? ""}
|
||||
onChange={(e) => setForm({ ...form, longitude: e.target.value ? parseFloat(e.target.value) : undefined })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ACTIONS */}
|
||||
<div className="flex justify-end gap-2 pt-3 border-t">
|
||||
<button onClick={onClose} className="px-4 py-2 rounded hover:bg-gray-100">
|
||||
|
||||
@@ -30,58 +30,103 @@ export default function MetersTable({
|
||||
GRANDES: "Grandes consumidores",
|
||||
};
|
||||
|
||||
const isPruebaProject = selectedProject === "PRUEBA";
|
||||
|
||||
const defaultColumns = [
|
||||
{ title: "Serial", field: "serialNumber", render: (r: Meter) => r.serialNumber || "-" },
|
||||
{ title: "Meter ID", field: "meterId", render: (r: Meter) => r.meterId || "-" },
|
||||
{ title: "Nombre", field: "name", render: (r: Meter) => r.name || "-" },
|
||||
{ title: "Ubicación", field: "location", render: (r: Meter) => r.location || "-" },
|
||||
{
|
||||
title: "Tipo",
|
||||
field: "type",
|
||||
render: (r: Meter) => {
|
||||
const typeLabels: Record<string, string> = {
|
||||
LORA: "LoRa",
|
||||
LORAWAN: "LoRaWAN",
|
||||
GRANDES: "Grandes Consumidores",
|
||||
};
|
||||
const typeColors: Record<string, string> = {
|
||||
LORA: "text-green-600 border-green-600",
|
||||
LORAWAN: "text-purple-600 border-purple-600",
|
||||
GRANDES: "text-orange-600 border-orange-600",
|
||||
};
|
||||
const type = r.type || "LORA";
|
||||
return (
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-semibold border ${typeColors[type] || "text-gray-600 border-gray-600"}`}
|
||||
>
|
||||
{typeLabels[type] || type}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Estado",
|
||||
field: "status",
|
||||
render: (r: Meter) => (
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-semibold border ${
|
||||
r.status === "ACTIVE"
|
||||
? "text-blue-600 border-blue-600"
|
||||
: "text-red-600 border-red-600"
|
||||
}`}
|
||||
>
|
||||
{r.status || "-"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{ title: "Concentrador", field: "concentratorName", render: (r: Meter) => r.concentratorName || "-" },
|
||||
{ title: "Última Lectura", field: "lastReadingValue", render: (r: Meter) => r.lastReadingValue != null ? Number(r.lastReadingValue).toFixed(2) : "-" },
|
||||
];
|
||||
|
||||
const pruebaColumns = [
|
||||
{ title: "Meters No.", field: "meterId", render: (r: Meter) => r.meterId || r.serialNumber || "-" },
|
||||
{ title: "Name", field: "name", render: (r: Meter) => r.name || "-" },
|
||||
{ title: "Protocol", field: "protocol", render: (r: Meter) => r.protocol || "-" },
|
||||
{
|
||||
title: "Status",
|
||||
field: "status",
|
||||
render: (r: Meter) => (
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-semibold border ${
|
||||
r.status === "ACTIVE"
|
||||
? "text-blue-600 border-blue-600"
|
||||
: "text-red-600 border-red-600"
|
||||
}`}
|
||||
>
|
||||
{r.status || "-"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{ title: "Total Flow", field: "lastReadingValue", render: (r: Meter) => r.lastReadingValue != null ? Number(r.lastReadingValue).toFixed(2) : "-" },
|
||||
{
|
||||
title: "Last Contact",
|
||||
field: "lastReadingAt",
|
||||
render: (r: Meter) => r.lastReadingAt ? new Date(r.lastReadingAt).toLocaleString('es-MX', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
}) : "-"
|
||||
},
|
||||
{ title: "Voltage", field: "voltage", render: (r: Meter) => r.voltage != null ? `${Number(r.voltage).toFixed(2)} V` : "-" },
|
||||
{ title: "Signal", field: "signal", render: (r: Meter) => r.signal != null ? `${r.signal} dBm` : "-" },
|
||||
{ title: "Leakage Status", field: "leakageStatus", render: (r: Meter) => r.leakageStatus || "-" },
|
||||
{ title: "Burst Status", field: "burstStatus", render: (r: Meter) => r.burstStatus || "-" },
|
||||
{ title: "Current Flow", field: "currentFlow", render: (r: Meter) => r.currentFlow != null ? Number(r.currentFlow).toFixed(4) : "-" },
|
||||
{ title: "Total Flow Reverse", field: "totalFlowReverse", render: (r: Meter) => r.totalFlowReverse != null ? Number(r.totalFlowReverse).toFixed(4) : "-" },
|
||||
];
|
||||
|
||||
const columns = isPruebaProject ? pruebaColumns : defaultColumns;
|
||||
|
||||
return (
|
||||
<div className={disabled ? "opacity-60 pointer-events-none" : ""}>
|
||||
<MaterialTable
|
||||
title="Meters"
|
||||
isLoading={isLoading}
|
||||
columns={[
|
||||
{ title: "Serial", field: "serialNumber", render: (r: Meter) => r.serialNumber || "-" },
|
||||
{ title: "Meter ID", field: "meterId", render: (r: Meter) => r.meterId || "-" },
|
||||
{ title: "Nombre", field: "name", render: (r: Meter) => r.name || "-" },
|
||||
{ title: "Ubicación", field: "location", render: (r: Meter) => r.location || "-" },
|
||||
{
|
||||
title: "Tipo",
|
||||
field: "type",
|
||||
render: (r: Meter) => {
|
||||
const typeLabels: Record<string, string> = {
|
||||
LORA: "LoRa",
|
||||
LORAWAN: "LoRaWAN",
|
||||
GRANDES: "Grandes Consumidores",
|
||||
};
|
||||
const typeColors: Record<string, string> = {
|
||||
LORA: "text-green-600 border-green-600",
|
||||
LORAWAN: "text-purple-600 border-purple-600",
|
||||
GRANDES: "text-orange-600 border-orange-600",
|
||||
};
|
||||
const type = r.type || "LORA";
|
||||
return (
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-semibold border ${typeColors[type] || "text-gray-600 border-gray-600"}`}
|
||||
>
|
||||
{typeLabels[type] || type}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Estado",
|
||||
field: "status",
|
||||
render: (r: Meter) => (
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-semibold border ${
|
||||
r.status === "ACTIVE"
|
||||
? "text-blue-600 border-blue-600"
|
||||
: "text-red-600 border-red-600"
|
||||
}`}
|
||||
>
|
||||
{r.status || "-"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{ title: "Concentrador", field: "concentratorName", render: (r: Meter) => r.concentratorName || "-" },
|
||||
{ title: "Última Lectura", field: "lastReadingValue", render: (r: Meter) => r.lastReadingValue != null ? Number(r.lastReadingValue).toFixed(2) : "-" },
|
||||
]}
|
||||
columns={columns}
|
||||
data={disabled ? [] : data}
|
||||
onRowClick={(_, rowData) => onRowClick(rowData as Meter)}
|
||||
options={{
|
||||
|
||||
Reference in New Issue
Block a user