Skip to content

Commit

Permalink
Make form controlled
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffxy committed Jan 23, 2025
1 parent 2389eda commit 8eec87a
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 5 deletions.
139 changes: 139 additions & 0 deletions ui/src/components/CreateEditVdbeForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { useState } from "react";
import InsetPanel from "./InsetPanel";
import CheckCircleOutlineRoundedIcon from "@mui/icons-material/CheckCircleOutlineRounded";
import FormControl from "@mui/material/FormControl";
import InputLabel from "@mui/material/InputLabel";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
import InputAdornment from "@mui/material/InputAdornment";
import Button from "@mui/material/Button";
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
import EditRoundedIcon from "@mui/icons-material/EditRounded";
import Select from "@mui/material/Select";
import VdbeView from "./VdbeView";
import "./styles/CreateEditVdbeForm.css";

function CreateEditFormFields({ vdbe, setVdbe }) {
const onStalenessChange = (event) => {
const maxStalenessMins = parseInt(event.target.value);
if (isNaN(maxStalenessMins)) {
setVdbe({ ...vdbe, max_staleness_ms: null });
return;
}
setVdbe({ ...vdbe, max_staleness_ms: maxStalenessMins * 60 * 1000 });
};

const onSloChange = (event) => {
const sloMs = parseInt(event.target.value);
if (isNaN(sloMs)) {
setVdbe({ ...vdbe, p90_latency_slo_ms: null });
return;
}
setVdbe({ ...vdbe, p90_latency_slo_ms: sloMs });
};

return (
<div className="cev-form-fields">
<TextField
variant="outlined"
label="Name"
className="cev-field"
value={vdbe.name}
onChange={(event) => setVdbe({ ...vdbe, name: event.target.value })}
/>
<TextField
variant="outlined"
label="Maximum Staleness (Freshness Constraint)"
className="cev-field"
slotProps={{
input: {
endAdornment: (
<InputAdornment position="end">minutes</InputAdornment>
),
},
}}
value={
vdbe.max_staleness_ms != null ? vdbe.max_staleness_ms / 60000 : ""
}
onChange={onStalenessChange}
/>
<TextField
variant="outlined"
label="Maximum p90 Latency (Performance SLO)"
className="cev-field"
slotProps={{
input: {
endAdornment: (
<InputAdornment position="end">milliseconds</InputAdornment>
),
},
}}
value={vdbe.p90_latency_slo_ms != null ? vdbe.p90_latency_slo_ms : ""}
onChange={onSloChange}
/>
<FormControl fullWidth>
<InputLabel id="cev-query-interface">Query Interface</InputLabel>
<Select
labelId="cev-query-interface"
variant="outlined"
label="Query Interface"
className="cev-field"
value={vdbe.interface}
onChange={(event) =>
setVdbe({ ...vdbe, interface: event.target.value })
}
>
<MenuItem value="common">Common SQL</MenuItem>
<MenuItem value="postgresql">PostgreSQL SQL</MenuItem>
<MenuItem value="athena">Athena SQL</MenuItem>
</Select>
</FormControl>
</div>
);
}

function getEmptyVdbe() {
return {
name: null,
max_staleness_ms: null,
p90_latency_slo_ms: null,
queryInterface: "postgresql",
tables: [],
};
}

function CreateEditVdbeForm({ isEdit, currentVdbe }) {
const [vdbe, setVdbe] = useState(
currentVdbe != null ? currentVdbe : getEmptyVdbe(),
);

return (
<InsetPanel className="create-edit-vdbe-form">
<h2>
{isEdit ? (
<EditRoundedIcon style={{ marginRight: "10px" }} />
) : (
<AddCircleOutlineIcon style={{ marginRight: "10px" }} />
)}
{isEdit ? "Edit VDBE" : "Create VDBE"}
</h2>
<div className="cev-form-body">
<CreateEditFormFields vdbe={vdbe} setVdbe={setVdbe} />
<div className="cev-preview">
<VdbeView vdbe={vdbe} highlight={{}} editable={false} />
</div>
</div>
<div className="cev-buttons">
<Button variant="outlined">Cancel</Button>
<Button
variant="contained"
startIcon={<CheckCircleOutlineRoundedIcon />}
>
{isEdit ? "Save" : "Create"}
</Button>
</div>
</InsetPanel>
);
}

