forked from BLC/sgeUpdated
Add 'sge-frontend/' from commit '5fa787e054b25ac53edc7ff0275ea7960a709401'
git-subtree-dir: sge-frontend git-subtree-mainline:876c278ac4git-subtree-split:5fa787e054
This commit is contained in:
265
sge-frontend/src/views/DataCenter.js
Normal file
265
sge-frontend/src/views/DataCenter.js
Normal file
@@ -0,0 +1,265 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { Card, CardHeader, CardTitle, Button } from "reactstrap";
|
||||
import DataTable from "react-data-table-component";
|
||||
import { ChevronDown, RefreshCw, Server, Monitor } from "react-feather";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { getDataCenters } from "../redux/actions/dataCenter";
|
||||
import SpinnerComponent from "../@core/components/spinner/Fallback-spinner";
|
||||
|
||||
const DataCenter = () => {
|
||||
const { t } = useTranslation();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Redux state
|
||||
const dataCenterStore = useSelector((state) => state.dataCenter);
|
||||
const [refreshInterval, setRefreshInterval] = useState(null);
|
||||
|
||||
const getAllPhysicalMachines = (dataCenter) => {
|
||||
if (!dataCenter.projects) return [];
|
||||
return dataCenter.projects.flatMap(project =>
|
||||
project.physicalMachines || []
|
||||
);
|
||||
};
|
||||
|
||||
// Table columns following your pattern
|
||||
const initialColumns = [
|
||||
{
|
||||
name: "Number",
|
||||
selector: (row) => row.number,
|
||||
sortable: true,
|
||||
minWidth: "100px",
|
||||
},
|
||||
{
|
||||
name: "Data Centers",
|
||||
selector: (row) => row.dataCenter,
|
||||
sortable: true,
|
||||
minWidth: "200px",
|
||||
},
|
||||
// Projects
|
||||
{
|
||||
name: "Projects",
|
||||
selector: (row) =>
|
||||
(row.projects || []).map((p) => p.name).join(", "),
|
||||
sortable: false,
|
||||
minWidth: "200px",
|
||||
cell: (row) => (
|
||||
<span>
|
||||
{(row.projects || []).map((p) => p.name).join(", ") || "-"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
// Physical Machines
|
||||
{
|
||||
name: "Physical Machines",
|
||||
selector: (row) => getAllPhysicalMachines(row),
|
||||
sortable: false,
|
||||
minWidth: "200px",
|
||||
cell: (row) => {
|
||||
const pms = getAllPhysicalMachines(row);
|
||||
return (
|
||||
<div className="d-flex align-items-center">
|
||||
<Server size={16} className="mr-1" />
|
||||
<span>{pms.length} machines</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Total Active VMs",
|
||||
selector: (row) => {
|
||||
const pms = getAllPhysicalMachines(row);
|
||||
return pms.reduce(
|
||||
(total, pm) => total + (pm.vms?.active?.length || 0),
|
||||
0
|
||||
);
|
||||
},
|
||||
sortable: true,
|
||||
minWidth: "150px",
|
||||
cell: (row) => {
|
||||
const pms = getAllPhysicalMachines(row);
|
||||
const totalVMs = pms.reduce(
|
||||
(total, pm) => total + (pm.vms?.active?.length || 0),
|
||||
0
|
||||
);
|
||||
return (
|
||||
<div className="d-flex align-items-center">
|
||||
<Monitor size={16} className="mr-1" />
|
||||
<span>{totalVMs}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Fetch data on component mount
|
||||
useEffect(() => {
|
||||
dispatch(getDataCenters());
|
||||
|
||||
// Set up auto-refresh every 2 minutes
|
||||
const interval = setInterval(() => {
|
||||
dispatch(getDataCenters());
|
||||
}, 2 * 60 * 1000);
|
||||
|
||||
setRefreshInterval(interval);
|
||||
|
||||
return () => {
|
||||
if (interval) clearInterval(interval);
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
// Handle errors
|
||||
useEffect(() => {
|
||||
if (dataCenterStore.error) {
|
||||
enqueueSnackbar(dataCenterStore.error, {
|
||||
variant: "error",
|
||||
preventDuplicate: true,
|
||||
});
|
||||
}
|
||||
}, [dataCenterStore.error, enqueueSnackbar]);
|
||||
|
||||
const handleRefresh = () => {
|
||||
dispatch(getDataCenters());
|
||||
};
|
||||
|
||||
// Expandable component for showing physical machines and VMs
|
||||
const ExpandableComponent = ({ data }) => {
|
||||
const physicalMachines = getAllPhysicalMachines(data);
|
||||
console.log("▶️ Expandable Data", physicalMachines);
|
||||
return (
|
||||
<div className="expandable-content p-3">
|
||||
<h6 className="mb-3">Physical Machines:</h6>
|
||||
{physicalMachines.length === 0 && (
|
||||
<p className="text-muted ml-3">No physical machines</p>
|
||||
)}
|
||||
{physicalMachines.map((pm) => (
|
||||
<div key={pm.id} className="mb-3 border rounded p-3">
|
||||
<h6 className="text-primary">{pm.name}</h6>
|
||||
|
||||
{/* Active VMs */}
|
||||
<p className="mb-2">
|
||||
<strong>Active VMs ({pm.vms?.active?.length || 0}):</strong>
|
||||
</p>
|
||||
{pm.vms?.active?.length > 0 ? (
|
||||
<div className="ml-3">
|
||||
{pm.vms.active.map((vm) => (
|
||||
<div
|
||||
key={vm.id}
|
||||
className="mb-2 p-2 border-left border-success"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col-md-3">
|
||||
<strong>Name:</strong> {vm.name}
|
||||
</div>
|
||||
<div className="col-md-2">
|
||||
<strong>Status:</strong>
|
||||
<span className="badge badge-success ml-1">
|
||||
{vm.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-md-2">
|
||||
<strong>Power:</strong> {vm.power}
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<strong>Config:</strong> CPU: {vm.config?.cpu}, RAM:{" "}
|
||||
{vm.config?.ram}, Disk: {vm.config?.disk}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-muted ml-3">No active VMs</p>
|
||||
)}
|
||||
|
||||
{/* Inactive VMs */}
|
||||
<p className="mb-2 mt-3">
|
||||
<strong>Inactive VMs ({pm.vms?.inactive?.length || 0}):</strong>
|
||||
</p>
|
||||
{pm.vms?.inactive?.length > 0 ? (
|
||||
<div className="ml-3">
|
||||
{pm.vms.inactive.map((vm) => (
|
||||
<div
|
||||
key={vm.id}
|
||||
className="mb-2 p-2 border-left border-warning"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col-md-3">
|
||||
<strong>Name:</strong> {vm.name}
|
||||
</div>
|
||||
<div className="col-md-2">
|
||||
<strong>Status:</strong>
|
||||
<span className="badge badge-warning ml-1">
|
||||
{vm.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-md-2">
|
||||
<strong>Power:</strong> {vm.power}
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<strong>Config:</strong> CPU: {vm.config?.cpu}, RAM:{" "}
|
||||
{vm.config?.ram}, Disk: {vm.config?.disk}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-muted ml-3">No inactive VMs</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: "2%" }}>
|
||||
<Card>
|
||||
<CardHeader className="border-bottom">
|
||||
<CardTitle tag="h4">Test</CardTitle>
|
||||
<Button
|
||||
className="ml-2"
|
||||
color="primary"
|
||||
onClick={handleRefresh}
|
||||
disabled={dataCenterStore.loading}
|
||||
>
|
||||
<RefreshCw size={15} />
|
||||
<span className="align-middle ml-50">
|
||||
{dataCenterStore.loading ? "Refreshing..." : "Refresh"}
|
||||
</span>
|
||||
</Button>
|
||||
</CardHeader>
|
||||
|
||||
<DataTable
|
||||
noHeader
|
||||
columns={initialColumns}
|
||||
data={dataCenterStore.dataCenters}
|
||||
sortIcon={<ChevronDown size={10} />}
|
||||
expandableRows
|
||||
expandableRowsComponent={ExpandableComponent}
|
||||
progressPending={dataCenterStore.loading}
|
||||
progressComponent={
|
||||
<div className="d-flex justify-content-center p-3">
|
||||
<SpinnerComponent />
|
||||
</div>
|
||||
}
|
||||
noDataComponent={
|
||||
<div className="d-flex flex-column align-items-center p-3">
|
||||
<Server size={48} className="text-muted mb-2" />
|
||||
|
||||
<p className="text-muted">No data centers found</p>
|
||||
</div>
|
||||
}
|
||||
className="react-dataTable"
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataCenter;
|
||||
Reference in New Issue
Block a user