import { useState, useTransition } from "react"; import { useShallow } from "zustand/react/shallow"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useKisRuntimeStore } from "@/features/settings/store/use-kis-runtime-store"; import { revokeKisCredentials, validateKisCredentials, } from "@/features/settings/apis/kis-auth.api"; import { KeyRound, Shield, CheckCircle2, XCircle, Lock, Sparkles, Zap, Activity, } from "lucide-react"; import { InlineSpinner } from "@/components/ui/loading-spinner"; /** * @description KIS 인증 입력 폼 (Minimal Redesign v4) * - User Feedback: "입력창이 너무 길어", "파란색/하늘색 제거해" * - Compact Width: max-w-lg + mx-auto * - Monochrome Mock Mode: Blue -> Zinc/Gray */ export function KisAuthForm() { const { kisTradingEnvInput, kisAppKeyInput, kisAppSecretInput, verifiedCredentials, isKisVerified, setKisTradingEnvInput, setKisAppKeyInput, setKisAppSecretInput, setVerifiedKisSession, invalidateKisVerification, clearKisRuntimeSession, } = useKisRuntimeStore( useShallow((state) => ({ kisTradingEnvInput: state.kisTradingEnvInput, kisAppKeyInput: state.kisAppKeyInput, kisAppSecretInput: state.kisAppSecretInput, verifiedCredentials: state.verifiedCredentials, isKisVerified: state.isKisVerified, setKisTradingEnvInput: state.setKisTradingEnvInput, setKisAppKeyInput: state.setKisAppKeyInput, setKisAppSecretInput: state.setKisAppSecretInput, setVerifiedKisSession: state.setVerifiedKisSession, invalidateKisVerification: state.invalidateKisVerification, clearKisRuntimeSession: state.clearKisRuntimeSession, })), ); const [statusMessage, setStatusMessage] = useState(null); const [errorMessage, setErrorMessage] = useState(null); const [isValidating, startValidateTransition] = useTransition(); const [isRevoking, startRevokeTransition] = useTransition(); // 입력 필드 Focus 상태 관리를 위한 State const [focusedField, setFocusedField] = useState< "appKey" | "appSecret" | null >(null); function handleValidate() { startValidateTransition(async () => { try { setErrorMessage(null); setStatusMessage(null); const appKey = kisAppKeyInput.trim(); const appSecret = kisAppSecretInput.trim(); if (!appKey || !appSecret) { throw new Error("App Key와 App Secret을 모두 입력해 주세요."); } const credentials = { appKey, appSecret, tradingEnv: kisTradingEnvInput, accountNo: verifiedCredentials?.accountNo ?? "", }; const result = await validateKisCredentials(credentials); setVerifiedKisSession(credentials, result.tradingEnv); setStatusMessage( `${result.message} (${result.tradingEnv === "real" ? "실전" : "모의"})`, ); } catch (err) { invalidateKisVerification(); setErrorMessage( err instanceof Error ? err.message : "API 키 검증 중 오류가 발생했습니다.", ); } }); } function handleRevoke() { if (!verifiedCredentials) return; startRevokeTransition(async () => { try { setErrorMessage(null); setStatusMessage(null); const result = await revokeKisCredentials(verifiedCredentials); clearKisRuntimeSession(result.tradingEnv); setStatusMessage( `${result.message} (${result.tradingEnv === "real" ? "실전" : "모의"})`, ); } catch (err) { setErrorMessage( err instanceof Error ? err.message : "연결 해제 중 오류가 발생했습니다.", ); } }); } return (
{/* Inner Content Container - Compact spacing */}
{/* Header Section */}

KIS API Key Connection {isKisVerified && ( Connected )}

한국투자증권에서 발급받은 API 키를 입력해 주세요.

{/* Trading Mode Switch (Segmented Control - Compact) */}
{/* Input Fields Section (Compact Stacked Layout) */}
{/* App Key Input */}
App Key
setKisAppKeyInput(e.target.value)} onFocus={() => setFocusedField("appKey")} onBlur={() => setFocusedField(null)} placeholder="App Key 입력" className="h-9 flex-1 border-none bg-transparent px-3 text-xs text-zinc-900 placeholder:text-zinc-400 focus-visible:ring-0 dark:text-zinc-100 dark:placeholder:text-zinc-600" autoComplete="off" />
{/* App Secret Input */}
Secret
setKisAppSecretInput(e.target.value)} onFocus={() => setFocusedField("appSecret")} onBlur={() => setFocusedField(null)} placeholder="App Secret 입력" className="h-9 flex-1 border-none bg-transparent px-3 text-xs text-zinc-900 placeholder:text-zinc-400 focus-visible:ring-0 dark:text-zinc-100 dark:placeholder:text-zinc-600" autoComplete="off" />
{/* Action & Status Section */}
{isKisVerified && ( )}
{/* Status Messages - Compact */}
{errorMessage && (

{errorMessage}

)} {statusMessage && (

{statusMessage}

)} {!errorMessage && !statusMessage && !isKisVerified && (

미연결

)}
); }