diff --git a/src/components/Authentication/Kratos/KratosUserProfileDropdown.tsx b/src/components/Authentication/Kratos/KratosUserProfileDropdown.tsx index 081fe09e8..553792f7f 100644 --- a/src/components/Authentication/Kratos/KratosUserProfileDropdown.tsx +++ b/src/components/Authentication/Kratos/KratosUserProfileDropdown.tsx @@ -6,9 +6,14 @@ import { ClickableSvg } from "../../../ui/ClickableSvg/ClickableSvg"; import { VersionInfo } from "../../VersionInfo/VersionInfo"; import KratosLogoutButton from "./KratosLogoutButton"; -export function KratosUserProfileDropdown() { - const { user } = useUser(); +type UserProfileDropdownProps = { + openKubeConfigModal: () => void; +}; +export function KratosUserProfileDropdown({ + openKubeConfigModal +}: UserProfileDropdownProps) { + const { user } = useUser(); const userNavigation = [{ name: "Your Profile", href: "/profile-settings" }]; return ( @@ -56,6 +61,14 @@ export function KratosUserProfileDropdown() { ))} + + + diff --git a/src/components/KubeConfig/AddKubeConfigModal.tsx b/src/components/KubeConfig/AddKubeConfigModal.tsx new file mode 100644 index 000000000..07c468fe8 --- /dev/null +++ b/src/components/KubeConfig/AddKubeConfigModal.tsx @@ -0,0 +1,82 @@ +import { useUser } from "@flanksource-ui/context"; +import { Button } from "@flanksource-ui/ui/Buttons/Button"; +import CodeBlock from "@flanksource-ui/ui/Code/CodeBlock"; +import { Modal } from "@flanksource-ui/ui/Modal"; +import StepperList from "@flanksource-ui/ui/StepperList"; +import { useMemo } from "react"; +import { IoMdDownload } from "react-icons/io"; + +const downloadKubeConfigURL = `/api/kubeconfig`; + +type AddKubeConfigModalProps = { + isOpen: boolean; + onClose: () => void; +}; + +export default function AddKubeConfigModal({ + isOpen, + onClose +}: AddKubeConfigModalProps) { + const { backendUrl } = useUser(); + + const kubeConfigFilename = useMemo( + () => + backendUrl + ?.replaceAll("https://", "") + .replaceAll(".flanksource.com", "") + .replaceAll("/", "") + "-kubeconfig.yaml", + [backendUrl] + ); + + return ( + +
+
+
+

Instructions

+ + + +
, +
+ Apply the kubeconfig file to your kubectl configuration{" "} + +
, +
+ + Learn more + {" "} + on how to use the Kubernetes Interface to Mission Control +
+ ]} + /> +
+
+ +
+
+
+ ); +} diff --git a/src/components/Users/UserProfile.tsx b/src/components/Users/UserProfile.tsx index 2238c6994..e61172b44 100644 --- a/src/components/Users/UserProfile.tsx +++ b/src/components/Users/UserProfile.tsx @@ -1,21 +1,44 @@ import { OrganizationSwitcher, UserButton } from "@clerk/nextjs"; +import { useState } from "react"; +import { IoMdDownload } from "react-icons/io"; import { KratosUserProfileDropdown } from "../Authentication/Kratos/KratosUserProfileDropdown"; import useDetermineAuthSystem from "../Authentication/useDetermineAuthSystem"; +import AddKubeConfigModal from "../KubeConfig/AddKubeConfigModal"; export function UserProfileDropdown() { const authSystem = useDetermineAuthSystem(); + const [isDownloadKubeConfigModalOpen, setIsDownloadKubeConfigModalOpen] = + useState(false); - return authSystem === "clerk" ? ( -
- + {authSystem === "clerk" ? ( +
+ + + + } + onClick={() => setIsDownloadKubeConfigModalOpen(true)} + /> + + +
+ ) : ( + setIsDownloadKubeConfigModalOpen(true)} + /> + )} + setIsDownloadKubeConfigModalOpen(false)} /> - -
- ) : ( - + ); } diff --git a/src/ui/StepperList.stories.tsx b/src/ui/StepperList.stories.tsx new file mode 100644 index 000000000..f40b146ef --- /dev/null +++ b/src/ui/StepperList.stories.tsx @@ -0,0 +1,22 @@ +import { Meta, StoryFn } from "@storybook/react"; +import StepperList from "./StepperList"; + +export default { + title: "ui/StepperList", + component: StepperList +} satisfies Meta; + +const Template: StoryFn = (args) => ( + +); + +export const Variant1 = Template.bind({}); +Variant1.args = { + items: [ +
Some list item
, +
+ Some list item +
, +
Some other list item
+ ] +}; diff --git a/src/ui/StepperList.tsx b/src/ui/StepperList.tsx new file mode 100644 index 000000000..c7858518f --- /dev/null +++ b/src/ui/StepperList.tsx @@ -0,0 +1,38 @@ +import React from "react"; + +type StepperListProps = { + items: React.ReactNode[]; + showStepLabel?: boolean; +}; + +export default function StepperList({ + items, + showStepLabel = false +}: StepperListProps) { + return ( + <> + {items.map((item, index) => ( +
+
+
+
+

{index + 1}

+
+
+ {index !== items.length - 1 && ( +
+ )} +
+
+ {showStepLabel && ( +
+ Step {index + 1} +
+ )} +
{item}
+
+
+ ))} + + ); +}