refactor(SettingDialog): extract reduplicate code to common component
DO NOT REPEAT YOURSELF!
This commit is contained in:
parent
996cc1b674
commit
776d389c1e
|
@ -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[
|
config.chatModelProviders[
|
||||||
selectedChatModelProvider
|
selectedChatModelProvider
|
||||||
].length > 0 ? (
|
];
|
||||||
config.chatModelProviders[
|
|
||||||
selectedChatModelProvider
|
return chatModelProvider
|
||||||
].map((model) => (
|
? chatModelProvider.length > 0
|
||||||
<option key={model} value={model}>
|
? chatModelProvider.map((model) => ({
|
||||||
{model}
|
value: model,
|
||||||
</option>
|
label: model,
|
||||||
))
|
}))
|
||||||
) : (
|
: [
|
||||||
<option value="" disabled>
|
{
|
||||||
No models available
|
value: '',
|
||||||
</option>
|
label: 'No models available',
|
||||||
)
|
disabled: true,
|
||||||
) : (
|
},
|
||||||
<option value="" disabled>
|
]
|
||||||
Invalid provider, please check backend logs
|
: [
|
||||||
</option>
|
{
|
||||||
)}
|
value: '',
|
||||||
</select>
|
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[
|
config.embeddingModelProviders[
|
||||||
selectedEmbeddingModelProvider
|
selectedEmbeddingModelProvider
|
||||||
].length > 0 ? (
|
];
|
||||||
config.embeddingModelProviders[
|
|
||||||
selectedEmbeddingModelProvider
|
return embeddingModelProvider
|
||||||
].map((model) => (
|
? embeddingModelProvider.length > 0
|
||||||
<option key={model} value={model}>
|
? embeddingModelProvider.map((model) => ({
|
||||||
{model}
|
label: model,
|
||||||
</option>
|
value: model,
|
||||||
))
|
}))
|
||||||
) : (
|
: [
|
||||||
<option value="" disabled selected>
|
{
|
||||||
No embedding models available
|
label: 'No embedding models available',
|
||||||
</option>
|
value: '',
|
||||||
)
|
disabled: true,
|
||||||
) : (
|
},
|
||||||
<option value="" disabled selected>
|
]
|
||||||
Invalid provider, please check backend logs
|
: [
|
||||||
</option>
|
{
|
||||||
)}
|
label:
|
||||||
</select>
|
'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>
|
||||||
|
|
Loading…
Reference in New Issue