Skip to content

Commit

Permalink
feat: make the add topology form wizard
Browse files Browse the repository at this point in the history
Closes #1518
  • Loading branch information
mainawycliffe committed Dec 4, 2023
1 parent 8557ce4 commit de607cd
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 4 deletions.
67 changes: 63 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"recharts": "2.1.12",
"tailwindcss": "^3.0.24",
"timeago.js": "^4.0.2",
"type-fest": "^4.8.2",
"typescript": "^4.7.2",
"uuid": "^8.3.2",
"web-vitals": "^2.1.4",
Expand Down
34 changes: 34 additions & 0 deletions src/components/Topology/Settings/AddTopologyResource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { TupleToUnion } from "type-fest";
import AddTopologyOptionsList, {
createTopologyOptions
} from "./StepsForms/AddTopologyOptionsList";
import { useState } from "react";
import TopologyResourceForm from "./StepsForms/TopologyResourceForm";

type AddTopologyResourceProps = {
onSuccess: () => void;
};

export default function AddTopologyResource({
onSuccess
}: AddTopologyResourceProps) {
const [selectedOption, setSelectedOption] = useState<
TupleToUnion<typeof createTopologyOptions> | undefined
>();

return (
<div className="flex flex-col ">
{selectedOption ? (
<TopologyResourceForm
selectedOption={selectedOption}
onSuccess={onSuccess}
onBack={() => setSelectedOption(undefined)}
/>
) : (
<AddTopologyOptionsList
onSelectOption={(options) => setSelectedOption(options)}
/>
)}
</div>
);
}
19 changes: 19 additions & 0 deletions src/components/Topology/Settings/EditTopologyResource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import TopologyResourceForm, {
TopologyResource
} from "./StepsForms/TopologyResourceForm";

type EditTopologyResourceProps = {
onSuccess: () => void;
topologyResource: TopologyResource;
};

export default function EditTopologyResource({
onSuccess,
topologyResource
}: EditTopologyResourceProps) {
return (
<div className="flex flex-col ">
<TopologyResourceForm onSuccess={onSuccess} topology={topologyResource} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { TupleToUnion } from "type-fest";

export const createTopologyOptions = [
"Kubernetes",
"Flux",
"Prometheus",
"Custom"
] as const;

type AddTopologyOptionsListProps = {
onSelectOption: (options: TupleToUnion<typeof createTopologyOptions>) => void;
};

export default function AddTopologyOptionsList({
onSelectOption
}: AddTopologyOptionsListProps) {
return (
<div className="flex flex-wrap p-2">
{createTopologyOptions.map((item) => {
return (
<div className="flex flex-col w-1/5 p-2" key={item}>
<div
role="button"
className="flex flex-col items-center space-y-2 justify-center p-2 border border-gray-300 hover:border-blue-200 hover:bg-gray-100 rounded-md text-center h-20"
onClick={(e) => {
onSelectOption(item);
}}
>
<div>{item}</div>
</div>
</div>
);
})}
</div>
);
}
150 changes: 150 additions & 0 deletions src/components/Topology/Settings/StepsForms/TopologyResourceForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { Form, Formik } from "formik";
import FormikTextInput from "../../../Forms/Formik/FormikTextInput";
import { FormikCodeEditor } from "../../../Forms/Formik/FormikCodeEditor";
import { Button } from "../../../Button";
import DeleteResource from "../../../SchemaResourcePage/Delete/DeleteResource";
import { schemaResourceTypes } from "../../../SchemaResourcePage/resourceTypes";
import clsx from "clsx";
import { createTopologyOptions } from "./AddTopologyOptionsList";
import { TupleToUnion } from "type-fest";
import { useCallback, useMemo } from "react";
import {
useSettingsCreateResource,
useSettingsUpdateResource
} from "../../../../api/query-hooks/mutations/useSettingsResourcesMutations";
import { FaSpinner } from "react-icons/fa";

export type TopologyResource = {
id: string;
name: string;
namespace: string;
labels: Record<string, string>;
spec: Record<string, any>;
};

type TopologyResourceFormProps = {
topology?: TopologyResource;
selectedOption?: TupleToUnion<typeof createTopologyOptions>;
onBack?: () => void;
footerClassName?: string;
onSuccess?: () => void;
isModal?: boolean;
};

export default function TopologyResourceForm({
topology,
onBack,
selectedOption,
footerClassName = "bg-gray-100 p-4",
onSuccess = () => {},
isModal = false
}: TopologyResourceFormProps) {
const resourceInfo = schemaResourceTypes.find(
(item) => item.name === "Topology"
);

const { mutate: createResource, isLoading: isCreatingResource } =
useSettingsCreateResource(resourceInfo!, onSuccess);

const { mutate: updateResource, isLoading: isUpdatingResource } =
useSettingsUpdateResource(resourceInfo!, topology, isModal);

const isLoading = isCreatingResource || isUpdatingResource;

const initialValues: TopologyResource = useMemo(() => {
if (topology) {
return topology;
}
// the spec here is determined by the selected option
// todo: pull the specs for each option from the backend and use that to
// determine the initial values for the spec field
return {
id: "",
name: "",
namespace: "",
labels: {},
spec: {}
};
}, [topology]);

const handleSubmit = useCallback(
(values: TopologyResource) => {
if (topology) {
updateResource(values);
} else {
createResource(values);
}
},
[createResource, topology, updateResource]
);

const saveButtonText = useMemo(() => {
if (topology) {
return isLoading ? "Updating" : "Update";
}
return isLoading ? "Saving" : "Save";
}, [isLoading, topology]);

return (
<div className="flex flex-col ">
<Formik
initialValues={initialValues}
onSubmit={(values) => handleSubmit(values)}
>
{({ isValid, handleSubmit }) => (
<Form onSubmit={handleSubmit}>
<FormikTextInput
label="Name"
name="name"
placeholder="Topology name"
/>
<FormikTextInput
label="Namespace"
name="namespace"
placeholder="Namespace"
defaultValue={"default"}
/>
<FormikCodeEditor label="Labels" fieldName="labels" format="json" />
<FormikCodeEditor label="Spec" fieldName="spec" format="yaml" />
<div className={`flex flex-col ${footerClassName}`}>
<div className="flex flex-1 flex-row items-center space-x-4 justify-end">
{!topology?.id && (
<div className="flex flex-1 flex-row">
<Button
type="button"
text="Back"
className="btn-default btn-btn-secondary-base btn-secondary"
onClick={onBack}
/>
</div>
)}
{!!topology?.id && (
<DeleteResource
resourceId={topology?.id}
resourceInfo={
schemaResourceTypes.find(
({ name }) => name === "Topology"
)!
}
/>
)}

<Button
disabled={!isValid}
type="submit"
icon={
isLoading ? (
<FaSpinner className="animate-spin" />
) : undefined
}
text={saveButtonText}
className={clsx("btn-primary")}
/>
</div>
</div>
</Form>
)}
</Formik>
</div>
);
}
Loading

0 comments on commit de607cd

Please sign in to comment.