Add 'sge-frontend/' from commit '5fa787e054b25ac53edc7ff0275ea7960a709401'

git-subtree-dir: sge-frontend
git-subtree-mainline: 876c278ac4
git-subtree-split: 5fa787e054
This commit is contained in:
2025-08-04 00:27:23 +03:00
337 changed files with 854877 additions and 0 deletions

View 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;