refactor: improve form validation in CodexConfigEditor using HTML5 validation API
- Replace custom error state with native HTML5 form validation - Add useRef hooks for input field validation management - Add pattern attributes to enforce non-empty input validation - Leverage browser's built-in validation UI for better UX - Extract closeTemplateModal function for consistent modal closing
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
|
||||||
import { X, Save } from "lucide-react";
|
import { X, Save } from "lucide-react";
|
||||||
|
|
||||||
@@ -92,8 +92,11 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
const [templateWebsiteUrl, setTemplateWebsiteUrl] = useState("");
|
const [templateWebsiteUrl, setTemplateWebsiteUrl] = useState("");
|
||||||
|
|
||||||
const [templateModelName, setTemplateModelName] = useState("gpt-5-codex");
|
const [templateModelName, setTemplateModelName] = useState("gpt-5-codex");
|
||||||
|
const apiKeyInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [templateError, setTemplateError] = useState("");
|
const baseUrlInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const modelNameInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
// 移除自动填充逻辑,因为现在在点击自定义按钮时就已经填充
|
// 移除自动填充逻辑,因为现在在点击自定义按钮时就已经填充
|
||||||
|
|
||||||
@@ -125,19 +128,31 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
setIsCommonConfigModalOpen(false);
|
setIsCommonConfigModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeTemplateModal = () => {
|
||||||
|
setIsTemplateModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
const applyTemplate = () => {
|
const applyTemplate = () => {
|
||||||
|
const requiredInputs = [
|
||||||
|
apiKeyInputRef.current,
|
||||||
|
baseUrlInputRef.current,
|
||||||
|
modelNameInputRef.current,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const input of requiredInputs) {
|
||||||
|
if (input && !input.checkValidity()) {
|
||||||
|
input.reportValidity();
|
||||||
|
input.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const trimmedKey = templateApiKey.trim();
|
const trimmedKey = templateApiKey.trim();
|
||||||
|
|
||||||
const trimmedBaseUrl = templateBaseUrl.trim();
|
const trimmedBaseUrl = templateBaseUrl.trim();
|
||||||
|
|
||||||
const trimmedModel = templateModelName.trim();
|
const trimmedModel = templateModelName.trim();
|
||||||
|
|
||||||
if (!trimmedKey || !trimmedBaseUrl || !trimmedModel) {
|
|
||||||
setTemplateError("请填写 API 密钥、API 基础地址和模型名称");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auth = generateThirdPartyAuth(trimmedKey);
|
const auth = generateThirdPartyAuth(trimmedKey);
|
||||||
|
|
||||||
const config = generateThirdPartyConfig(
|
const config = generateThirdPartyConfig(
|
||||||
@@ -170,9 +185,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
|
|
||||||
setTemplateModelName("gpt-5-codex");
|
setTemplateModelName("gpt-5-codex");
|
||||||
|
|
||||||
setTemplateError("");
|
closeTemplateModal();
|
||||||
|
|
||||||
setIsTemplateModalOpen(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTemplateInputKeyDown = (
|
const handleTemplateInputKeyDown = (
|
||||||
@@ -310,7 +323,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
className="fixed inset-0 z-50 flex items-center justify-center"
|
className="fixed inset-0 z-50 flex items-center justify-center"
|
||||||
onMouseDown={(e) => {
|
onMouseDown={(e) => {
|
||||||
if (e.target === e.currentTarget) {
|
if (e.target === e.currentTarget) {
|
||||||
setIsTemplateModalOpen(false);
|
closeTemplateModal();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -329,7 +342,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsTemplateModalOpen(false)}
|
onClick={closeTemplateModal}
|
||||||
className="rounded-md p-1 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100"
|
className="rounded-md p-1 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100"
|
||||||
aria-label="关闭"
|
aria-label="关闭"
|
||||||
>
|
>
|
||||||
@@ -354,12 +367,11 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={templateApiKey}
|
value={templateApiKey}
|
||||||
onChange={(e) => {
|
ref={apiKeyInputRef}
|
||||||
setTemplateApiKey(e.target.value);
|
onChange={(e) => setTemplateApiKey(e.target.value)}
|
||||||
|
|
||||||
setTemplateError("");
|
|
||||||
}}
|
|
||||||
onKeyDown={handleTemplateInputKeyDown}
|
onKeyDown={handleTemplateInputKeyDown}
|
||||||
|
pattern=".*\S.*"
|
||||||
|
title="请输入有效的内容"
|
||||||
placeholder="sk-your-api-key-here"
|
placeholder="sk-your-api-key-here"
|
||||||
required
|
required
|
||||||
className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm font-mono text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
|
className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm font-mono text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
|
||||||
@@ -393,11 +405,8 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
<input
|
<input
|
||||||
type="url"
|
type="url"
|
||||||
value={templateBaseUrl}
|
value={templateBaseUrl}
|
||||||
onChange={(e) => {
|
ref={baseUrlInputRef}
|
||||||
setTemplateBaseUrl(e.target.value);
|
onChange={(e) => setTemplateBaseUrl(e.target.value)}
|
||||||
|
|
||||||
setTemplateError("");
|
|
||||||
}}
|
|
||||||
onKeyDown={handleTemplateInputKeyDown}
|
onKeyDown={handleTemplateInputKeyDown}
|
||||||
placeholder="https://your-api-endpoint.com/v1"
|
placeholder="https://your-api-endpoint.com/v1"
|
||||||
required
|
required
|
||||||
@@ -432,12 +441,11 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={templateModelName}
|
value={templateModelName}
|
||||||
onChange={(e) => {
|
ref={modelNameInputRef}
|
||||||
setTemplateModelName(e.target.value);
|
onChange={(e) => setTemplateModelName(e.target.value)}
|
||||||
|
|
||||||
setTemplateError("");
|
|
||||||
}}
|
|
||||||
onKeyDown={handleTemplateInputKeyDown}
|
onKeyDown={handleTemplateInputKeyDown}
|
||||||
|
pattern=".*\S.*"
|
||||||
|
title="请输入有效的内容"
|
||||||
placeholder="gpt-5-codex"
|
placeholder="gpt-5-codex"
|
||||||
required
|
required
|
||||||
className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
|
className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
|
||||||
@@ -489,17 +497,12 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{templateError && (
|
|
||||||
<p className="text-sm text-red-500 dark:text-red-400">
|
|
||||||
{templateError}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-end gap-3 border-t border-gray-200 bg-gray-100 p-6 dark:border-gray-800 dark:bg-gray-800">
|
<div className="flex items-center justify-end gap-3 border-t border-gray-200 bg-gray-100 p-6 dark:border-gray-800 dark:bg-gray-800">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsTemplateModalOpen(false)}
|
onClick={closeTemplateModal}
|
||||||
className="rounded-lg px-4 py-2 text-sm font-medium text-gray-500 transition-colors hover:bg-white hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-100"
|
className="rounded-lg px-4 py-2 text-sm font-medium text-gray-500 transition-colors hover:bg-white hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-100"
|
||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
|
|||||||
Reference in New Issue
Block a user