* Refactor translation imports and update text for localization across settings and system monitor routes - Changed translation import from 'react-i18next' to '@/i18n/react-i18next-compat' in multiple files. - Updated various text strings to use translation keys for better localization support in: - Local API Server settings - MCP Servers settings - Privacy settings - Provider settings - Shortcuts settings - System Monitor - Thread details - Ensured consistent use of translation keys for all user-facing text. Update web-app/src/routes/settings/appearance.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Update web-app/src/routes/settings/appearance.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Update web-app/src/locales/vn/settings.json Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Update web-app/src/containers/dialogs/DeleteMCPServerConfirm.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Update web-app/src/locales/id/common.json Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Add Chinese (Simplified and Traditional) localization files for various components - Created `tools.json`, `updater.json`, `assistants.json`, `chat.json`, `common.json`, `hub.json`, `logs.json`, `mcp-servers.json`, `provider.json`, `providers.json`, `settings.json`, `setup.json`, `system-monitor.json`, `tool-approval.json` in both `zh-CN` and `zh-TW` locales. - Added translations for tool approval, updater notifications, assistant management, chat interface, common UI elements, hub interactions, logging messages, MCP server configurations, provider management, settings options, setup instructions, and system monitoring. * Refactor localization strings for improved clarity and consistency in English, Indonesian, and Vietnamese settings files * Fix missing key and reword * fix pr comment --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import { Input } from '@/components/ui/input'
|
|
import { useLocalApiServer } from '@/hooks/useLocalApiServer'
|
|
import { useState, useEffect, useCallback } from 'react'
|
|
import { Eye, EyeOff } from 'lucide-react'
|
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
|
|
|
interface ApiKeyInputProps {
|
|
showError?: boolean
|
|
onValidationChange?: (isValid: boolean) => void
|
|
}
|
|
|
|
export function ApiKeyInput({
|
|
showError = false,
|
|
onValidationChange,
|
|
}: ApiKeyInputProps) {
|
|
const { apiKey, setApiKey } = useLocalApiServer()
|
|
const [inputValue, setInputValue] = useState(apiKey.toString())
|
|
const [showPassword, setShowPassword] = useState(false)
|
|
const [error, setError] = useState('')
|
|
const { t } = useTranslation()
|
|
|
|
const validateApiKey = useCallback((value: string) => {
|
|
if (!value || value.trim().length === 0) {
|
|
setError(t('common:apiKeyRequired'))
|
|
onValidationChange?.(false)
|
|
return false
|
|
}
|
|
setError('')
|
|
onValidationChange?.(true)
|
|
return true
|
|
}, [onValidationChange, t])
|
|
|
|
useEffect(() => {
|
|
if (showError) {
|
|
validateApiKey(inputValue)
|
|
}
|
|
}, [showError, inputValue, validateApiKey])
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const value = e.target.value
|
|
setInputValue(value)
|
|
|
|
// Clear error when user starts typing
|
|
if (error && value.trim().length > 0) {
|
|
setError('')
|
|
onValidationChange?.(true)
|
|
}
|
|
}
|
|
|
|
const handleBlur = () => {
|
|
setApiKey(inputValue)
|
|
// Validate on blur if showError is true
|
|
if (showError) {
|
|
validateApiKey(inputValue)
|
|
}
|
|
}
|
|
|
|
const hasError = error && showError
|
|
|
|
return (
|
|
<div className="relative w-full">
|
|
<Input
|
|
type={showPassword ? 'text' : 'password'}
|
|
value={inputValue}
|
|
onChange={handleChange}
|
|
onBlur={handleBlur}
|
|
className={`w-full text-sm pr-10 ${
|
|
hasError
|
|
? 'border-1 border-destructive focus:border-destructive focus:ring-destructive'
|
|
: ''
|
|
}`}
|
|
placeholder={t('common:enterApiKey')}
|
|
/>
|
|
<div className="absolute right-2 top-1/2 transform -translate-y-1/2 flex items-center gap-1">
|
|
<button
|
|
onClick={() => setShowPassword(!showPassword)}
|
|
className="p-1 rounded hover:bg-main-view-fg/5 text-main-view-fg/70"
|
|
type="button"
|
|
>
|
|
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
|
|
</button>
|
|
</div>
|
|
{hasError && (
|
|
<p className="text-destructive text-xs mt-1 absolute -bottom-5 left-0">
|
|
{error}
|
|
</p>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|