jan/web-app/src/containers/ApiKeyInput.tsx
Louis cb3ac4b136
feat: Jan API Server should have API Key setting (#5193)
* feat: Jan API Server should have API Key setting

* chore: reveal api key icon

* Update src-tauri/src/core/server.rs

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* chore: add validation apiKey

---------

Co-authored-by: Faisal Amir <urmauur@gmail.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-06-04 18:04:47 +07:00

89 lines
2.4 KiB
TypeScript

import { Input } from '@/components/ui/input'
import { useLocalApiServer } from '@/hooks/useLocalApiServer'
import { useState, useEffect } from 'react'
import { Eye, EyeOff } from 'lucide-react'
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 validateApiKey = (value: string) => {
if (!value || value.trim().length === 0) {
setError('API Key is required')
onValidationChange?.(false)
return false
}
setError('')
onValidationChange?.(true)
return true
}
useEffect(() => {
if (showError) {
validateApiKey(inputValue)
}
}, [showError, inputValue])
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="Enter API Key"
/>
<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>
)
}