✨ (frontend): add switch-case-size helper for modal size management
♻️ (frontend): refactor BaseModal to use switchCaseModalSize helper ✨ (frontend): add GeneralPageHeaderComponent for settings page header ✨ (frontend): add PasswordFormComponent for password management in settings page ✨ (SettingsPage): add ProfileGradientFormComponent to allow users to choose profile gradient ✨ (SettingsPage): add StoreApiKeyFormComponent to enable users to store API keys ♻️ (GeneralPage): refactor GeneralPage to use modular components for forms
This commit is contained in:
parent
898e9f101a
commit
14a7e2835e
7 changed files with 387 additions and 273 deletions
|
|
@ -0,0 +1,67 @@
|
||||||
|
export const switchCaseModalSize = (size: string) => {
|
||||||
|
let minWidth: string;
|
||||||
|
let height: string;
|
||||||
|
switch (size) {
|
||||||
|
case "x-small":
|
||||||
|
minWidth = "min-w-[20vw]";
|
||||||
|
height = "h-full";
|
||||||
|
break;
|
||||||
|
case "smaller":
|
||||||
|
minWidth = "min-w-[40vw]";
|
||||||
|
height = "h-[11rem]";
|
||||||
|
break;
|
||||||
|
case "smaller-h-full":
|
||||||
|
minWidth = "min-w-[40vw]";
|
||||||
|
height = "h-full";
|
||||||
|
break;
|
||||||
|
case "small":
|
||||||
|
minWidth = "min-w-[40vw]";
|
||||||
|
height = "h-[40vh]";
|
||||||
|
break;
|
||||||
|
case "small-h-full":
|
||||||
|
minWidth = "min-w-[40vw]";
|
||||||
|
height = "h-full";
|
||||||
|
break;
|
||||||
|
case "medium":
|
||||||
|
minWidth = "min-w-[60vw]";
|
||||||
|
height = "h-[60vh]";
|
||||||
|
break;
|
||||||
|
case "medium-h-full":
|
||||||
|
minWidth = "min-w-[60vw]";
|
||||||
|
height = "h-full";
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "large":
|
||||||
|
minWidth = "min-w-[85vw]";
|
||||||
|
height = "h-[80vh]";
|
||||||
|
break;
|
||||||
|
case "three-cards":
|
||||||
|
minWidth = "min-w-[1066px]";
|
||||||
|
height = "h-fit";
|
||||||
|
break;
|
||||||
|
case "large-thin":
|
||||||
|
minWidth = "min-w-[65vw]";
|
||||||
|
height = "h-[80vh]";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "md-thin":
|
||||||
|
minWidth = "min-w-[85vw]";
|
||||||
|
height = "h-[70vh]";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "sm-thin":
|
||||||
|
minWidth = "min-w-[65vw]";
|
||||||
|
height = "h-[70vh]";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "large-h-full":
|
||||||
|
minWidth = "min-w-[80vw]";
|
||||||
|
height = "h-full";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
minWidth = "min-w-[80vw]";
|
||||||
|
height = "h-[80vh]";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return { minWidth, height };
|
||||||
|
};
|
||||||
|
|
@ -16,10 +16,10 @@ import {
|
||||||
} from "../../components/ui/dialog-with-no-close";
|
} from "../../components/ui/dialog-with-no-close";
|
||||||
|
|
||||||
import { DialogClose } from "@radix-ui/react-dialog";
|
import { DialogClose } from "@radix-ui/react-dialog";
|
||||||
import ForwardedIconComponent from "../../components/genericIconComponent";
|
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { modalHeaderType } from "../../types/components";
|
import { modalHeaderType } from "../../types/components";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
|
import { switchCaseModalSize } from "./helpers/switch-case-size";
|
||||||
|
|
||||||
type ContentProps = { children: ReactNode };
|
type ContentProps = { children: ReactNode };
|
||||||
type HeaderProps = { children: ReactNode; description: string };
|
type HeaderProps = { children: ReactNode; description: string };
|
||||||
|
|
@ -148,71 +148,7 @@ function BaseModal({
|
||||||
(child) => (child as React.ReactElement).type === Footer,
|
(child) => (child as React.ReactElement).type === Footer,
|
||||||
);
|
);
|
||||||
|
|
||||||
let minWidth: string;
|
let { minWidth, height } = switchCaseModalSize(size);
|
||||||
let height: string;
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case "x-small":
|
|
||||||
minWidth = "min-w-[20vw]";
|
|
||||||
height = "h-full";
|
|
||||||
break;
|
|
||||||
case "smaller":
|
|
||||||
minWidth = "min-w-[40vw]";
|
|
||||||
height = "h-[11rem]";
|
|
||||||
break;
|
|
||||||
case "smaller-h-full":
|
|
||||||
minWidth = "min-w-[40vw]";
|
|
||||||
height = "h-full";
|
|
||||||
break;
|
|
||||||
case "small":
|
|
||||||
minWidth = "min-w-[40vw]";
|
|
||||||
height = "h-[40vh]";
|
|
||||||
break;
|
|
||||||
case "small-h-full":
|
|
||||||
minWidth = "min-w-[40vw]";
|
|
||||||
height = "h-full";
|
|
||||||
break;
|
|
||||||
case "medium":
|
|
||||||
minWidth = "min-w-[60vw]";
|
|
||||||
height = "h-[60vh]";
|
|
||||||
break;
|
|
||||||
case "medium-h-full":
|
|
||||||
minWidth = "min-w-[60vw]";
|
|
||||||
height = "h-full";
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "large":
|
|
||||||
minWidth = "min-w-[85vw]";
|
|
||||||
height = "h-[80vh]";
|
|
||||||
break;
|
|
||||||
case "three-cards":
|
|
||||||
minWidth = "min-w-[1066px]";
|
|
||||||
height = "h-fit";
|
|
||||||
break;
|
|
||||||
case "large-thin":
|
|
||||||
minWidth = "min-w-[65vw]";
|
|
||||||
height = "h-[80vh]";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "md-thin":
|
|
||||||
minWidth = "min-w-[85vw]";
|
|
||||||
height = "h-[70vh]";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "sm-thin":
|
|
||||||
minWidth = "min-w-[65vw]";
|
|
||||||
height = "h-[70vh]";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "large-h-full":
|
|
||||||
minWidth = "min-w-[80vw]";
|
|
||||||
height = "h-full";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
minWidth = "min-w-[80vw]";
|
|
||||||
height = "h-[80vh]";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onChangeOpenModal) {
|
if (onChangeOpenModal) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import ForwardedIconComponent from "../../../../../../components/genericIconComponent";
|
||||||
|
|
||||||
|
const GeneralPageHeaderComponent = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex w-full items-center justify-between gap-4 space-y-0.5">
|
||||||
|
<div className="flex w-full flex-col">
|
||||||
|
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||||
|
General
|
||||||
|
<ForwardedIconComponent
|
||||||
|
name="SlidersHorizontal"
|
||||||
|
className="ml-2 h-5 w-5 text-primary"
|
||||||
|
/>
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Manage settings related to Langflow and your account.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default GeneralPageHeaderComponent;
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
import * as Form from "@radix-ui/react-form";
|
||||||
|
import InputComponent from "../../../../../../components/inputComponent";
|
||||||
|
import { Button } from "../../../../../../components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "../../../../../../components/ui/card";
|
||||||
|
|
||||||
|
type PasswordFormComponentProps = {
|
||||||
|
password: string;
|
||||||
|
cnfPassword: string;
|
||||||
|
handleInput: (event: any) => void;
|
||||||
|
handlePatchPassword: (
|
||||||
|
password: string,
|
||||||
|
cnfPassword: string,
|
||||||
|
handleInput: any,
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
const PasswordFormComponent = ({
|
||||||
|
password,
|
||||||
|
cnfPassword,
|
||||||
|
handleInput,
|
||||||
|
handlePatchPassword,
|
||||||
|
}: PasswordFormComponentProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Root
|
||||||
|
onSubmit={(event) => {
|
||||||
|
handlePatchPassword(password, cnfPassword, handleInput);
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card x-chunk="dashboard-04-chunk-2">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Password</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Type your new password and confirm it.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex w-full gap-4">
|
||||||
|
<Form.Field name="password" className="w-full">
|
||||||
|
<InputComponent
|
||||||
|
id="pasword"
|
||||||
|
onChange={(value) => {
|
||||||
|
handleInput({ target: { name: "password", value } });
|
||||||
|
}}
|
||||||
|
value={password}
|
||||||
|
isForm
|
||||||
|
password={true}
|
||||||
|
placeholder="Password"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<Form.Message match="valueMissing" className="field-invalid">
|
||||||
|
Please enter your password
|
||||||
|
</Form.Message>
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field name="cnfPassword" className="w-full">
|
||||||
|
<InputComponent
|
||||||
|
id="cnfPassword"
|
||||||
|
onChange={(value) => {
|
||||||
|
handleInput({
|
||||||
|
target: { name: "cnfPassword", value },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={cnfPassword}
|
||||||
|
isForm
|
||||||
|
password={true}
|
||||||
|
placeholder="Confirm Password"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Form.Message className="field-invalid" match="valueMissing">
|
||||||
|
Please confirm your password
|
||||||
|
</Form.Message>
|
||||||
|
</Form.Field>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="border-t px-6 py-4">
|
||||||
|
<Form.Submit asChild>
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</Form.Submit>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</Form.Root>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default PasswordFormComponent;
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import * as Form from "@radix-ui/react-form";
|
||||||
|
import GradientChooserComponent from "../../../../../../components/gradientChooserComponent";
|
||||||
|
import { Button } from "../../../../../../components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "../../../../../../components/ui/card";
|
||||||
|
import { gradients } from "../../../../../../utils/styleUtils";
|
||||||
|
|
||||||
|
type ProfileGradientFormComponentProps = {
|
||||||
|
gradient: string;
|
||||||
|
handleInput: (event: any) => void;
|
||||||
|
handlePatchGradient: (gradient: string) => void;
|
||||||
|
userData: any;
|
||||||
|
};
|
||||||
|
const ProfileGradientFormComponent = ({
|
||||||
|
gradient,
|
||||||
|
handleInput,
|
||||||
|
handlePatchGradient,
|
||||||
|
userData,
|
||||||
|
}: ProfileGradientFormComponentProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Root
|
||||||
|
onSubmit={(event) => {
|
||||||
|
handlePatchGradient(gradient);
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card x-chunk="dashboard-04-chunk-1">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Profile Gradient</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Choose the gradient that appears as your profile picture.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="py-2">
|
||||||
|
<GradientChooserComponent
|
||||||
|
value={
|
||||||
|
gradient == ""
|
||||||
|
? userData?.profile_image ??
|
||||||
|
gradients[
|
||||||
|
parseInt(userData?.id ?? "", 30) % gradients.length
|
||||||
|
]
|
||||||
|
: gradient
|
||||||
|
}
|
||||||
|
onChange={(value) => {
|
||||||
|
handleInput({ target: { name: "gradient", value } });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="border-t px-6 py-4">
|
||||||
|
<Form.Submit asChild>
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</Form.Submit>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</Form.Root>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ProfileGradientFormComponent;
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
import * as Form from "@radix-ui/react-form";
|
||||||
|
import InputComponent from "../../../../../../components/inputComponent";
|
||||||
|
import { Button } from "../../../../../../components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "../../../../../../components/ui/card";
|
||||||
|
import {
|
||||||
|
CREATE_API_KEY,
|
||||||
|
INSERT_API_KEY,
|
||||||
|
INVALID_API_KEY,
|
||||||
|
NO_API_KEY,
|
||||||
|
} from "../../../../../../constants/constants";
|
||||||
|
|
||||||
|
type StoreApiKeyFormComponentProps = {
|
||||||
|
apikey: string;
|
||||||
|
handleInput: (event: any) => void;
|
||||||
|
handleSaveKey: (apikey: string, handleInput: any) => void;
|
||||||
|
loadingApiKey: boolean;
|
||||||
|
validApiKey: boolean;
|
||||||
|
hasApiKey: boolean;
|
||||||
|
};
|
||||||
|
const StoreApiKeyFormComponent = ({
|
||||||
|
apikey,
|
||||||
|
handleInput,
|
||||||
|
handleSaveKey,
|
||||||
|
loadingApiKey,
|
||||||
|
validApiKey,
|
||||||
|
hasApiKey,
|
||||||
|
}: StoreApiKeyFormComponentProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Root
|
||||||
|
onSubmit={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
handleSaveKey(apikey, handleInput);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card x-chunk="dashboard-04-chunk-2" id="api">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Store API Key</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{(hasApiKey && !validApiKey
|
||||||
|
? INVALID_API_KEY
|
||||||
|
: !hasApiKey
|
||||||
|
? NO_API_KEY
|
||||||
|
: "") + INSERT_API_KEY}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex w-full flex-col gap-3">
|
||||||
|
<div className="flex w-full gap-4">
|
||||||
|
<Form.Field name="apikey" className="w-full">
|
||||||
|
<InputComponent
|
||||||
|
id="apikey"
|
||||||
|
onChange={(value) => {
|
||||||
|
handleInput({ target: { name: "apikey", value } });
|
||||||
|
}}
|
||||||
|
value={apikey}
|
||||||
|
isForm
|
||||||
|
password={true}
|
||||||
|
placeholder="Insert your API Key"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<Form.Message match="valueMissing" className="field-invalid">
|
||||||
|
Please enter your API Key
|
||||||
|
</Form.Message>
|
||||||
|
</Form.Field>
|
||||||
|
</div>
|
||||||
|
<span className="pr-1 text-xs text-muted-foreground">
|
||||||
|
{CREATE_API_KEY}{" "}
|
||||||
|
<a
|
||||||
|
className="text-high-indigo underline"
|
||||||
|
href="https://langflow.store/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
langflow.store
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="border-t px-6 py-4">
|
||||||
|
<Form.Submit asChild>
|
||||||
|
<Button
|
||||||
|
loading={loadingApiKey}
|
||||||
|
type="submit"
|
||||||
|
data-testid="api-key-save-button-store"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Form.Submit>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</Form.Root>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default StoreApiKeyFormComponent;
|
||||||
|
|
@ -1,25 +1,6 @@
|
||||||
import * as Form from "@radix-ui/react-form";
|
|
||||||
import { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
import { CONTROL_PATCH_USER_STATE } from "../../../../constants/constants";
|
||||||
import GradientChooserComponent from "../../../../components/gradientChooserComponent";
|
|
||||||
import InputComponent from "../../../../components/inputComponent";
|
|
||||||
import { Button } from "../../../../components/ui/button";
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "../../../../components/ui/card";
|
|
||||||
import {
|
|
||||||
CONTROL_PATCH_USER_STATE,
|
|
||||||
CREATE_API_KEY,
|
|
||||||
INSERT_API_KEY,
|
|
||||||
INVALID_API_KEY,
|
|
||||||
NO_API_KEY,
|
|
||||||
} from "../../../../constants/constants";
|
|
||||||
import { AuthContext } from "../../../../contexts/authContext";
|
import { AuthContext } from "../../../../contexts/authContext";
|
||||||
import useAlertStore from "../../../../stores/alertStore";
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
|
|
@ -28,21 +9,24 @@ import {
|
||||||
inputHandlerEventType,
|
inputHandlerEventType,
|
||||||
patchUserInputStateType,
|
patchUserInputStateType,
|
||||||
} from "../../../../types/components";
|
} from "../../../../types/components";
|
||||||
import { gradients } from "../../../../utils/styleUtils";
|
|
||||||
import usePatchGradient from "../hooks/use-patch-gradient";
|
import usePatchGradient from "../hooks/use-patch-gradient";
|
||||||
import usePatchPassword from "../hooks/use-patch-password";
|
import usePatchPassword from "../hooks/use-patch-password";
|
||||||
import useSaveKey from "../hooks/use-save-key";
|
import useSaveKey from "../hooks/use-save-key";
|
||||||
import useScrollToElement from "../hooks/use-scroll-to-element";
|
import useScrollToElement from "../hooks/use-scroll-to-element";
|
||||||
|
import GeneralPageHeaderComponent from "./components/GeneralPageHeader";
|
||||||
|
import PasswordFormComponent from "./components/PasswordForm";
|
||||||
|
import ProfileGradientFormComponent from "./components/ProfileGradientForm";
|
||||||
|
import StoreApiKeyFormComponent from "./components/StoreApiKeyForm";
|
||||||
|
|
||||||
export default function GeneralPage() {
|
export default function GeneralPage() {
|
||||||
const setCurrentFlowId = useFlowsManagerStore(
|
const setCurrentFlowId = useFlowsManagerStore(
|
||||||
(state) => state.setCurrentFlowId
|
(state) => state.setCurrentFlowId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { scrollId } = useParams();
|
const { scrollId } = useParams();
|
||||||
|
|
||||||
const [inputState, setInputState] = useState<patchUserInputStateType>(
|
const [inputState, setInputState] = useState<patchUserInputStateType>(
|
||||||
CONTROL_PATCH_USER_STATE
|
CONTROL_PATCH_USER_STATE,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { autoLogin } = useContext(AuthContext);
|
const { autoLogin } = useContext(AuthContext);
|
||||||
|
|
@ -63,14 +47,14 @@ export default function GeneralPage() {
|
||||||
const { handlePatchPassword } = usePatchPassword(
|
const { handlePatchPassword } = usePatchPassword(
|
||||||
userData,
|
userData,
|
||||||
setSuccessData,
|
setSuccessData,
|
||||||
setErrorData
|
setErrorData,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handlePatchGradient } = usePatchGradient(
|
const { handlePatchGradient } = usePatchGradient(
|
||||||
setSuccessData,
|
setSuccessData,
|
||||||
setErrorData,
|
setErrorData,
|
||||||
userData,
|
userData,
|
||||||
setUserData
|
setUserData,
|
||||||
);
|
);
|
||||||
|
|
||||||
useScrollToElement(scrollId, setCurrentFlowId);
|
useScrollToElement(scrollId, setCurrentFlowId);
|
||||||
|
|
@ -80,7 +64,7 @@ export default function GeneralPage() {
|
||||||
setErrorData,
|
setErrorData,
|
||||||
setHasApiKey,
|
setHasApiKey,
|
||||||
setValidApiKey,
|
setValidApiKey,
|
||||||
setLoadingApiKey
|
setLoadingApiKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleInput({
|
function handleInput({
|
||||||
|
|
@ -91,192 +75,33 @@ export default function GeneralPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full flex-col gap-6">
|
<div className="flex h-full w-full flex-col gap-6">
|
||||||
<div className="flex w-full items-center justify-between gap-4 space-y-0.5">
|
<GeneralPageHeaderComponent />
|
||||||
<div className="flex w-full flex-col">
|
|
||||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
|
||||||
General
|
|
||||||
<ForwardedIconComponent
|
|
||||||
name="SlidersHorizontal"
|
|
||||||
className="ml-2 h-5 w-5 text-primary"
|
|
||||||
/>
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Manage settings related to Langflow and your account.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-6">
|
<div className="grid gap-6">
|
||||||
<Form.Root
|
<ProfileGradientFormComponent
|
||||||
onSubmit={(event) => {
|
gradient={gradient}
|
||||||
handlePatchGradient(gradient);
|
handleInput={handleInput}
|
||||||
event.preventDefault();
|
handlePatchGradient={handlePatchGradient}
|
||||||
}}
|
userData={userData}
|
||||||
>
|
/>
|
||||||
<Card x-chunk="dashboard-04-chunk-1">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Profile Gradient</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Choose the gradient that appears as your profile picture.
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="py-2">
|
|
||||||
<GradientChooserComponent
|
|
||||||
value={
|
|
||||||
gradient == ""
|
|
||||||
? userData?.profile_image ??
|
|
||||||
gradients[
|
|
||||||
parseInt(userData?.id ?? "", 30) % gradients.length
|
|
||||||
]
|
|
||||||
: gradient
|
|
||||||
}
|
|
||||||
onChange={(value) => {
|
|
||||||
handleInput({ target: { name: "gradient", value } });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className="border-t px-6 py-4">
|
|
||||||
<Form.Submit asChild>
|
|
||||||
<Button type="submit">Save</Button>
|
|
||||||
</Form.Submit>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</Form.Root>
|
|
||||||
{!autoLogin && (
|
|
||||||
<Form.Root
|
|
||||||
onSubmit={(event) => {
|
|
||||||
handlePatchPassword(password, cnfPassword, handleInput);
|
|
||||||
event.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Card x-chunk="dashboard-04-chunk-2">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Password</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Type your new password and confirm it.
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="flex w-full gap-4">
|
|
||||||
<Form.Field name="password" className="w-full">
|
|
||||||
<InputComponent
|
|
||||||
id="pasword"
|
|
||||||
onChange={(value) => {
|
|
||||||
handleInput({ target: { name: "password", value } });
|
|
||||||
}}
|
|
||||||
value={password}
|
|
||||||
isForm
|
|
||||||
password={true}
|
|
||||||
placeholder="Password"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<Form.Message
|
|
||||||
match="valueMissing"
|
|
||||||
className="field-invalid"
|
|
||||||
>
|
|
||||||
Please enter your password
|
|
||||||
</Form.Message>
|
|
||||||
</Form.Field>
|
|
||||||
<Form.Field name="cnfPassword" className="w-full">
|
|
||||||
<InputComponent
|
|
||||||
id="cnfPassword"
|
|
||||||
onChange={(value) => {
|
|
||||||
handleInput({
|
|
||||||
target: { name: "cnfPassword", value },
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
value={cnfPassword}
|
|
||||||
isForm
|
|
||||||
password={true}
|
|
||||||
placeholder="Confirm Password"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Form.Message
|
{!autoLogin && (
|
||||||
className="field-invalid"
|
<PasswordFormComponent
|
||||||
match="valueMissing"
|
password={password}
|
||||||
>
|
cnfPassword={cnfPassword}
|
||||||
Please confirm your password
|
handleInput={handleInput}
|
||||||
</Form.Message>
|
handlePatchPassword={handlePatchPassword}
|
||||||
</Form.Field>
|
/>
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className="border-t px-6 py-4">
|
|
||||||
<Form.Submit asChild>
|
|
||||||
<Button type="submit">Save</Button>
|
|
||||||
</Form.Submit>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</Form.Root>
|
|
||||||
)}
|
)}
|
||||||
{hasStore && (
|
{hasStore && (
|
||||||
<Form.Root
|
<StoreApiKeyFormComponent
|
||||||
onSubmit={(event) => {
|
apikey={apikey}
|
||||||
event.preventDefault();
|
handleInput={handleInput}
|
||||||
handleSaveKey(apikey, handleInput);
|
handleSaveKey={handleSaveKey}
|
||||||
}}
|
loadingApiKey={loadingApiKey}
|
||||||
>
|
validApiKey={validApiKey}
|
||||||
<Card x-chunk="dashboard-04-chunk-2" id="api">
|
hasApiKey={hasApiKey}
|
||||||
<CardHeader>
|
/>
|
||||||
<CardTitle>Store API Key</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
{(hasApiKey && !validApiKey
|
|
||||||
? INVALID_API_KEY
|
|
||||||
: !hasApiKey
|
|
||||||
? NO_API_KEY
|
|
||||||
: "") + INSERT_API_KEY}
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div className="flex w-full flex-col gap-3">
|
|
||||||
<div className="flex w-full gap-4">
|
|
||||||
<Form.Field name="apikey" className="w-full">
|
|
||||||
<InputComponent
|
|
||||||
id="apikey"
|
|
||||||
onChange={(value) => {
|
|
||||||
handleInput({ target: { name: "apikey", value } });
|
|
||||||
}}
|
|
||||||
value={apikey}
|
|
||||||
isForm
|
|
||||||
password={true}
|
|
||||||
placeholder="Insert your API Key"
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<Form.Message
|
|
||||||
match="valueMissing"
|
|
||||||
className="field-invalid"
|
|
||||||
>
|
|
||||||
Please enter your API Key
|
|
||||||
</Form.Message>
|
|
||||||
</Form.Field>
|
|
||||||
</div>
|
|
||||||
<span className="pr-1 text-xs text-muted-foreground">
|
|
||||||
{CREATE_API_KEY}{" "}
|
|
||||||
<a
|
|
||||||
className="text-high-indigo underline"
|
|
||||||
href="https://langflow.store/"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
langflow.store
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className="border-t px-6 py-4">
|
|
||||||
<Form.Submit asChild>
|
|
||||||
<Button
|
|
||||||
loading={loadingApiKey}
|
|
||||||
type="submit"
|
|
||||||
data-testid="api-key-save-button-store"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</Form.Submit>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</Form.Root>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue