-
Notifications
You must be signed in to change notification settings - Fork 376
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #368 from pascalbreuninger/feat/ENG-1499-clone-pro…
…vider feat(cli): add option to clone existing provider
- Loading branch information
Showing
17 changed files
with
499 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
desktop/src/views/Providers/AddProvider/LoadingProviderIndicator.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Box, HStack, Text } from "@chakra-ui/react" | ||
|
||
export function LoadingProviderIndicator({ label }: Readonly<{ label: string | undefined }>) { | ||
return ( | ||
<HStack marginTop="2" justifyContent="center" alignItems="center" color="gray.600"> | ||
<Text fontWeight="medium">{label}</Text> | ||
<Box as="svg" height="3" marginInlineStart="0 !important" width="8" viewBox="0 0 48 30"> | ||
<circle fill="currentColor" stroke="none" cx="6" cy="24" r="6"> | ||
<animateTransform | ||
attributeName="transform" | ||
dur="1s" | ||
type="translate" | ||
values="0 0; 0 -12; 0 0; 0 0; 0 0; 0 0" | ||
repeatCount="indefinite" | ||
begin="0" | ||
/> | ||
</circle> | ||
<circle fill="currentColor" stroke="none" cx="24" cy="24" r="6"> | ||
<animateTransform | ||
id="op" | ||
attributeName="transform" | ||
dur="1s" | ||
type="translate" | ||
values="0 0; 0 -12; 0 0; 0 0; 0 0; 0 0" | ||
repeatCount="indefinite" | ||
begin="0.3s" | ||
/> | ||
</circle> | ||
<circle fill="currentColor" stroke="none" cx="42" cy="24" r="6"> | ||
<animateTransform | ||
id="op" | ||
attributeName="transform" | ||
dur="1s" | ||
type="translate" | ||
values="0 0; 0 -12; 0 0; 0 0; 0 0; 0 0" | ||
repeatCount="indefinite" | ||
begin="0.6s" | ||
/> | ||
</circle> | ||
</Box> | ||
</HStack> | ||
) | ||
} |
133 changes: 133 additions & 0 deletions
133
desktop/src/views/Providers/AddProvider/SetupClonedProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { | ||
Code, | ||
FormControl, | ||
FormErrorMessage, | ||
FormHelperText, | ||
FormLabel, | ||
VStack, | ||
} from "@chakra-ui/react" | ||
import styled from "@emotion/styled" | ||
import { Controller, useForm } from "react-hook-form" | ||
import { ErrorMessageBox } from "../../../components" | ||
import { useProviders } from "../../../contexts" | ||
import { exists, isError, useFormErrors } from "../../../lib" | ||
import { CustomNameInput } from "./SetupProviderSourceForm" | ||
import { ALLOWED_NAMES_REGEX, mergeProviderOptions } from "./helpers" | ||
import { FieldName, TCloneProviderInfo, TFormValues, TSetupProviderResult } from "./types" | ||
import { useAddProvider } from "./useAddProvider" | ||
import { useCallback } from "react" | ||
import { LoadingProviderIndicator } from "./LoadingProviderIndicator" | ||
|
||
const Form = styled.form` | ||
width: 100%; | ||
display: flex; | ||
flex-flow: column nowrap; | ||
justify-content: center; | ||
` | ||
|
||
type TCloneProviderProps = Readonly<{ | ||
isModal?: boolean | ||
cloneProviderInfo: TCloneProviderInfo | ||
onFinish: (result: TSetupProviderResult) => void | ||
reset: () => void | ||
}> | ||
export function SetupClonedProvider({ cloneProviderInfo, onFinish, reset }: TCloneProviderProps) { | ||
const [[providers]] = useProviders() | ||
const { handleSubmit, formState, control, watch } = useForm<TFormValues>({ | ||
defaultValues: { | ||
[FieldName.PROVIDER_SOURCE]: cloneProviderInfo.sourceProviderSource, | ||
}, | ||
}) | ||
const newProviderName = watch(FieldName.PROVIDER_NAME) | ||
const { providerNameError } = useFormErrors([FieldName.PROVIDER_NAME], formState) | ||
const { | ||
mutate: addProvider, | ||
status, | ||
error, | ||
} = useAddProvider({ | ||
onSuccess(result) { | ||
const oldProvider = cloneProviderInfo.sourceProvider | ||
|
||
onFinish({ | ||
providerID: result.providerID, | ||
optionGroups: result.optionGroups, | ||
options: mergeProviderOptions(oldProvider.state?.options, result.options), | ||
}) | ||
}, | ||
onError() { | ||
reset() | ||
}, | ||
}) | ||
const onSubmit = useCallback( | ||
async (values: TFormValues) => { | ||
addProvider({ | ||
rawProviderSource: values[FieldName.PROVIDER_SOURCE], | ||
config: { name: values[FieldName.PROVIDER_NAME] }, | ||
}) | ||
// gotta merge the options with the existing state now | ||
}, | ||
[addProvider] | ||
) | ||
const isLoading = status === "loading" | ||
|
||
return ( | ||
<> | ||
<VStack align="start" spacing={8} width="full" marginBottom={6}> | ||
{ | ||
<Form onSubmit={handleSubmit(onSubmit)}> | ||
<FormControl | ||
alignSelf="center" | ||
maxWidth={{ base: "3xl", xl: "4xl" }} | ||
marginBottom={4} | ||
isDisabled={isLoading || status === "success"} | ||
isInvalid={exists(providerNameError)}> | ||
<FormLabel>Name</FormLabel> | ||
<Controller | ||
name={FieldName.PROVIDER_NAME} | ||
control={control} | ||
rules={{ | ||
pattern: { | ||
value: ALLOWED_NAMES_REGEX, | ||
message: "Name can only contain letters, numbers and -", | ||
}, | ||
validate: { | ||
unique: (value) => { | ||
if (value === undefined) return true | ||
if (value === "") return "Name cannot be empty" | ||
|
||
return providers?.[value] === undefined ? true : "Name must be unique" | ||
}, | ||
}, | ||
maxLength: { value: 48, message: "Name cannot be longer than 48 characters" }, | ||
}} | ||
render={({ field }) => ( | ||
<CustomNameInput | ||
field={field} | ||
onAccept={handleSubmit(onSubmit)} | ||
isInvalid={exists(providerNameError)} | ||
isDisabled={isLoading || status === "success"} | ||
/> | ||
)} | ||
/> | ||
{exists(providerNameError) ? ( | ||
<FormErrorMessage>{providerNameError.message ?? "Error"}</FormErrorMessage> | ||
) : ( | ||
<FormHelperText> | ||
Please give your provider a different name from the one specified in its{" "} | ||
<Code>provider.yaml</Code> | ||
</FormHelperText> | ||
)} | ||
</FormControl> | ||
|
||
{status === "error" && isError(error) && <ErrorMessageBox error={error} />} | ||
{isLoading && ( | ||
<LoadingProviderIndicator | ||
label={`Cloning ${cloneProviderInfo.sourceProviderID} -> ${newProviderName}`} | ||
/> | ||
)} | ||
</Form> | ||
} | ||
</VStack> | ||
</> | ||
) | ||
} |
Oops, something went wrong.