refactor(SettingDialog): extract reduplicate code to common component

DO NOT REPEAT YOURSELF!
This commit is contained in:
WanQuanXie 2024-05-24 21:58:14 +08:00
parent 996cc1b674
commit 776d389c1e
1 changed files with 127 additions and 86 deletions

View File

@ -1,6 +1,51 @@
import { cn } from '@/lib/utils';
import { Dialog, Transition } from '@headlessui/react'; import { Dialog, Transition } from '@headlessui/react';
import { CloudUpload, RefreshCcw, RefreshCw } from 'lucide-react'; import { CloudUpload, RefreshCcw, RefreshCw } from 'lucide-react';
import React, { Fragment, useEffect, useState } from 'react'; import React, {
Fragment,
useEffect,
useMemo,
useState,
type SelectHTMLAttributes,
} from 'react';
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
function Input({ className, ...restProps }: InputProps) {
return (
<input
{...restProps}
className={cn(
'bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm',
className,
)}
/>
);
}
interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
options: { value: string; label: string; disabled?: boolean }[];
}
function Select({ className, options, ...restProps }: SelectProps) {
return (
<select
{...restProps}
className={cn(
'bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm',
className,
)}
>
{options.map(({ label, value, disabled }) => {
return (
<option key={value} value={value} disabled={disabled}>
{label}
</option>
);
})}
</select>
);
}
interface SettingsType { interface SettingsType {
chatModelProviders: { chatModelProviders: {
@ -169,7 +214,7 @@ const SettingsDialog = ({
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Chat model Provider Chat model Provider
</p> </p>
<select <Select
value={selectedChatModelProvider ?? undefined} value={selectedChatModelProvider ?? undefined}
onChange={(e) => { onChange={(e) => {
setSelectedChatModelProvider(e.target.value); setSelectedChatModelProvider(e.target.value);
@ -177,17 +222,15 @@ const SettingsDialog = ({
config.chatModelProviders[e.target.value][0], config.chatModelProviders[e.target.value][0],
); );
}} }}
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm" options={Object.keys(config.chatModelProviders).map(
> (provider) => ({
{Object.keys(config.chatModelProviders).map( value: provider,
(provider) => ( label:
<option key={provider} value={provider}> provider.charAt(0).toUpperCase() +
{provider.charAt(0).toUpperCase() + provider.slice(1),
provider.slice(1)} }),
</option>
),
)} )}
</select> />
</div> </div>
)} )}
{selectedChatModelProvider && {selectedChatModelProvider &&
@ -196,37 +239,40 @@ const SettingsDialog = ({
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Chat Model Chat Model
</p> </p>
<select <Select
value={selectedChatModel ?? undefined} value={selectedChatModel ?? undefined}
onChange={(e) => onChange={(e) =>
setSelectedChatModel(e.target.value) setSelectedChatModel(e.target.value)
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm" options={(() => {
> const chatModelProvider =
{config.chatModelProviders[
selectedChatModelProvider
] ? (
config.chatModelProviders[
selectedChatModelProvider
].length > 0 ? (
config.chatModelProviders[ config.chatModelProviders[
selectedChatModelProvider selectedChatModelProvider
].map((model) => ( ];
<option key={model} value={model}>
{model} return chatModelProvider
</option> ? chatModelProvider.length > 0
)) ? chatModelProvider.map((model) => ({
) : ( value: model,
<option value="" disabled> label: model,
No models available }))
</option> : [
) {
) : ( value: '',
<option value="" disabled> label: 'No models available',
Invalid provider, please check backend logs disabled: true,
</option> },
)} ]
</select> : [
{
value: '',
label:
'Invalid provider, please check backend logs',
disabled: true,
},
];
})()}
/>
</div> </div>
)} )}
{selectedChatModelProvider && {selectedChatModelProvider &&
@ -236,42 +282,39 @@ const SettingsDialog = ({
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Model name Model name
</p> </p>
<input <Input
type="text" type="text"
placeholder="Model name" placeholder="Model name"
defaultValue={selectedChatModel!} defaultValue={selectedChatModel!}
onChange={(e) => onChange={(e) =>
setSelectedChatModel(e.target.value) setSelectedChatModel(e.target.value)
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Custom OpenAI API Key Custom OpenAI API Key
</p> </p>
<input <Input
type="text" type="text"
placeholder="Custom OpenAI API Key" placeholder="Custom OpenAI API Key"
defaultValue={customOpenAIApiKey!} defaultValue={customOpenAIApiKey!}
onChange={(e) => onChange={(e) =>
setCustomOpenAIApiKey(e.target.value) setCustomOpenAIApiKey(e.target.value)
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Custom OpenAI Base URL Custom OpenAI Base URL
</p> </p>
<input <Input
type="text" type="text"
placeholder="Custom OpenAI Base URL" placeholder="Custom OpenAI Base URL"
defaultValue={customOpenAIBaseURL!} defaultValue={customOpenAIBaseURL!}
onChange={(e) => onChange={(e) =>
setCustomOpenAIBaseURL(e.target.value) setCustomOpenAIBaseURL(e.target.value)
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
</> </>
@ -282,7 +325,7 @@ const SettingsDialog = ({
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Embedding model Provider Embedding model Provider
</p> </p>
<select <Select
value={selectedEmbeddingModelProvider ?? undefined} value={selectedEmbeddingModelProvider ?? undefined}
onChange={(e) => { onChange={(e) => {
setSelectedEmbeddingModelProvider(e.target.value); setSelectedEmbeddingModelProvider(e.target.value);
@ -290,17 +333,15 @@ const SettingsDialog = ({
config.embeddingModelProviders[e.target.value][0], config.embeddingModelProviders[e.target.value][0],
); );
}} }}
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm" options={Object.keys(
> config.embeddingModelProviders,
{Object.keys(config.embeddingModelProviders).map( ).map((provider) => ({
(provider) => ( label:
<option key={provider} value={provider}> provider.charAt(0).toUpperCase() +
{provider.charAt(0).toUpperCase() + provider.slice(1),
provider.slice(1)} value: provider,
</option> }))}
), />
)}
</select>
</div> </div>
)} )}
{selectedEmbeddingModelProvider && ( {selectedEmbeddingModelProvider && (
@ -308,44 +349,47 @@ const SettingsDialog = ({
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Embedding Model Embedding Model
</p> </p>
<select <Select
value={selectedEmbeddingModel ?? undefined} value={selectedEmbeddingModel ?? undefined}
onChange={(e) => onChange={(e) =>
setSelectedEmbeddingModel(e.target.value) setSelectedEmbeddingModel(e.target.value)
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm" options={(() => {
> const embeddingModelProvider =
{config.embeddingModelProviders[
selectedEmbeddingModelProvider
] ? (
config.embeddingModelProviders[
selectedEmbeddingModelProvider
].length > 0 ? (
config.embeddingModelProviders[ config.embeddingModelProviders[
selectedEmbeddingModelProvider selectedEmbeddingModelProvider
].map((model) => ( ];
<option key={model} value={model}>
{model} return embeddingModelProvider
</option> ? embeddingModelProvider.length > 0
)) ? embeddingModelProvider.map((model) => ({
) : ( label: model,
<option value="" disabled selected> value: model,
No embedding models available }))
</option> : [
) {
) : ( label: 'No embedding models available',
<option value="" disabled selected> value: '',
Invalid provider, please check backend logs disabled: true,
</option> },
)} ]
</select> : [
{
label:
'Invalid provider, please check backend logs',
value: '',
disabled: true,
},
];
})()}
/>
</div> </div>
)} )}
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
OpenAI API Key OpenAI API Key
</p> </p>
<input <Input
type="text" type="text"
placeholder="OpenAI API Key" placeholder="OpenAI API Key"
defaultValue={config.openaiApiKey} defaultValue={config.openaiApiKey}
@ -355,14 +399,13 @@ const SettingsDialog = ({
openaiApiKey: e.target.value, openaiApiKey: e.target.value,
}) })
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
Ollama API URL Ollama API URL
</p> </p>
<input <Input
type="text" type="text"
placeholder="Ollama API URL" placeholder="Ollama API URL"
defaultValue={config.ollamaApiUrl} defaultValue={config.ollamaApiUrl}
@ -372,14 +415,13 @@ const SettingsDialog = ({
ollamaApiUrl: e.target.value, ollamaApiUrl: e.target.value,
}) })
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<p className="text-black/70 dark:text-white/70 text-sm"> <p className="text-black/70 dark:text-white/70 text-sm">
GROQ API Key GROQ API Key
</p> </p>
<input <Input
type="text" type="text"
placeholder="GROQ API Key" placeholder="GROQ API Key"
defaultValue={config.groqApiKey} defaultValue={config.groqApiKey}
@ -389,7 +431,6 @@ const SettingsDialog = ({
groqApiKey: e.target.value, groqApiKey: e.target.value,
}) })
} }
className="bg-primaryLight dark:bg-primaryDark px-3 py-2 flex items-center overflow-hidden border border-light dark:border-dark dark:text-white rounded-lg text-sm"
/> />
</div> </div>
</div> </div>