Device Management section
This commit is contained in:
10
src/App.tsx
10
src/App.tsx
@@ -8,10 +8,6 @@ import DataMonitoring from "./pages/DataMonitoring";
|
|||||||
import DataQuery from "./pages/DataQuery";
|
import DataQuery from "./pages/DataQuery";
|
||||||
import Home from "./pages/Home";
|
import Home from "./pages/Home";
|
||||||
|
|
||||||
// Tipos para las páginas que reciben subPage
|
|
||||||
interface PageProps {
|
|
||||||
subPage: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [page, setPage] = useState<string>("home");
|
const [page, setPage] = useState<string>("home");
|
||||||
@@ -22,11 +18,11 @@ export default function App() {
|
|||||||
case "home":
|
case "home":
|
||||||
return <Home />;
|
return <Home />;
|
||||||
case "area":
|
case "area":
|
||||||
return <AreaManagement subPage={subPage} />; // ahora tipado correctamente
|
return <AreaManagement />;
|
||||||
case "operator":
|
case "operator":
|
||||||
return <OperatorManagement subPage={subPage} />; // también
|
return <OperatorManagement />;
|
||||||
case "device-management":
|
case "device-management":
|
||||||
return <DeviceManagement subPage={subPage} />;
|
return <DeviceManagement />;
|
||||||
case "data-monitoring":
|
case "data-monitoring":
|
||||||
return <DataMonitoring subPage={subPage} />;
|
return <DataMonitoring subPage={subPage} />;
|
||||||
case "data-query":
|
case "data-query":
|
||||||
|
|||||||
@@ -153,8 +153,14 @@ export default function AreaManagement() {
|
|||||||
<DataGrid
|
<DataGrid
|
||||||
rows={rows}
|
rows={rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pageSize={5}
|
initialState={{
|
||||||
rowsPerPageOptions={[5]}
|
pagination: {
|
||||||
|
paginationModel: {
|
||||||
|
pageSize: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
pageSizeOptions={[5]}
|
||||||
sx={{ border: "none", "& .MuiDataGrid-row:hover": { backgroundColor: "rgba(0,0,0,0.03)" } }}
|
sx={{ border: "none", "& .MuiDataGrid-row:hover": { backgroundColor: "rgba(0,0,0,0.03)" } }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,233 +1,503 @@
|
|||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
import { Plus, Trash2, Pencil, RefreshCcw } from "lucide-react";
|
||||||
import { Add, Delete, Refresh, Edit } from "@mui/icons-material";
|
import MaterialTable from "@material-table/core";
|
||||||
import { Button, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, TextField, CircularProgress } from "@mui/material";
|
|
||||||
|
|
||||||
interface Device {
|
interface Device {
|
||||||
id: number;
|
id: string;
|
||||||
areaName: string;
|
"Area Name": string;
|
||||||
deviceSn: string;
|
"Account Number": string;
|
||||||
deviceName: string;
|
"User Name": string;
|
||||||
deviceType: string;
|
"User Address": string;
|
||||||
deviceStatus: string;
|
"Meter S/N": string;
|
||||||
operator: string;
|
"Meter Name": string;
|
||||||
installedTime: string;
|
"Meter Status": string;
|
||||||
communicationTime: string;
|
"Protocol Type": string;
|
||||||
|
"Price No.": string;
|
||||||
|
"Price Name": string;
|
||||||
|
"DMA Partition": string;
|
||||||
|
"Supply Types": string;
|
||||||
|
"Device ID": string;
|
||||||
|
"Device Name": string;
|
||||||
|
"Device Type": string;
|
||||||
|
"Usage Analysis Type": string;
|
||||||
|
"Installed Time": string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeviceManagementProps {
|
interface ApiResponse {
|
||||||
subPage: string;
|
records: Device[];
|
||||||
|
next?: string;
|
||||||
|
prev?: string;
|
||||||
|
nestedNext?: string;
|
||||||
|
nestedPrev?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DeviceManagement({ subPage: _subPage }: DeviceManagementProps) {
|
export default function DeviceManagement() {
|
||||||
const [rows, setRows] = useState<Device[]>([
|
const [devices, setDevices] = useState<Device[]>([]);
|
||||||
{
|
const [search, setSearch] = useState("");
|
||||||
id: 1,
|
const [showModal, setShowModal] = useState(false);
|
||||||
areaName: "Operaciones",
|
const [editingId, setEditingId] = useState<string | null>(null);
|
||||||
deviceSn: "DEV001",
|
const [activeDevice, setActiveDevice] = useState<Device | null>(null);
|
||||||
deviceName: "Water Meter A1",
|
|
||||||
deviceType: "Flow Sensor",
|
|
||||||
deviceStatus: "Installed",
|
|
||||||
operator: "Juan Pérez",
|
|
||||||
installedTime: "2024-01-15 10:30:00",
|
|
||||||
communicationTime: "2024-12-16 14:25:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
areaName: "Calidad",
|
|
||||||
deviceSn: "DEV002",
|
|
||||||
deviceName: "Pressure Monitor B2",
|
|
||||||
deviceType: "Pressure Sensor",
|
|
||||||
deviceStatus: "Installed",
|
|
||||||
operator: "María García",
|
|
||||||
installedTime: "2024-02-20 09:15:00",
|
|
||||||
communicationTime: "2024-12-16 13:45:00"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
areaName: "Mantenimiento",
|
|
||||||
deviceSn: "DEV003",
|
|
||||||
deviceName: "Temperature Sensor C3",
|
|
||||||
deviceType: "Temp Sensor",
|
|
||||||
deviceStatus: "Uninstalled",
|
|
||||||
operator: "Carlos López",
|
|
||||||
installedTime: "2024-03-10 11:00:00",
|
|
||||||
communicationTime: "2024-12-15 16:30:00"
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
|
||||||
const [editMode, setEditMode] = useState(false);
|
|
||||||
const [currentDevice, setCurrentDevice] = useState<Device>({
|
|
||||||
id: 0,
|
|
||||||
areaName: "",
|
|
||||||
deviceSn: "",
|
|
||||||
deviceName: "",
|
|
||||||
deviceType: "",
|
|
||||||
deviceStatus: "",
|
|
||||||
operator: "",
|
|
||||||
installedTime: "",
|
|
||||||
communicationTime: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const columns: GridColDef[] = [
|
const emptyDevice: Omit<Device, "id"> = {
|
||||||
{ field: "areaName", headerName: "Area Name", width: 150 },
|
"Area Name": "",
|
||||||
{ field: "deviceSn", headerName: "Device S/N", width: 130 },
|
"Account Number": "",
|
||||||
{ field: "deviceName", headerName: "Device Name", width: 180 },
|
"User Name": "",
|
||||||
{ field: "deviceType", headerName: "Device Type", width: 130 },
|
"User Address": "",
|
||||||
{ field: "deviceStatus", headerName: "Device Status", width: 130 },
|
"Meter S/N": "",
|
||||||
{ field: "operator", headerName: "Operator", width: 150 },
|
"Meter Name": "",
|
||||||
{ field: "installedTime", headerName: "Installed Time", width: 180 },
|
"Meter Status": "",
|
||||||
{ field: "communicationTime", headerName: "Communication Time", width: 180 },
|
"Protocol Type": "",
|
||||||
|
"Price No.": "",
|
||||||
|
"Price Name": "",
|
||||||
|
"DMA Partition": "",
|
||||||
|
"Supply Types": "",
|
||||||
|
"Device ID": "",
|
||||||
|
"Device Name": "",
|
||||||
|
"Device Type": "",
|
||||||
|
"Usage Analysis Type": "",
|
||||||
|
"Installed Time": "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const [form, setForm] = useState<Omit<Device, "id">>(emptyDevice);
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
"/api/v3/data/ppfu31vhv5gf6i0/mp1izvcpok5rk6s/records"
|
||||||
|
);
|
||||||
|
const data: ApiResponse = await response.json();
|
||||||
|
setDevices(data.records);
|
||||||
|
setActiveDevice(null);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading devices:", error);
|
||||||
|
const mockData: Device[] = [
|
||||||
{
|
{
|
||||||
field: "actions",
|
id: "1",
|
||||||
headerName: "Actions",
|
"Area Name": "Operaciones",
|
||||||
width: 150,
|
"Account Number": "ACC001",
|
||||||
renderCell: (params) => (
|
"User Name": "Juan Pérez",
|
||||||
<div className="flex gap-2">
|
"User Address": "Calle Principal 123",
|
||||||
<IconButton color="primary" size="small" onClick={() => handleEdit(params.row)}>
|
"Meter S/N": "DEV001",
|
||||||
<Edit fontSize="small" />
|
"Meter Name": "Water Meter A1",
|
||||||
</IconButton>
|
"Meter Status": "Active",
|
||||||
<IconButton color="error" size="small" onClick={() => handleDelete(params.row.id)}>
|
"Protocol Type": "MQTT",
|
||||||
<Delete fontSize="small" />
|
"Price No.": "P001",
|
||||||
</IconButton>
|
"Price Name": "Standard Rate",
|
||||||
</div>
|
"DMA Partition": "Zone A",
|
||||||
),
|
"Supply Types": "Water",
|
||||||
|
"Device ID": "D001",
|
||||||
|
"Device Name": "Flow Sensor",
|
||||||
|
"Device Type": "Flow Sensor",
|
||||||
|
"Usage Analysis Type": "Daily",
|
||||||
|
"Installed Time": "2024-01-15 10:30:00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
"Area Name": "Calidad",
|
||||||
|
"Account Number": "ACC002",
|
||||||
|
"User Name": "María García",
|
||||||
|
"User Address": "Avenida Central 456",
|
||||||
|
"Meter S/N": "DEV002",
|
||||||
|
"Meter Name": "Pressure Monitor B2",
|
||||||
|
"Meter Status": "Active",
|
||||||
|
"Protocol Type": "LoRa",
|
||||||
|
"Price No.": "P002",
|
||||||
|
"Price Name": "Premium Rate",
|
||||||
|
"DMA Partition": "Zone B",
|
||||||
|
"Supply Types": "Water",
|
||||||
|
"Device ID": "D002",
|
||||||
|
"Device Name": "Pressure Sensor",
|
||||||
|
"Device Type": "Pressure Sensor",
|
||||||
|
"Usage Analysis Type": "Hourly",
|
||||||
|
"Installed Time": "2024-02-20 09:15:00",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
setDevices(mockData);
|
||||||
const handleDelete = (id: number) => {
|
} finally {
|
||||||
if (confirm("¿Deseas eliminar este dispositivo?")) {
|
setLoading(false);
|
||||||
setRows(rows.filter(row => row.id !== id));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = (device: Device) => {
|
useEffect(() => {
|
||||||
setCurrentDevice(device);
|
loadData();
|
||||||
setEditMode(true);
|
}, []);
|
||||||
setDialogOpen(true);
|
|
||||||
|
const handleSave = () => {
|
||||||
|
if (editingId) {
|
||||||
|
setDevices((prev) =>
|
||||||
|
prev.map((device) =>
|
||||||
|
device.id === editingId ? { ...device, ...form } : device
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const newDevice: Device = {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
...form,
|
||||||
|
};
|
||||||
|
setDevices((prev) => [...prev, newDevice]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowModal(false);
|
||||||
|
setEditingId(null);
|
||||||
|
setForm(emptyDevice);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleEdit = () => {
|
||||||
const newId = rows.length ? Math.max(...rows.map(r => r.id)) + 1 : 1;
|
if (!activeDevice) return;
|
||||||
setRows([...rows, { ...currentDevice, id: newId }]);
|
setEditingId(activeDevice.id);
|
||||||
setDialogOpen(false);
|
setForm({ ...activeDevice });
|
||||||
resetForm();
|
setShowModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdate = () => {
|
const handleDelete = () => {
|
||||||
setRows(rows.map(r => (r.id === currentDevice.id ? currentDevice : r)));
|
if (!activeDevice) return;
|
||||||
setDialogOpen(false);
|
|
||||||
resetForm();
|
if (confirm("¿Deseas eliminar este dispositivo?")) {
|
||||||
|
setDevices((prev) =>
|
||||||
|
prev.filter((device) => device.id !== activeDevice.id)
|
||||||
|
);
|
||||||
|
setActiveDevice(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetForm = () => {
|
const filteredDevices = devices.filter((device) => {
|
||||||
setCurrentDevice({
|
const q = search.toLowerCase();
|
||||||
id: 0,
|
return (
|
||||||
areaName: "",
|
device["Area Name"].toLowerCase().includes(q) ||
|
||||||
deviceSn: "",
|
device["User Name"].toLowerCase().includes(q) ||
|
||||||
deviceName: "",
|
device["Meter S/N"].toLowerCase().includes(q) ||
|
||||||
deviceType: "",
|
device["Device Name"].toLowerCase().includes(q)
|
||||||
deviceStatus: "",
|
);
|
||||||
operator: "",
|
|
||||||
installedTime: "",
|
|
||||||
communicationTime: "",
|
|
||||||
});
|
});
|
||||||
setEditMode(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRefresh = () => {
|
|
||||||
setLoading(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setRows([...rows]);
|
|
||||||
setLoading(false);
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 p-6 h-full">
|
<div className="flex gap-6 p-6 w-full bg-gray-100">
|
||||||
|
<div className="flex-1 flex flex-col gap-6">
|
||||||
<div
|
<div
|
||||||
className="flex justify-between items-center p-5 rounded-xl"
|
className="rounded-xl shadow p-6 text-white flex justify-between items-center"
|
||||||
style={{
|
style={{
|
||||||
background: "linear-gradient(135deg, #4c5f9e, #2a355d, #566bb8, #3d4e87)",
|
background:
|
||||||
|
"linear-gradient(135deg, #4c5f9e, #2a355d, #566bb8, #3d4e87)",
|
||||||
backgroundSize: "350% 350%",
|
backgroundSize: "350% 350%",
|
||||||
animation: "gradientMove 10s ease infinite",
|
animation: "gradientMove 10s ease infinite",
|
||||||
color: "white",
|
|
||||||
backdropFilter: "blur(10px)",
|
|
||||||
border: "1px solid rgba(255,255,255,0.25)",
|
|
||||||
boxShadow: "0px 8px 22px rgba(0,0,0,0.25)",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h1 className="text-xl font-bold">Device Management</h1>
|
<div>
|
||||||
<div className="flex gap-3">
|
<h1 className="text-2xl font-bold">Device Management</h1>
|
||||||
<Button
|
<p className="text-sm text-blue-100">Water Meter Devices</p>
|
||||||
variant="outlined"
|
</div>
|
||||||
startIcon={<Add />}
|
|
||||||
sx={{
|
|
||||||
color: "white",
|
|
||||||
borderColor: "rgba(255,255,255,0.4)",
|
|
||||||
"&:hover": { borderColor: "white", background: "rgba(255,255,255,0.15)" },
|
|
||||||
}}
|
|
||||||
onClick={() => setDialogOpen(true)}
|
|
||||||
>
|
|
||||||
Agregar
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<div className="flex items-center gap-3">
|
||||||
variant="outlined"
|
<button
|
||||||
startIcon={loading ? <CircularProgress size={18} color="inherit" /> : <Refresh />}
|
onClick={() => {
|
||||||
sx={{
|
setForm(emptyDevice);
|
||||||
color: "white",
|
setEditingId(null);
|
||||||
borderColor: "rgba(255,255,255,0.4)",
|
setShowModal(true);
|
||||||
"&:hover": { borderColor: "white", background: "rgba(255,255,255,0.15)" },
|
|
||||||
}}
|
}}
|
||||||
onClick={handleRefresh}
|
className="flex items-center gap-2 px-4 py-2 bg-white text-[#4c5f9e] rounded-lg"
|
||||||
>
|
>
|
||||||
{loading ? "Recargando..." : "Refrescar"}
|
<Plus size={16} /> Add
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleEdit}
|
||||||
|
disabled={!activeDevice}
|
||||||
|
className={`flex items-center gap-2 px-4 py-2 border border-white/40 rounded-lg
|
||||||
|
${!activeDevice ? "opacity-70 cursor-not-allowed" : "hover:bg-white/10"}`}
|
||||||
|
>
|
||||||
|
<Pencil size={16} /> Edit
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleDelete}
|
||||||
|
disabled={!activeDevice}
|
||||||
|
className={`flex items-center gap-2 px-4 py-2 border border-white/40 rounded-lg
|
||||||
|
${!activeDevice ? "opacity-70 cursor-not-allowed" : "hover:bg-white/10"}`}
|
||||||
|
>
|
||||||
|
<Trash2 size={16} /> Delete
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={loadData}
|
||||||
|
className="flex items-center gap-2 px-4 py-2 border border-white/40 rounded-lg hover:bg-white/10"
|
||||||
|
>
|
||||||
|
<RefreshCcw size={16} /> Refresh
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 bg-white rounded-xl overflow-hidden shadow-md">
|
<input
|
||||||
<DataGrid
|
className="bg-white rounded-lg shadow px-4 py-2 text-sm"
|
||||||
rows={rows}
|
placeholder="Search devices..."
|
||||||
columns={columns}
|
value={search}
|
||||||
initialState={{
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
pagination: {
|
/>
|
||||||
paginationModel: {
|
|
||||||
pageSize: 5,
|
<MaterialTable
|
||||||
|
title="Devices"
|
||||||
|
columns={[
|
||||||
|
{ title: "Area Name", field: "Area Name" },
|
||||||
|
{ title: "Account Number", field: "Account Number" },
|
||||||
|
{ title: "User Name", field: "User Name" },
|
||||||
|
{ title: "User Address", field: "User Address" },
|
||||||
|
{ title: "Meter S/N", field: "Meter S/N" },
|
||||||
|
{ title: "Meter Name", field: "Meter Name" },
|
||||||
|
{
|
||||||
|
title: "Meter Status",
|
||||||
|
field: "Meter Status",
|
||||||
|
render: (rowData) => (
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-semibold border ${
|
||||||
|
rowData["Meter Status"] === "Active"
|
||||||
|
? "text-blue-600 border-blue-600"
|
||||||
|
: "text-red-600 border-red-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{rowData["Meter Status"]}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
{ title: "Protocol Type", field: "Protocol Type" },
|
||||||
|
{ title: "Price No.", field: "Price No." },
|
||||||
|
{ title: "Price Name", field: "Price Name" },
|
||||||
|
{ title: "DMA Partition", field: "DMA Partition" },
|
||||||
|
{ title: "Supply Types", field: "Supply Types" },
|
||||||
|
{ title: "Device ID", field: "Device ID" },
|
||||||
|
{ title: "Device Name", field: "Device Name" },
|
||||||
|
{ title: "Device Type", field: "Device Type" },
|
||||||
|
{ title: "Usage Analysis Type", field: "Usage Analysis Type" },
|
||||||
|
{
|
||||||
|
title: "Installed Time",
|
||||||
|
field: "Installed Time",
|
||||||
|
type: "datetime",
|
||||||
},
|
},
|
||||||
|
]}
|
||||||
|
data={filteredDevices}
|
||||||
|
onRowClick={(_event, rowData) => {
|
||||||
|
setActiveDevice(rowData as Device);
|
||||||
}}
|
}}
|
||||||
pageSizeOptions={[5]}
|
actions={[
|
||||||
sx={{ border: "none", "& .MuiDataGrid-row:hover": { backgroundColor: "rgba(0,0,0,0.03)" } }}
|
{
|
||||||
|
icon: () => <Pencil size={16} />,
|
||||||
|
tooltip: "Edit Device",
|
||||||
|
onClick: (_event, rowData) => {
|
||||||
|
setActiveDevice(rowData as Device);
|
||||||
|
setEditingId((rowData as Device).id);
|
||||||
|
setForm({ ...(rowData as Device) });
|
||||||
|
setShowModal(true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: () => <Trash2 size={16} />,
|
||||||
|
tooltip: "Delete Device",
|
||||||
|
onClick: (_event, rowData) => {
|
||||||
|
setActiveDevice(rowData as Device);
|
||||||
|
handleDelete();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
options={{
|
||||||
|
actionsColumnIndex: -1,
|
||||||
|
search: false,
|
||||||
|
paging: true,
|
||||||
|
sorting: true,
|
||||||
|
headerStyle: {
|
||||||
|
textAlign: "center",
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
maxBodyHeight: "500px",
|
||||||
|
tableLayout: "fixed",
|
||||||
|
rowStyle: (rowData) => ({
|
||||||
|
backgroundColor:
|
||||||
|
activeDevice?.id === (rowData as Device).id
|
||||||
|
? "#EEF2FF"
|
||||||
|
: "#FFFFFF",
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
isLoading={loading}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog open={dialogOpen} onClose={() => { setDialogOpen(false); resetForm(); }}>
|
{showModal && (
|
||||||
<DialogTitle>{editMode ? "Editar Dispositivo" : "Agregar Nuevo Dispositivo"}</DialogTitle>
|
<div className="fixed inset-0 bg-black/40 flex items-center justify-center">
|
||||||
<DialogContent className="flex flex-col gap-3 min-w-[400px]">
|
<div className="bg-white rounded-xl p-6 w-[600px] max-h-[80vh] overflow-y-auto space-y-3">
|
||||||
{["areaName","deviceSn","deviceName","deviceType","deviceStatus","operator","installedTime","communicationTime"].map((field) => (
|
<h2 className="text-lg font-semibold">
|
||||||
<TextField
|
{editingId ? "Edit Device" : "Add Device"}
|
||||||
key={field}
|
</h2>
|
||||||
label={field.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}
|
|
||||||
type="text"
|
<div className="grid grid-cols-2 gap-3">
|
||||||
value={String(currentDevice[field as keyof Device] || "")}
|
<input
|
||||||
onChange={(e) => setCurrentDevice({ ...currentDevice, [field]: e.target.value })}
|
className="w-full border px-3 py-2 rounded"
|
||||||
fullWidth
|
placeholder="Area Name"
|
||||||
|
value={form["Area Name"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Area Name": e.target.value })
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
|
||||||
</DialogContent>
|
<input
|
||||||
<DialogActions>
|
className="w-full border px-3 py-2 rounded"
|
||||||
<Button onClick={() => { setDialogOpen(false); resetForm(); }}>Cancelar</Button>
|
placeholder="Account Number"
|
||||||
<Button variant="contained" onClick={editMode ? handleUpdate : handleAdd}>
|
value={form["Account Number"]}
|
||||||
{editMode ? "Actualizar" : "Agregar"}
|
onChange={(e) =>
|
||||||
</Button>
|
setForm({ ...form, "Account Number": e.target.value })
|
||||||
</DialogActions>
|
}
|
||||||
</Dialog>
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="User Name"
|
||||||
|
value={form["User Name"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "User Name": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="User Address"
|
||||||
|
value={form["User Address"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "User Address": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Meter S/N"
|
||||||
|
value={form["Meter S/N"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Meter S/N": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Meter Name"
|
||||||
|
value={form["Meter Name"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Meter Name": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<select
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
value={form["Meter Status"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Meter Status": e.target.value })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="">Select Status</option>
|
||||||
|
<option value="Active">Active</option>
|
||||||
|
<option value="Inactive">Inactive</option>
|
||||||
|
<option value="Maintenance">Maintenance</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Protocol Type"
|
||||||
|
value={form["Protocol Type"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Protocol Type": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Price No."
|
||||||
|
value={form["Price No."]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Price No.": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Price Name"
|
||||||
|
value={form["Price Name"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Price Name": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="DMA Partition"
|
||||||
|
value={form["DMA Partition"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "DMA Partition": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Supply Types"
|
||||||
|
value={form["Supply Types"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Supply Types": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Device ID"
|
||||||
|
value={form["Device ID"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Device ID": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Device Name"
|
||||||
|
value={form["Device Name"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Device Name": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Device Type"
|
||||||
|
value={form["Device Type"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Device Type": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
placeholder="Usage Analysis Type"
|
||||||
|
value={form["Usage Analysis Type"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Usage Analysis Type": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="datetime-local"
|
||||||
|
className="w-full border px-3 py-2 rounded"
|
||||||
|
value={form["Installed Time"]}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({ ...form, "Installed Time": e.target.value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-2 pt-3">
|
||||||
|
<button onClick={() => setShowModal(false)}>Cancel</button>
|
||||||
|
<button
|
||||||
|
onClick={handleSave}
|
||||||
|
className="bg-[#4c5f9e] text-white px-4 py-2 rounded"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
{`
|
{`
|
||||||
|
|||||||
Reference in New Issue
Block a user