Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
572 changes: 544 additions & 28 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "0.1.0",
"license": "Apache-2.0",
"scripts": {
"build": "npx parcel build src/index.html",
"build": "npx parcel build src/index.html && cp src/_redirects dist/_redirects",
"serve": "npx parcel serve src/index.html --no-cache",
"clean": "rm -rf dist/*",
"test": "echo \"Error: no test specified\" && exit 1",
Expand All @@ -15,6 +15,8 @@
],
"dependencies": {
"@babel/runtime": "^7.28.6",
"@carbon/icons": "^11.74.0",
"@carbon/react": "^1.100.0",
"@date-io/core": "^3.2.0",
"@date-io/date-fns": "^3.2.1",
"@emotion/react": "^11.14.0",
Expand Down
1 change: 1 addition & 0 deletions src/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
7 changes: 6 additions & 1 deletion src/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { createRoot } from "react-dom/client";
import Routes from "./route";
import { ThemeProvider } from "./context/ThemeContext";

const root = createRoot(document.getElementById("app"));
root.render(<Routes />);
root.render(
<ThemeProvider>
<Routes />
</ThemeProvider>,
);
135 changes: 135 additions & 0 deletions src/components/Assets/AssetDetailsModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React from "react";
import {
Modal,
StructuredListWrapper,
StructuredListHead,
StructuredListBody,
StructuredListRow,
StructuredListCell,
} from "@carbon/react";
import { formatDuration, bytesToString } from "../../util";
import SummaryRow from "./SummaryRow";

const AssetDetailsModal = ({ open, onClose, asset }) => {
if (!asset) return null;

const { properties = {}, labels = {}, ...rest } = asset;

return (
<Modal
open={open}
onRequestClose={onClose}
modalHeading={properties.name || rest.name || "Asset Details"}
passiveModal
size="lg"
>
<div style={{ marginBottom: "1.5rem" }}>
<h5 style={{ marginBottom: "0.5rem" }}>Overview</h5>
<StructuredListWrapper isCondensed ariaLabel="Asset overview">
<StructuredListBody>
<SummaryRow
label="Category"
value={properties.category || rest.category}
/>
<SummaryRow label="Type" value={rest.type} />
<SummaryRow label="Provider" value={properties.provider} />
<SummaryRow label="Service" value={properties.service} />
<SummaryRow label="Cluster" value={properties.cluster} />
<SummaryRow
label="Total Cost"
value={rest.totalCost ? `$${rest.totalCost.toFixed(6)}` : null}
/>
<SummaryRow
label="Duration"
value={rest.minutes ? formatDuration(rest.minutes) : null}
/>
<SummaryRow label="CPU Cores" value={rest.cpuCores} />
<SummaryRow
label="RAM"
value={rest.ramBytes ? bytesToString(rest.ramBytes) : null}
/>
<SummaryRow label="GPU Count" value={rest.gpuCount} />
<SummaryRow
label="Preemptible"
value={rest.preemptible ? "Yes" : "No"}
/>
</StructuredListBody>
</StructuredListWrapper>
</div>

<div style={{ marginBottom: "1.5rem" }}>
<h5 style={{ marginBottom: "0.5rem" }}>
Properties ({Object.keys(properties).length})
</h5>
<StructuredListWrapper isCondensed ariaLabel="Properties list">
<StructuredListHead>
<StructuredListRow head>
<StructuredListCell head>Key</StructuredListCell>
<StructuredListCell head>Value</StructuredListCell>
</StructuredListRow>
</StructuredListHead>
<StructuredListBody>
{Object.entries(properties).length > 0 ? (
Object.entries(properties).map(([key, value]) => (
<StructuredListRow key={key}>
<StructuredListCell
style={{ width: "30%", wordBreak: "break-all" }}
>
{key}
</StructuredListCell>
<StructuredListCell style={{ wordBreak: "break-all" }}>
{String(value)}
</StructuredListCell>
</StructuredListRow>
))
) : (
<StructuredListRow>
<StructuredListCell colSpan={2}>
No properties available
</StructuredListCell>
</StructuredListRow>
)}
</StructuredListBody>
</StructuredListWrapper>
</div>

<div style={{ marginBottom: "1.5rem" }}>
<h5 style={{ marginBottom: "0.5rem" }}>
Labels ({Object.keys(labels).length})
</h5>
<StructuredListWrapper isCondensed ariaLabel="Labels list">
<StructuredListHead>
<StructuredListRow head>
<StructuredListCell head>Key</StructuredListCell>
<StructuredListCell head>Value</StructuredListCell>
</StructuredListRow>
</StructuredListHead>
<StructuredListBody>
{Object.entries(labels).length > 0 ? (
Object.entries(labels).map(([key, value]) => (
<StructuredListRow key={key}>
<StructuredListCell
style={{ width: "30%", wordBreak: "break-all" }}
>
{key}
</StructuredListCell>
<StructuredListCell style={{ wordBreak: "break-all" }}>
{String(value)}
</StructuredListCell>
</StructuredListRow>
))
) : (
<StructuredListRow>
<StructuredListCell colSpan={2}>
No labels available
</StructuredListCell>
</StructuredListRow>
)}
</StructuredListBody>
</StructuredListWrapper>
</div>
</Modal>
);
};

export default AssetDetailsModal;
56 changes: 56 additions & 0 deletions src/components/Assets/AssetTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
DataTable,
Table,
TableHead,
TableRow,
TableHeader,
TableBody,
TableCell,
} from "@carbon/react";