export default CreateEditVdbeForm;
7 changes: 7 additions & 0 deletions ui/src/components/InsetPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import "./styles/InsetPanel.css";

function InsetPanel({ children, className }) {
return <div className={`inset-panel-wrap ${className}`}>{children}</div>;
}

export default InsetPanel;
38 changes: 33 additions & 5 deletions ui/src/components/VdbeView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
} from "../highlight";

function formatMilliseconds(milliseconds) {
if (milliseconds == null) {
return null;
}

const precision = 2;
if (milliseconds >= 1000 * 60 * 60) {
// Use hours.
Expand All @@ -27,19 +31,30 @@ function formatMilliseconds(milliseconds) {
}

function formatFreshness(maxStalenessMs) {
if (maxStalenessMs == null) {
return null;
}

if (maxStalenessMs === 0) {
return "No staleness";
}
return `Staleness ≤ ${formatMilliseconds(maxStalenessMs)}`;
}

function formatDialect(queryInterface) {
if (queryInterface == null) {
return null;
}

if (queryInterface === "postgresql") {
return "PostgreSQL SQL";
} else if (queryInterface === "athena") {
return "Athena SQL";
} else if (queryInterface === "common") {
return "SQL-99";
} else {
console.error("Unknown", queryInterface);
return null;
}
}

Expand All @@ -60,7 +75,13 @@ function EditControls({ onEditClick, onDeleteClick }) {
);
}

function VdbeView({ vdbe, highlight, onTableHoverEnter, onTableHoverExit }) {
function VdbeView({
vdbe,
highlight,
onTableHoverEnter,
onTableHoverExit,
editable,
}) {
const vengName = vdbe.name;
const tables = vdbe.tables;
const freshness = formatFreshness(vdbe.max_staleness_ms);
Expand All @@ -74,13 +95,20 @@ function VdbeView({ vdbe, highlight, onTableHoverEnter, onTableHoverExit }) {
>
<div className="vdbe-db-wrap">
<DbCylinder color="green">{vengName}</DbCylinder>
<EditControls onEditClick={() => {}} onDeleteClick={() => {}} />
{editable && (
<EditControls onEditClick={() => {}} onDeleteClick={() => {}} />
)}
</div>
<div class="vdbe-view-props">
<ul>
<li>🌿: {freshness}</li>
<li>⏱️: p90 Query Latency ≤ {peakLatency}</li>
<li>🗣: {dialect}</li>
<li>🌿: {freshness != null ? freshness : "-----"}</li>
<li>
⏱️:{" "}
{peakLatency != null
? `p90 Query Latency ≤ ${peakLatency}`
: "-----"}
</li>
<li>🗣: {dialect != null ? dialect : "-----"}</li>
</ul>
</div>
<ExpandableTableSet>
Expand Down
1 change: 1 addition & 0 deletions ui/src/components/VirtualInfraView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function VirtualInfraView({
onTableHoverEnter={onTableHoverEnter}
onTableHoverExit={onTableHoverExit}
vdbe={vdbe}
editable={true}
/>
))}
</div>
Expand Down
42 changes: 42 additions & 0 deletions ui/src/components/styles/CreateEditVdbeForm.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.create-edit-vdbe-form {
display: flex;
flex-direction: column;
}

.create-edit-vdbe-form h2 {
margin-bottom: 20px;
}

.cev-form-body {
display: flex;
flex-direction: row;
}

.cev-form-fields {
display: flex;
flex-direction: column;
flex-grow: 3;
margin-bottom: 10px;
}

.cev-form-fields .cev-field {
margin: 0 0 15px 0;
}

.cev-preview {
flex-grow: 2;
display: flex;
justify-content: center;
margin-top: -20px;
}

.cev-buttons {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}

.cev-buttons button {
margin-left: 20px;
}
15 changes: 15 additions & 0 deletions ui/src/components/styles/InsetPanel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.inset-panel-wrap {
display: flex;
flex-direction: column;
background-color: #f7f8f8;
border-radius: 19px;
padding: 29px;
margin-bottom: 19px;
}

.inset-panel-wrap h2 {
display: flex;
align-items: center;
font-size: 1.5em;
margin: 0 0 5px 0;
}

0 comments on commit 8eec87a

Please sign in to comment.