diff --git a/src/app/AccountDetails/AccountDetails.tsx b/src/app/AccountDetails/AccountDetails.tsx index 2c1567b..344b292 100644 --- a/src/app/AccountDetails/AccountDetails.tsx +++ b/src/app/AccountDetails/AccountDetails.tsx @@ -1,3 +1,5 @@ +import { parseScanTimestamp, parseNumberToCurrency, } from 'src/app/utils/parseFuncs'; +import { renderStatusLabel } from "src/app/utils/renderStatusLabel"; import React, { useEffect, useState } from "react"; import { useParams } from 'react-router-dom'; import { @@ -18,9 +20,7 @@ import { FlexItem, Page, Spinner, - LabelGroup, } from "@patternfly/react-core"; -import InfoCircleIcon from "@patternfly/react-icons/dist/js/icons/info-circle-icon"; import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; import { getAccountByName, getAccountClusters } from "../services/api"; import { Link } from "react-router-dom"; @@ -32,15 +32,6 @@ interface LabelGroupOverflowProps { }[]; } -const LabelGroupOverflow: React.FunctionComponent = ({ - labels, -}) => ( - - {labels.map((label, index) => ( - - ))} - -); const AggregateClustersPerAccount: React.FunctionComponent = () => { const [data, setData] = useState([]); @@ -87,6 +78,7 @@ const AggregateClustersPerAccount: React.FunctionComponent = () => { ID Name + Status Provider Instance Count @@ -102,6 +94,9 @@ const AggregateClustersPerAccount: React.FunctionComponent = () => { {cluster.name} + + {renderStatusLabel(cluster.status)} + {cluster.provider} {cluster.instanceCount} @@ -122,7 +117,6 @@ const AccountDetails: React.FunctionComponent = () => { }); const [loading, setLoading] = useState(true); const location = useLocation(); - const queryParams = new URLSearchParams(location.search); useEffect(() => { const fetchData = async () => { try { @@ -192,21 +186,20 @@ const AccountDetails: React.FunctionComponent = () => { {accountData.accounts[0].provider} + Account Total Cost (Estimated) + + {parseNumberToCurrency(accountData.accounts[0].totalCost)} + - Labels Last scanned at - + {parseScanTimestamp(accountData.accounts[0].lastScanTimestamp)} - Created at - - - diff --git a/src/app/ClusterDetails/ClusterDetails.tsx b/src/app/ClusterDetails/ClusterDetails.tsx index 85f044b..2adf128 100644 --- a/src/app/ClusterDetails/ClusterDetails.tsx +++ b/src/app/ClusterDetails/ClusterDetails.tsx @@ -1,5 +1,7 @@ import React, { useEffect, useState } from "react"; import { useParams } from 'react-router-dom'; +import { renderStatusLabel } from "src/app/utils/renderStatusLabel"; +import { parseScanTimestamp, parseNumberToCurrency, } from 'src/app/utils/parseFuncs'; import { PageSection, PageSectionVariants, @@ -20,7 +22,6 @@ import { Spinner, LabelGroup, } from "@patternfly/react-core"; -import InfoCircleIcon from "@patternfly/react-icons/dist/js/icons/info-circle-icon"; import { Table, Tbody, Td, Th, Thead, Tr, ThProps } from "@patternfly/react-table"; import { getCluster, getClusterInstances, getClusterTags } from "../services/api"; import { ClusterData, Instance, Tag, TagData } from "@app/types/types"; @@ -29,17 +30,6 @@ interface LabelGroupOverflowProps { labels: Array; } -const renderLabel = (labelText: string | null | undefined) => { - switch (labelText) { - case "Running": - return ; - case "Stopped": - return ; - default: - return ; - } -}; - const LabelGroupOverflow: React.FunctionComponent = ({ labels, }) => ( @@ -84,8 +74,8 @@ const AggregateInstancesPerCluster: React.FunctionComponent = () => { const [activeSortDirection, setActiveSortDirection] = React.useState<'asc' | 'desc' | undefined>('asc'); // sort dropdown expansion const getSortableRowValues = (instance: Instance): (string | number | null)[] => { - const { id, name, availabilityZone, instanceType, state, clusterID, provider } = instance; - return [id, name, availabilityZone, instanceType, state, clusterID, provider]; + const { id, name, availabilityZone, instanceType, status, clusterID, provider } = instance; + return [id, name, availabilityZone, instanceType, status, clusterID, provider]; }; // Sorting @@ -145,8 +135,8 @@ const AggregateInstancesPerCluster: React.FunctionComponent = () => { ID Name Type + Status AvailabilityZone - State @@ -161,10 +151,10 @@ const AggregateInstancesPerCluster: React.FunctionComponent = () => { {instance.name} {instance.instanceType} - {instance.availabilityZone} - - {renderLabel(instance.state)} + + {renderStatusLabel(instance.status)} + {instance.availabilityZone} ))} @@ -188,7 +178,6 @@ const ClusterDetails: React.FunctionComponent = () => { const [loading, setLoading] = useState(true); const location = useLocation(); const queryParams = new URLSearchParams(location.search); - const clusterStatus = queryParams.get('status'); useEffect(() => { const fetchData = async () => { try { @@ -259,58 +248,60 @@ const ClusterDetails: React.FunctionComponent = () => { {clusterID} + Status + + {renderStatusLabel(cluster.clusters[0].status)} + + Web console Console + Cluster Total Cost (Estimated) + + {parseNumberToCurrency(cluster.clusters[0].totalCost)} + - + + Cloud Provider {cluster.clusters[0].provider} - - - Labels - - - Account {cluster.clusters[0].accountName || "unknown"} - - - Last scanned at - - {cluster.clusters[0].lastScanTimestamp || "unknown"} - - - Region {cluster.clusters[0].region || "unknown"} - + + Created at - + {parseScanTimestamp(cluster.clusters[0].creationTimestamp)} - - - Owner + Last scanned at - {ownerTag} + {parseScanTimestamp(cluster.clusters[0].lastScanTimestamp)} - + + + Labels + Partner {partnerTag} + Owner + + {ownerTag} + diff --git a/src/app/Clusters/Clusters.tsx b/src/app/Clusters/Clusters.tsx index 25142f0..a6b6448 100644 --- a/src/app/Clusters/Clusters.tsx +++ b/src/app/Clusters/Clusters.tsx @@ -1,8 +1,6 @@ import { - PageSection, PageSectionVariants, - Pagination, Panel, ToolbarItem, Toolbar, @@ -10,38 +8,13 @@ import { TextContent, Text, SearchInput, - MenuToggle, - MenuToggleElement, - Label, - Select, - SelectList, - SelectGroup, - SelectOption, - OverflowMenu, - OverflowMenuGroup, - OverflowMenuContent, - OverflowMenuControl, - OverflowMenuItem, - OverflowMenuDropdownItem, - Button, - Dropdown, - ToolbarGroup, - DropdownList, Spinner, } from "@patternfly/react-core"; +import { renderStatusLabel } from "src/app/utils/renderStatusLabel"; import { Table, Thead, Tr, Th, Tbody, Td, ThProps } from "@patternfly/react-table"; import React, { useEffect, useState } from "react"; import { Link, useLocation } from "react-router-dom"; import { getClusters } from "../services/api"; -import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; -import EditIcon from '@patternfly/react-icons/dist/esm/icons/edit-icon'; -import SyncIcon from '@patternfly/react-icons/dist/esm/icons/sync-icon'; -import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon'; -import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon'; -import SortAmountDownIcon from '@patternfly/react-icons/dist/esm/icons/sort-amount-down-icon'; -import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; -import DashboardWrapper from '@patternfly/react-core/src/demos/examples/DashboardWrapper'; -import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; import { Cluster } from "@app/types/types"; @@ -115,17 +88,6 @@ const ClusterTable: React.FunctionComponent<{ searchValue: string, statusFilter: setFilteredData(filtered); }, [searchValue, clusterData, statusFilter, cloudProviderFilter]); - const renderLabel = (labelText: string | null | undefined) => { - switch (labelText) { - case "Running": - return ; - case "Stopped": - return ; - default: - return ; - } - }; - const columnNames = { id: "ID", name: "Name", @@ -226,7 +188,7 @@ const ClusterTable: React.FunctionComponent<{ searchValue: string, statusFilter: {cluster.name} - {renderLabel(cluster.status)} + {renderStatusLabel(cluster.status)} {cluster.accountName} diff --git a/src/app/ServerDetails/ServerDetails.tsx b/src/app/ServerDetails/ServerDetails.tsx index 23b7b93..6502ab9 100644 --- a/src/app/ServerDetails/ServerDetails.tsx +++ b/src/app/ServerDetails/ServerDetails.tsx @@ -1,4 +1,6 @@ import React, { useEffect, useState } from "react"; +import { renderStatusLabel } from "src/app/utils/renderStatusLabel"; +import { parseScanTimestamp, parseNumberToCurrency, } from 'src/app/utils/parseFuncs'; import { useParams } from 'react-router-dom'; import { PageSection, @@ -24,7 +26,7 @@ import InfoCircleIcon from "@patternfly/react-icons/dist/js/icons/info-circle-ic import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; import { getInstances, getInstanceByID } from "../services/api"; import { Link } from "react-router-dom"; -import { Instance, Instances, Tag } from "@app/types/types"; +import { Instances, Tag } from "@app/types/types"; import { useLocation } from "react-router-dom"; interface LabelGroupOverflowProps { labels: Array; @@ -110,6 +112,10 @@ const ServerDetails: React.FunctionComponent = () => { {instanceID} + Status + + {renderStatusLabel(instanceData.instances[0].status)} + Cluster ID { {instanceData.instances[0].clusterID} - - Cloud Provider {instanceData.instances[0].provider} + Labels - - Last scanned at - {instanceData.instances[0].tags.filter(label => label.key == "LaunchTime").map(label => ( - - ))} + {parseScanTimestamp(instanceData.instances[0].creationTimestamp)} + + Created at + + {parseScanTimestamp(instanceData.instances[0].creationTimestamp)} + - Created at + + + + Daily Cost (aprox) - {instanceData.instances[0].tags.filter(label => label.key == "LaunchTime").map(label => ( - - ))} + {parseNumberToCurrency(instanceData.instances[0].dailyCost)} + Total Cost (aprox) + + {parseNumberToCurrency(instanceData.instances[0].totalCost)} + + + + diff --git a/src/app/Servers/Servers.tsx b/src/app/Servers/Servers.tsx index 01e00b2..f87beb4 100644 --- a/src/app/Servers/Servers.tsx +++ b/src/app/Servers/Servers.tsx @@ -1,3 +1,4 @@ +import { renderStatusLabel } from "src/app/utils/renderStatusLabel"; import { PageSection, PageSectionVariants, @@ -494,7 +495,7 @@ const ServersTable: React.FunctionComponent = ({ if (statusSelection) { filtered = filtered.filter((instance) => - instance.state.includes(statusSelection) + instance.status.includes(statusSelection) ); // console.log("Filtered by status:", filtered); } @@ -526,23 +527,12 @@ const ServersTable: React.FunctionComponent = ({ const columnNames = { id: "ID", name: "Name", - state: "Status", + status: "Status", provider: "Provider", availabilityZone: "AZ", instanceType: "Type", }; - const renderLabel = (labelText: string | null | undefined) => { - switch (labelText) { - case "Running": - return ; - case "Stopped": - return ; - default: - return ; - } - }; - return ( {loading ? ( @@ -562,7 +552,7 @@ const ServersTable: React.FunctionComponent = ({ {columnNames.id} {columnNames.name} - {columnNames.state} + {columnNames.status} {columnNames.provider} {columnNames.availabilityZone} {columnNames.instanceType} @@ -581,8 +571,8 @@ const ServersTable: React.FunctionComponent = ({ {instance.name} - - {renderLabel(instance.state)} + + {renderStatusLabel(instance.status)} {instance.provider} {instance.availabilityZone} @@ -599,10 +589,6 @@ const ServersTable: React.FunctionComponent = ({ }; const Servers: React.FunctionComponent = () => { - const provider = useLocation(); - const queryParams = new URLSearchParams(provider.search); - const statusFilter = queryParams.get("status"); - const cloudProviderFilter = queryParams.get("provider"); const [searchValue, setSearchValue] = useState(""); const [statusSelection, setStatusSelection] = useState(""); const [providerSelections, setProviderSelections] = useState([]); diff --git a/src/app/types/types.tsx b/src/app/types/types.tsx index 915f9da..6ca46a5 100644 --- a/src/app/types/types.tsx +++ b/src/app/types/types.tsx @@ -12,7 +12,9 @@ export type Cluster = { consoleLink: string; accountName: string; instanceCount: number; + creationTimestamp: string; lastScanTimestamp: string; + totalCost: number; instances: Instance[]; }; @@ -26,6 +28,8 @@ export type Account = { name: string; provider: string; clusterCount: number; + lastScanTimestamp: string; + totalCost: number; clusters: Record; }; @@ -49,9 +53,13 @@ export type Instance = { name: string; availabilityZone: string; instanceType: string; - state: string; + status: string; clusterID: string; provider: string; + lastScanTimestamp: string; + creationTimestamp: string; + dailyCost: number; + totalCost: number; tags: Array; } diff --git a/src/app/utils/parseFuncs.tsx b/src/app/utils/parseFuncs.tsx new file mode 100644 index 0000000..00e867c --- /dev/null +++ b/src/app/utils/parseFuncs.tsx @@ -0,0 +1,11 @@ +import { parseISO, format } from 'date-fns'; + +export function parseScanTimestamp(ts: string) { + return format(parseISO(ts), 'HH:mm:ss - dd/MM/yyyy'); +} + +export function parseNumberToCurrency(value: number) { + return value.toLocaleString("en-US", { + style: "currency", currency:"EUR" + }); +} diff --git a/src/app/utils/renderStatusLabel.tsx b/src/app/utils/renderStatusLabel.tsx new file mode 100644 index 0000000..45d2550 --- /dev/null +++ b/src/app/utils/renderStatusLabel.tsx @@ -0,0 +1,19 @@ +import { + Label, +} from "@patternfly/react-core"; +import React from "react"; + +export function renderStatusLabel (labelText: string | null | undefined) { + switch (labelText) { + case "Running": + return ; + case "Stopped": + return ; + case "Terminated": + return ; + case "Unknown": + return ; + default: + return ; + } + } diff --git a/src/index.html b/src/index.html index ac848b0..9a742f2 100644 --- a/src/index.html +++ b/src/index.html @@ -3,8 +3,8 @@ - ClusterIQ - + Red Hat ClusterIQ + @@ -15,4 +15,4 @@
- \ No newline at end of file +