const AssetTable = ({ headers, rows, formatters = {}, onRowClick }) => {
return (
<DataTable rows={rows} headers={headers} isSortable>
{({ rows, headers, getHeaderProps, getRowProps }) => (
<Table>
<TableHead>
<TableRow>
{headers.map((header) => {
const { key, ...headerProps } = getHeaderProps({ header });
return (
<TableHeader key={key} {...headerProps}>
{header.header}
</TableHeader>
);
})}
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => {
const { key, ...rowProps } = getRowProps({ row });
return (
<TableRow
key={key}
{...rowProps}
onClick={() => onRowClick && onRowClick(row.id)}
style={{ cursor: "pointer" }}
>
{row.cells.map((cell, index) => {
const headerKey = headers[index].key;
const formatter = formatters[headerKey];
const displayValue = formatter
? formatter(cell.value)
: cell.value;
return <TableCell key={cell.id}>{displayValue}</TableCell>;
})}
</TableRow>
);
})}
</TableBody>
</Table>
)}
</DataTable>
);
};

export default AssetTable;
18 changes: 18 additions & 0 deletions src/components/Assets/SummaryRow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { StructuredListRow, StructuredListCell } from "@carbon/react";

const SummaryRow = ({ label, value }) => {
if (value === undefined || value === null || value === "") return null;
return (
<StructuredListRow>
<StructuredListCell
className="bx--structured-list-content--nowrap"
style={{ fontWeight: "bold", width: "30%" }}
>
{label}
</StructuredListCell>
<StructuredListCell>{value}</StructuredListCell>
</StructuredListRow>
);
};

export default SummaryRow;
3 changes: 2 additions & 1 deletion src/components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Parser as HtmlToReactParser } from "html-to-react";

// Footer could be HTML, so we need to parse it.
const Footer = () => {
const content = '<div align="right"><br/>PLACEHOLDER_FOOTER_CONTENT</div>';
const content =
'<div align="right" style="padding-bottom: 20px;"><br/>PLACEHOLDER_FOOTER_CONTENT</div>';
const htmlToReactParser = new HtmlToReactParser();
const parsedContent = htmlToReactParser.parse(content);
return parsedContent;
Expand Down
24 changes: 10 additions & 14 deletions src/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import Breadcrumbs from "@mui/material/Breadcrumbs";
import Link from "@mui/material/Link";
import Typography from "@mui/material/Typography";
import { Heading, Breadcrumb, BreadcrumbItem, Link } from "@carbon/react";

const Header = (props) => {
const { title, breadcrumbs, headerTitle } = props;
Expand All @@ -15,22 +13,20 @@ const Header = (props) => {
marginTop: "10px",
}}
>
<Typography variant="h3" style={{ marginBottom: "10px" }}>
{headerTitle}
</Typography>
<Heading style={{ marginBottom: "10px" }}>{headerTitle}</Heading>
<div style={{ flex: "1 0 auto" }}>
{title && <Typography variant="h4">{title}</Typography>}
{title}
{breadcrumbs && breadcrumbs.length > 0 && (
<Breadcrumbs aria-label="breadcrumb">
<Breadcrumb aria-label="breadcrumb">
{breadcrumbs.slice(0, breadcrumbs.length - 1).map((b) => (
<Link color="inherit" href={b.href} key={b.name}>
{b.name}
</Link>
<BreadcrumbItem key={b.name}>
<Link href={b.href}>{b.name}</Link>
</BreadcrumbItem>
))}
<Typography color="textPrimary">
<BreadcrumbItem>
{breadcrumbs[breadcrumbs.length - 1].name}
</Typography>
</Breadcrumbs>
</BreadcrumbItem>
</Breadcrumb>
)}
</div>
<div style={{ flex: "0 0 auto" }}>{props.children}</div>
Expand Down
55 changes: 0 additions & 55 deletions src/components/Nav/NavItem.js

This file was deleted.

Loading