From a02e73e2a4d4a8d0a4da56bf92bd96bcf6cd9966 Mon Sep 17 00:00:00 2001 From: Meet Date: Thu, 19 Sep 2024 01:01:18 +0530 Subject: [PATCH] chore: refactor frontend and UI improvements --- .../components/features/FormLabelToolTip.tsx | 36 ++++++ .../src/components/features/TtlFormLabel.tsx | 32 +----- .../AzureEntraIdInputForm.tsx | 105 ++++++++++++------ .../CreateDynamicSecretForm.tsx | 37 +----- 4 files changed, 115 insertions(+), 95 deletions(-) create mode 100644 frontend/src/components/features/FormLabelToolTip.tsx diff --git a/frontend/src/components/features/FormLabelToolTip.tsx b/frontend/src/components/features/FormLabelToolTip.tsx new file mode 100644 index 000000000..584c47aae --- /dev/null +++ b/frontend/src/components/features/FormLabelToolTip.tsx @@ -0,0 +1,36 @@ +import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { FormLabel, Tooltip } from "../v2"; + +// To give users example of possible values of TTL +export const FormLabelToolTip = ({ label, linkToMore, content }: { label: string, linkToMore: string, content: string }) => ( +
+ + {content}{" "} + + More + + + } + > + + + } + /> +
+); diff --git a/frontend/src/components/features/TtlFormLabel.tsx b/frontend/src/components/features/TtlFormLabel.tsx index 14382abb4..5278feec7 100644 --- a/frontend/src/components/features/TtlFormLabel.tsx +++ b/frontend/src/components/features/TtlFormLabel.tsx @@ -1,36 +1,12 @@ -import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import { FormLabel, Tooltip } from "../v2"; +import { FormLabelToolTip } from "./FormLabelToolTip"; // To give users example of possible values of TTL export const TtlFormLabel = ({ label }: { label: string }) => (
- - 1m, 2h, 3d.{" "} - - More - - - } - > - - - } + content="1m, 2h, 3d. " + linkToMore="https://github.com/vercel/ms?tab=readme-ov-file#examples" />
); diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/AzureEntraIdInputForm.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/AzureEntraIdInputForm.tsx index ef63c0c07..49d86d235 100644 --- a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/AzureEntraIdInputForm.tsx +++ b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/AzureEntraIdInputForm.tsx @@ -1,13 +1,13 @@ import { useState } from "react"; import { Controller, useForm } from "react-hook-form"; -import { faWarning } from "@fortawesome/free-solid-svg-icons"; +import { faCheckCircle, faWarning } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import ms from "ms"; import { z } from "zod"; -import ListBoxMultiple from "@app/components/basic/ListboxMultiple"; import { TtlFormLabel } from "@app/components/features"; +import { FormLabelToolTip } from "@app/components/features/FormLabelToolTip"; import { createNotification } from "@app/components/notifications"; import { Button, @@ -15,11 +15,19 @@ import { Input, Spinner, } from "@app/components/v2"; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@app/components/v2/Dropdown/Dropdown"; import { useCreateDynamicSecret } from "@app/hooks/api"; import { useGetDynamicSecretProviderData } from "@app/hooks/api/dynamicSecret/queries"; import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types"; +import { AzureEntraIdSetup } from "./AzureEntraIdSetup"; + const formSchema = z.object({ + selectedUsers: z.array(z.object({ + id: z.string().min(1), + name: z.string().min(1), + email: z.string().min(1), + })), provider: z.object({ tenantId: z.string().min(1), }), @@ -55,12 +63,6 @@ type Props = { environment: string; }; -type EntraIDUser = { - name: string; - id: string; - email: string; -}; - export const AzureEntraIdInputForm = ({ onCompleted, onCancel, @@ -77,25 +79,11 @@ export const AzureEntraIdInputForm = ({ resolver: zodResolver(formSchema) }); const tenantId = watch("provider.tenantId"); - const [selectedUsers, setSelectedUsers] = useState([]); - const { data, isLoading, isFetched, isError, isFetching } = useGetDynamicSecretProviderData({ dataFetchType: "Users", provider: {type: DynamicSecretProviders.AzureEntraId, inputs: { userId : "unused", email: "unused", tenantId }}, enabled: !!tenantId }); - const mappedUserList = data?.map((user) => ({ - primaryText: user.name, - secondaryText: user.email, - id: user.id, - })); - const handleChange = (selected: { primaryText: string; secondaryText: string; id: string }[]) => { - // Map back to EntraIDUser based on the id or other attributes - const selectedMappedUsers = data?.filter(user => - selected.some(selectedItem => selectedItem.id === user.id) - ); - if(selectedMappedUsers){ - setSelectedUsers(selectedMappedUsers); - } - }; + const [onSetup, setOnSetup] = useState(true); + const { data, isLoading, isFetched, isError, isFetching } = useGetDynamicSecretProviderData({ dataFetchType: "Users", provider: { type: DynamicSecretProviders.AzureEntraId, inputs: { userId: "unused", email: "unused", tenantId } }, enabled: !!tenantId }); const createDynamicSecret = useCreateDynamicSecret(); - const handleCreateDynamicSecret = async ({ name, provider, maxTTL, defaultTTL }: TForm) => { + const handleCreateDynamicSecret = async ({ name, selectedUsers, provider, maxTTL, defaultTTL }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isLoading) return; try { @@ -121,7 +109,12 @@ export const AzureEntraIdInputForm = ({ return (
-
+ {onSetup && { setOnSetup(false); }} + onCancel={onCancel} + /> + } + {!onSetup &&
@@ -192,7 +185,7 @@ export const AzureEntraIdInputForm = ({ )} - + />
@@ -202,15 +195,55 @@ export const AzureEntraIdInputForm = ({ Select Users
-
+
{ - tenantId && !isError && !isFetching && isFetched && data && mappedUserList && - - selectedUsers.some(selectedUser => selectedUser.id === user.id) + tenantId && !isError && !isFetching && isFetched && data && + ( + } + isRequired + isError={Boolean(error)} + errorText={error?.message} + > + + + + + + {data.map((user) => { + const ids = value?.map((selectedUser) => selectedUser.id) + const isChecked = ids?.includes(user.id); + return ( + { + evt.preventDefault(); + onChange( + isChecked + ? value?.filter((el) => el.id !== user.id) + : [...(value || []), user] + ); + }} + key={`create-policy-members-${user.id}`} + iconPos="right" + icon={isChecked && } + > + {user.name}
{`(${user.email})`} +
+ ); + })} +
+
+
)} - onChange={handleChange} - data={mappedUserList} /> } { @@ -230,11 +263,15 @@ export const AzureEntraIdInputForm = ({ +
+ }
); }; diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx index 6cbbbee60..a28544a33 100644 --- a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx +++ b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/CreateDynamicSecretForm.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { DiRedis } from "react-icons/di"; -import { SiApachecassandra, SiElasticsearch, SiMongodb, SiRabbitmq } from "react-icons/si"; +import { SiApachecassandra, SiElasticsearch, SiMicrosoftazure, SiMongodb, SiRabbitmq } from "react-icons/si"; import { faAws } from "@fortawesome/free-brands-svg-icons"; import { faDatabase } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -12,7 +12,6 @@ import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types"; import { AwsElastiCacheInputForm } from "./AwsElastiCacheInputForm"; import { AwsIamInputForm } from "./AwsIamInputForm"; import { AzureEntraIdInputForm } from "./AzureEntraIdInputForm"; -import { AzureEntraIdSetup } from "./AzureEntraIdSetup"; import { CassandraInputForm } from "./CassandraInputForm"; import { ElasticSearchInputForm } from "./ElasticSearchInputForm"; import { MongoAtlasInputForm } from "./MongoAtlasInputForm"; @@ -31,7 +30,6 @@ type Props = { enum WizardSteps { SelectProvider = "select-provider", - ProviderSetup = "provider-setup", ProviderInputs = "provider-inputs" } @@ -82,10 +80,9 @@ const DYNAMIC_SECRET_LIST = [ title: "RabbitMQ" }, { - icon: , + icon: , provider: DynamicSecretProviders.AzureEntraId, title: "Azure Entra ID", - hasSetupStep: true } ]; @@ -123,7 +120,7 @@ export const CreateDynamicSecretForm = ({ >
Select a service to connect to:
- {DYNAMIC_SECRET_LIST.map(({ icon, provider, title, hasSetupStep }) => ( + {DYNAMIC_SECRET_LIST.map(({ icon, provider, title }) => (
{ setSelectedProvider(provider); - if(hasSetupStep){ - setWizardStep(WizardSteps.ProviderSetup); - } else { - setWizardStep(WizardSteps.ProviderInputs); - } + setWizardStep(WizardSteps.ProviderInputs); }} onKeyDown={(evt) => { if (evt.key === "Enter") { setSelectedProvider(provider); setWizardStep(WizardSteps.ProviderInputs); } - if(hasSetupStep){ - setWizardStep(WizardSteps.ProviderSetup); - } else { - setWizardStep(WizardSteps.ProviderInputs); - } }} > {icon} @@ -156,23 +144,6 @@ export const CreateDynamicSecretForm = ({
)} - { - wizardStep === WizardSteps.ProviderSetup && selectedProvider === DynamicSecretProviders.AzureEntraId - && ( - - { setWizardStep(WizardSteps.ProviderInputs) }} - onCancel={handleFormReset} - /> - - ) - } {wizardStep === WizardSteps.ProviderInputs && selectedProvider === DynamicSecretProviders.SqlDatabase && (