Skip to content

Commit

Permalink
Merge pull request #1536 from flanksource/1518-add-topology-page-wizard
Browse files Browse the repository at this point in the history
feat: make the add topology form wizard
  • Loading branch information
moshloop authored Dec 7, 2023
2 parents a1e34f7 + df12dcf commit 6b3121e
Show file tree
Hide file tree
Showing 12 changed files with 533 additions and 13 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
8 changes: 4 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BsLink, BsToggles } from "react-icons/bs";
import { FaBell, FaTasks } from "react-icons/fa";
import { HiUser } from "react-icons/hi";
import { ImLifebuoy } from "react-icons/im";
import { MdOutlineSupportAgent } from "react-icons/md";
import { VscJson } from "react-icons/vsc";
import {
BrowserRouter,
Expand All @@ -19,6 +20,7 @@ import {
import ReactTooltip from "react-tooltip";
import { Canary } from "./components";
import { withAccessCheck } from "./components/AccessCheck/AccessCheck";
import AgentsPage from "./components/Agents/AgentPage";
import AuthProviderWrapper from "./components/Authentication/AuthProviderWrapper";
import { ErrorBoundary } from "./components/ErrorBoundary";
import { LogsIcon } from "./components/Icons/LogsIcon";
Expand Down Expand Up @@ -57,18 +59,16 @@ import { ConnectionsPage } from "./pages/Settings/ConnectionsPage";
import { EventQueueStatusPage } from "./pages/Settings/EventQueueStatus";
import { FeatureFlagsPage } from "./pages/Settings/FeatureFlagsPage";
import { LogBackendsPage } from "./pages/Settings/LogBackendsPage";
import { PlaybooksListPage } from "./pages/playbooks/PlaybooksList";
import { TopologyCardPage } from "./pages/TopologyCard";
import { UsersPage } from "./pages/UsersPage";
import { ConfigDetailsInsightsPage } from "./pages/config/ConfigDetailsInsightsPage";
import { ConfigInsightsPage } from "./pages/config/ConfigInsightsList";
import { HealthPage } from "./pages/health";
import PlaybookRunsPage from "./pages/playbooks/PlaybookRuns";
import PlaybookRunsDetailsPage from "./pages/playbooks/PlaybookRunsDetails";
import { PlaybooksListPage } from "./pages/playbooks/PlaybooksList";
import { features } from "./services/permissions/features";
import { stringSortHelper } from "./utils/common";
import { MdOutlineSupportAgent } from "react-icons/md";
import AgentsPage from "./components/Agents/AgentPage";
import { TopologyCardPage } from "./pages/TopologyCard";

export type NavigationItems = {
name: string;
Expand Down
8 changes: 8 additions & 0 deletions src/components/SchemaResourcePage/SchemaResourceEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import HealthSpecEditor from "../SpecEditor/HealthSpecEditor";
import { Button } from "../Button";
import DeleteResource from "./Delete/DeleteResource";
import { HealthCheckEdit } from "../Canary/HealthCheckEdit";
import EditTopologyResource from "../Topology/Settings/EditTopologyResource";

const CodeEditor = dynamic(
() => import("../CodeEditor").then((m) => m.CodeEditor),
Expand Down Expand Up @@ -232,6 +233,13 @@ export function SchemaResourceEdit({
resourceValue={defaultValues}
/>
</div>
) : table === "topologies" ? (
<div className="flex-col flex flex-1 overflow-y-auto">
<EditTopologyResource
onSuccess={() => {}}
topologyResource={defaultValues as any}
/>
</div>
) : (
<form
className="flex flex-col flex-1 overflow-y-auto"
Expand Down
26 changes: 21 additions & 5 deletions src/components/SchemaResourcePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BreadcrumbNav, BreadcrumbRoot } from "../BreadcrumbNav";
import ErrorPage from "../Errors/ErrorPage";
import { Head } from "../Head/Head";
import { SearchLayout } from "../Layout";
import AddTopologyResourceModal from "../Topology/Settings/AddTopologyResourceModal";
import AddSchemaResourceModal from "./AddSchemaResourceModal";
import { SchemaResourceList } from "./SchemaResourceList";
import { SchemaResourceType } from "./resourceTypes";
Expand All @@ -29,13 +30,28 @@ export function SchemaResourcePage({
title={
<BreadcrumbNav
list={[
<BreadcrumbRoot link={`/settings/${resourceInfo.table}`}>
<BreadcrumbRoot
key="root"
link={`/settings/${resourceInfo.table}`}
>
{name}
</BreadcrumbRoot>,
<AddSchemaResourceModal
onClose={() => refetch()}
resourceInfo={resourceInfo!}
/>
// for topology, we want to show the add topology resource modal,
// which supports being linked to directly via the url
...(resourceInfo.name === "Topology"
? [
<AddTopologyResourceModal
key={"add-resource"}
onClose={() => refetch()}
/>
]
: [
<AddSchemaResourceModal
key={"add-resource"}
onClose={() => refetch()}
resourceInfo={resourceInfo!}
/>
])
]}
/>
}
Expand Down
47 changes: 47 additions & 0 deletions src/components/Topology/Settings/AddTopologyResource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { TupleToUnion } from "type-fest";
import AddTopologyOptionsList, {
createTopologyOptions
} from "./StepsForms/AddTopologyOptionsList";
import { useEffect, useState } from "react";
import TopologyResourceForm from "./StepsForms/TopologyResourceForm";

type AddTopologyResourceProps = {
onSuccess: () => void;
isModal?: boolean;
setModalTitle?: (title: string) => void;
};

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

useEffect(() => {
if (selectedOption) {
setModalTitle(`Create a ${selectedOption.toLocaleLowerCase()} topology`);
} else {
setModalTitle("Create Topology");
}
}, [selectedOption, setModalTitle]);

return (
<div className="flex flex-col flex-1 gap-2">
{selectedOption ? (
<TopologyResourceForm
selectedOption={selectedOption}
onSuccess={onSuccess}
onBack={() => setSelectedOption(undefined)}
isModal={isModal}
/>
) : (
<AddTopologyOptionsList
onSelectOption={(options) => setSelectedOption(options)}
/>
)}
</div>
);
}
56 changes: 56 additions & 0 deletions src/components/Topology/Settings/AddTopologyResourceModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { AiFillPlusCircle } from "react-icons/ai";
import { useSearchParams } from "react-router-dom";
import AddTopologyResource from "./AddTopologyResource";
import { Modal } from "../../Modal";
import { useState } from "react";

type Props = {
onClose?: () => void;
};

export default function AddTopologyResourceModal({
onClose = () => {}
}: Props) {
const [searchParams, setSearchParams] = useSearchParams();

const [modalTitle, setModalTitle] = useState("Create Topology");

const isModalOpen = searchParams.get("openCreateForm") === "true";

const setModalIsOpen = (isOpen: boolean) => {
if (isOpen) {
searchParams.set("openCreateForm", "true");
} else {
searchParams.delete("openCreateForm");
}
setSearchParams(searchParams);
};

return (
<>
<button type="button" className="" onClick={() => setModalIsOpen(true)}>
<AiFillPlusCircle size={32} className="text-blue-600" />
</button>

<Modal
open={isModalOpen}
onClose={() => {
setModalIsOpen(false);
onClose();
}}
bodyClass="flex flex-col flex-1 overflow-y-auto"
size="full"
title={modalTitle}
>
<AddTopologyResource
isModal
onSuccess={() => {
setModalIsOpen(false);
onClose();
}}
setModalTitle={setModalTitle}
/>
</Modal>
</>
);
}
26 changes: 26 additions & 0 deletions src/components/Topology/Settings/EditTopologyResource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import TopologyResourceForm, {
TopologyResource
} from "./StepsForms/TopologyResourceForm";

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

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

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-col gap-2">
<div className="flex flex-wrap p-2">
{createTopologyOptions.map((item) => {
return (
<div className="flex flex-col w-1/4 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);
}}
>
<Icon name={item.toLowerCase()} />
<div>{item}</div>
</div>
</div>
);
})}
</div>
</div>
);
}
Loading

0 comments on commit 6b3121e

Please sign in to comment.