feat: add customization components (#7419)

* ✨ (webhookFieldComponent): Add support for ENABLE_DATASTAX_LANGFLOW feature flag to conditionally show generate token button
🐛 (use-get-config): Set default value for webhook polling interval if not provided in data
♻️ (custom-secret-key-modal-button): Refactor to pass modal props as a separate object to improve readability and maintainability
🔧 (use-generate-token): Add new file for generating token function
🔧 (secretKeyModal): Refactor to use generate token function based on ENABLE_DATASTAX_LANGFLOW flag and separate modal props into a dedicated interface

* ✨ (constants.ts): add default polling interval and timeout constants for better code readability and maintainability
♻️ (use-get-config.ts): refactor to use the newly added default constants for polling interval and timeout to improve code consistency and reduce duplication

* 🐛 (typescript_test.yml): update the maximum shard count calculation to be 25 instead of 10 to improve test distribution and efficiency

* ⚙️ (typescript_test.yml): adjust optimal shard count calculation to ensure a maximum of 10 shards for test execution
♻️ (index.tsx): refactor modalConfigProps assignment to handle cases where modalProps is null or undefined

---------

Co-authored-by: Mike Fortman <michael.fortman@datastax.com>
This commit is contained in:
Cristhian Zanforlin Lousa 2025-04-03 15:56:46 -03:00 • committed by GitHub
commit f4a07fac69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 83 additions and 23 deletions

View file

@ -1,7 +1,8 @@
import { AuthContext } from "@/contexts/authContext"; import { AuthContext } from "@/contexts/authContext";
import { useGetBuildsMutation } from "@/controllers/API/queries/_builds/use-get-builds-polling-mutation"; import { useGetBuildsMutation } from "@/controllers/API/queries/_builds/use-get-builds-polling-mutation";
import SecretKeyModalButton from "@/customization/components/custom-secret-key-modal-button"; import SecretKeyModalButton from "@/customization/components/custom-secret-key-modal-button";
import useFlowStore from "@/stores/flowStore"; import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
import { getModalPropsApiKey } from "@/customization/utils/get-modal-props";
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { InputProps, TextAreaComponentType } from "../../types"; import { InputProps, TextAreaComponentType } from "../../types";
import CopyFieldAreaComponent from "../copyFieldAreaComponent"; import CopyFieldAreaComponent from "../copyFieldAreaComponent";
@ -19,14 +20,21 @@ export default function WebhookFieldComponent({
const [userId, setUserId] = useState(""); const [userId, setUserId] = useState("");
const { mutate: getBuildsMutation } = useGetBuildsMutation(); const { mutate: getBuildsMutation } = useGetBuildsMutation();
const hasInitialized = useRef(false); const hasInitialized = useRef(false);
const modalProps = getModalPropsApiKey();
const isBackendUrl = nodeInformationMetadata?.variableName === "endpoint"; const isBackendUrl = nodeInformationMetadata?.variableName === "endpoint";
const isCurlWebhook = nodeInformationMetadata?.variableName === "curl"; const isCurlWebhook = nodeInformationMetadata?.variableName === "curl";
const isAuth = nodeInformationMetadata?.isAuth; const isAuth = nodeInformationMetadata?.isAuth;
const showGenerateToken = isBackendUrl && !editNode && !isAuth; const showGenerateToken =
(isBackendUrl && !editNode && !isAuth) ||
(ENABLE_DATASTAX_LANGFLOW && !editNode);
useEffect(() => { useEffect(() => {
if (!editNode && isBackendUrl && !hasInitialized.current) { const getBuilds =
(!editNode && isBackendUrl && !hasInitialized.current) ||
(ENABLE_DATASTAX_LANGFLOW && !editNode);
if (getBuilds) {
hasInitialized.current = true; hasInitialized.current = true;
getBuildsMutation({ getBuildsMutation({
flowId: nodeInformationMetadata?.flowId!, flowId: nodeInformationMetadata?.flowId!,
@ -69,7 +77,7 @@ export default function WebhookFieldComponent({
{showGenerateToken && ( {showGenerateToken && (
<div> <div>
<SecretKeyModalButton userId={userId} /> <SecretKeyModalButton userId={userId} modalProps={modalProps} />
</div> </div>
)} )}
</div> </div>

View file

@ -1062,3 +1062,6 @@ export const OPENAI_VOICES = [
{ name: "shimmer", value: "shimmer" }, { name: "shimmer", value: "shimmer" },
{ name: "verse", value: "verse" }, { name: "verse", value: "verse" },
]; ];
export const DEFAULT_POLLING_INTERVAL = 5000;
export const DEFAULT_TIMEOUT = 30000;

View file

@ -1,3 +1,7 @@
import {
DEFAULT_POLLING_INTERVAL,
DEFAULT_TIMEOUT,
} from "@/constants/constants";
import { EventDeliveryType } from "@/constants/enums"; import { EventDeliveryType } from "@/constants/enums";
import useFlowsManagerStore from "@/stores/flowsManagerStore"; import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useUtilityStore } from "@/stores/utilityStore"; import { useUtilityStore } from "@/stores/utilityStore";
@ -44,7 +48,7 @@ export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
if (data) { if (data) {
const timeoutInMilliseconds = data.frontend_timeout const timeoutInMilliseconds = data.frontend_timeout
? data.frontend_timeout * 1000 ? data.frontend_timeout * 1000
: 30000; : DEFAULT_TIMEOUT;
axios.defaults.baseURL = ""; axios.defaults.baseURL = "";
axios.defaults.timeout = timeoutInMilliseconds; axios.defaults.timeout = timeoutInMilliseconds;
setAutoSaving(data.auto_saving); setAutoSaving(data.auto_saving);
@ -52,7 +56,9 @@ export const useGetConfig: useQueryFunctionType<undefined, ConfigResponse> = (
setHealthCheckMaxRetries(data.health_check_max_retries); setHealthCheckMaxRetries(data.health_check_max_retries);
setMaxFileSizeUpload(data.max_file_size_upload); setMaxFileSizeUpload(data.max_file_size_upload);
setFeatureFlags(data.feature_flags); setFeatureFlags(data.feature_flags);
setWebhookPollingInterval(data.webhook_polling_interval); setWebhookPollingInterval(
data.webhook_polling_interval ?? DEFAULT_POLLING_INTERVAL,
);
} }
return data; return data;
}; };

View file

@ -2,11 +2,11 @@ import { getModalPropsApiKey } from "@/pages/SettingsPage/pages/ApiKeysPage/help
export const SecretKeyModalButton = ({ export const SecretKeyModalButton = ({
userId, userId,
modalProps,
}: { }: {
userId: string; userId: string;
modalProps;
}): JSX.Element => { }): JSX.Element => {
const modalProps = getModalPropsApiKey();
return <></>; return <></>;
}; };

View file

@ -0,0 +1,7 @@
export const useGenerateToken = (): any => {
const tokenFunction = (() => {
return "token";
}) as any;
tokenFunction.token = "token";
return tokenFunction;
};

View file

@ -1,7 +1,6 @@
import * as Form from "@radix-ui/react-form"; import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
import { Label } from "@radix-ui/react-form"; import { useGenerateToken } from "@/customization/hooks/use-generate-token";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { Input } from "../../components/ui/input";
import { COPIED_NOTICE_ALERT } from "../../constants/alerts_constants"; import { COPIED_NOTICE_ALERT } from "../../constants/alerts_constants";
import { createApiKey } from "../../controllers/API"; import { createApiKey } from "../../controllers/API";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
@ -11,9 +10,21 @@ import { ContentRenderKey } from "./components/content-render";
import { FormKeyRender } from "./components/form-key-render"; import { FormKeyRender } from "./components/form-key-render";
import { HeaderRender } from "./components/header-render"; import { HeaderRender } from "./components/header-render";
interface ModalProps { // Add this interface for the modal props
generatedKeyMessage?: React.ReactNode; interface ModalConfigProps {
title?: string;
description?: React.ReactNode; description?: React.ReactNode;
inputLabel?: React.ReactNode;
inputPlaceholder?: string;
buttonText?: string;
generatedKeyMessage?: React.ReactNode;
showIcon?: boolean;
}
interface SecretKeyModalProps {
userId?: string;
size?: string;
modalProps?: ModalConfigProps;
} }
export default function SecretKeyModal({ export default function SecretKeyModal({
@ -21,7 +32,7 @@ export default function SecretKeyModal({
data, data,
onCloseModal, onCloseModal,
modalProps, modalProps,
}: ApiKeyType & { modalProps?: ModalProps }) { }: ApiKeyType & { modalProps: SecretKeyModalProps }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [apiKeyName, setApiKeyName] = useState(data?.apikeyname ?? ""); const [apiKeyName, setApiKeyName] = useState(data?.apikeyname ?? "");
const [apiKeyValue, setApiKeyValue] = useState(""); const [apiKeyValue, setApiKeyValue] = useState("");
@ -29,6 +40,8 @@ export default function SecretKeyModal({
const [textCopied, setTextCopied] = useState(true); const [textCopied, setTextCopied] = useState(true);
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
const generateToken = useGenerateToken();
const modalConfigProps = modalProps?.modalProps ?? modalProps;
useEffect(() => { useEffect(() => {
if (open) { if (open) {
@ -68,14 +81,33 @@ export default function SecretKeyModal({
.catch((err) => {}); .catch((err) => {});
} }
function handleSubmitForm() { async function handleSubmitForm() {
if (apiKeyValue) setOpen(false);
if (ENABLE_DATASTAX_LANGFLOW) {
handleDataStaxKey();
} else {
handleOSSKey();
}
}
const handleDataStaxKey = async () => {
try {
const { token } = await generateToken();
setApiKeyValue(token);
setRenderKey(true);
} catch (error) {
console.error("Error generating token:", error);
}
};
const handleOSSKey = () => {
if (!renderKey) { if (!renderKey) {
setRenderKey(true); setRenderKey(true);
handleAddNewKey(); handleAddNewKey();
} else { } else {
setOpen(false); setOpen(false);
} }
} };
return ( return (
<BaseModal <BaseModal
@ -89,30 +121,32 @@ export default function SecretKeyModal({
clampDescription={3} clampDescription={3}
description={ description={
renderKey ? ( renderKey ? (
<>{modalProps?.generatedKeyMessage}</> <>{modalConfigProps?.generatedKeyMessage}</>
) : ( ) : (
<>{modalProps?.description}</> <>{modalConfigProps?.description}</>
) )
} }
> >
<HeaderRender <HeaderRender
title={modalProps?.title} title={modalConfigProps?.title}
showIcon={modalProps?.showIcon} showIcon={modalConfigProps?.showIcon}
/> />
</BaseModal.Header> </BaseModal.Header>
<BaseModal.Content> <BaseModal.Content>
{renderKey ? ( {renderKey ? (
<ContentRenderKey <ContentRenderKey
inputLabel={String(modalProps?.inputLabel ?? "")} inputLabel={String(modalConfigProps?.inputLabel ?? "")}
inputRef={inputRef} inputRef={inputRef}
apiKeyValue={apiKeyValue} apiKeyValue={apiKeyValue}
handleCopyClick={handleCopyClick} handleCopyClick={handleCopyClick}
textCopied={textCopied} textCopied={textCopied}
renderKey={renderKey} renderKey={renderKey}
/> />
) : ENABLE_DATASTAX_LANGFLOW ? (
<></>
) : ( ) : (
<FormKeyRender <FormKeyRender
modalProps={modalProps} modalProps={modalConfigProps}
apiKeyName={apiKeyName} apiKeyName={apiKeyName}
inputRef={inputRef} inputRef={inputRef}
setApiKeyName={setApiKeyName} setApiKeyName={setApiKeyName}
@ -120,7 +154,9 @@ export default function SecretKeyModal({
)} )}
</BaseModal.Content> </BaseModal.Content>
<BaseModal.Footer <BaseModal.Footer
submit={{ label: renderKey ? "Done" : (modalProps?.buttonText ?? "") }} submit={{
label: renderKey ? "Done" : (modalConfigProps?.buttonText ?? ""),
}}
/> />
</BaseModal> </BaseModal>
); );