fix(access-keys): show framed, per-field, localized create errors (#137)

This commit is contained in:
Adnan Hussain Turki
2026-06-16 00:32:03 +05:30
committed by GitHub
parent 8e3161e0e7
commit 55079b713e
15 changed files with 102 additions and 15 deletions
+46 -1
View File
@@ -37,6 +37,7 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
const [policy, setPolicy] = React.useState("")
const [impliedPolicy, setImpliedPolicy] = React.useState(true)
const [submitting, setSubmitting] = React.useState(false)
const [submitError, setSubmitError] = React.useState("")
const [errors, setErrors] = React.useState({
accessKey: "",
secretKey: "",
@@ -60,6 +61,7 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
setExpiry(null)
setImpliedPolicy(true)
setErrors({ accessKey: "", secretKey: "", name: "" })
setSubmitError("")
api
.get("/accountinfo")
.then((userInfo: { policy?: unknown; Policy?: unknown }) => {
@@ -92,6 +94,8 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
newErrors.accessKey = t("Please enter Access Key")
} else if (accessKey.length < 3 || accessKey.length > 20) {
newErrors.accessKey = t("Access Key length must be between 3 and 20 characters")
} else if (/\s/.test(accessKey)) {
newErrors.accessKey = t("Access Key cannot contain spaces")
}
if (!secretKey) {
newErrors.secretKey = t("Please enter Secret Key")
@@ -100,12 +104,42 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
}
if (!name) {
newErrors.name = t("Please enter name")
} else if (name.length > 32) {
newErrors.name = t("Name must be at most 32 characters")
} else if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(name)) {
newErrors.name = t(
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter",
)
}
setErrors(newErrors)
return !newErrors.accessKey && !newErrors.secretKey && !newErrors.name
}
// Map a server-side validation error to the field it belongs to, so the
// message renders directly under the relevant input. Returns false when the
// error is not field-specific (caller falls back to a general message).
const applyServerError = (reason: string): boolean => {
const lowered = reason.toLowerCase()
if (lowered.includes("access key") && lowered.includes("space")) {
setErrors((prev) => ({ ...prev, accessKey: t("Access Key cannot contain spaces") }))
return true
}
if (lowered.includes("name must contain only") || lowered.includes("name must start")) {
setErrors((prev) => ({
...prev,
name: t("Name can only contain letters, numbers, underscores and hyphens, and must start with a letter"),
}))
return true
}
if (lowered.includes("name must not be longer") || lowered.includes("name is too long")) {
setErrors((prev) => ({ ...prev, name: t("Name must be at most 32 characters") }))
return true
}
return false
}
const submitForm = async () => {
setSubmitError("")
if (!validate()) {
message.error(t("Please fill in the correct format"))
return
@@ -140,7 +174,12 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
onSuccess()
} catch (error) {
console.error(error)
message.error(t("Add failed"))
const reason = error instanceof Error && error.message ? error.message : ""
const handledByField = reason ? applyServerError(reason) : false
if (!handledByField) {
setSubmitError(reason || t("Add failed"))
}
message.error(reason || t("Add failed"))
} finally {
setSubmitting(false)
}
@@ -273,6 +312,12 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
)}
</div>
{submitError && (
<p role="alert" className="px-2 text-sm text-destructive">
{submitError}
</p>
)}
<DialogFooter className="border-t pt-4">
<Button variant="outline" onClick={closeModal}>
{t("Cancel")}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "جار تحضير التنزيل",
"User menu": "قائمة المستخدم",
"Cleanup Warnings": "تحذيرات التنظيف",
"Failed to load objects": "فشل تحميل الكائنات"
"Failed to load objects": "فشل تحميل الكائنات",
"Access Key cannot contain spaces": "لا يمكن أن يحتوي مفتاح الوصول على مسافات",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "يمكن أن يحتوي الاسم على أحرف وأرقام وشرطات سفلية وشرطات فقط، ويجب أن يبدأ بحرف",
"Name must be at most 32 characters": "يجب ألا يزيد الاسم عن 32 حرفًا"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Download wird vorbereitet",
"User menu": "Benutzermenü",
"Cleanup Warnings": "Bereinigungswarnungen",
"Failed to load objects": "Objekte konnten nicht geladen werden"
"Failed to load objects": "Objekte konnten nicht geladen werden",
"Access Key cannot contain spaces": "Der Zugriffsschlüssel darf keine Leerzeichen enthalten",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Der Name darf nur Buchstaben, Ziffern, Unterstriche und Bindestriche enthalten und muss mit einem Buchstaben beginnen",
"Name must be at most 32 characters": "Der Name darf höchstens 32 Zeichen lang sein"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Preparing download",
"User menu": "User menu",
"Cleanup Warnings": "Cleanup Warnings",
"Failed to load objects": "Failed to load objects"
"Failed to load objects": "Failed to load objects",
"Access Key cannot contain spaces": "Access Key cannot contain spaces",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Name can only contain letters, numbers, underscores and hyphens, and must start with a letter",
"Name must be at most 32 characters": "Name must be at most 32 characters"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Preparando descarga",
"User menu": "Menú de usuario",
"Cleanup Warnings": "Advertencias de limpieza",
"Failed to load objects": "No se pudieron cargar los objetos"
"Failed to load objects": "No se pudieron cargar los objetos",
"Access Key cannot contain spaces": "La clave de acceso no puede contener espacios",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "El nombre solo puede contener letras, números, guiones bajos y guiones, y debe comenzar con una letra",
"Name must be at most 32 characters": "El nombre no puede tener más de 32 caracteres"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Préparation du téléchargement",
"User menu": "Menu utilisateur",
"Cleanup Warnings": "Avertissements de nettoyage",
"Failed to load objects": "Impossible de charger les objets"
"Failed to load objects": "Impossible de charger les objets",
"Access Key cannot contain spaces": "La clé d'accès ne peut pas contenir d'espaces",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Le nom ne peut contenir que des lettres, des chiffres, des traits de soulignement et des traits d'union, et doit commencer par une lettre",
"Name must be at most 32 characters": "Le nom ne doit pas dépasser 32 caractères"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Menyiapkan unduhan",
"User menu": "Menu pengguna",
"Cleanup Warnings": "Peringatan pembersihan",
"Failed to load objects": "Gagal memuat objek"
"Failed to load objects": "Gagal memuat objek",
"Access Key cannot contain spaces": "Kunci akses tidak boleh mengandung spasi",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Nama hanya boleh berisi huruf, angka, garis bawah, dan tanda hubung, serta harus diawali dengan huruf",
"Name must be at most 32 characters": "Nama tidak boleh lebih dari 32 karakter"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Preparazione download",
"User menu": "Menu utente",
"Cleanup Warnings": "Avvisi di pulizia",
"Failed to load objects": "Impossibile caricare gli oggetti"
"Failed to load objects": "Impossibile caricare gli oggetti",
"Access Key cannot contain spaces": "La chiave di accesso non può contenere spazi",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Il nome può contenere solo lettere, numeri, trattini bassi e trattini, e deve iniziare con una lettera",
"Name must be at most 32 characters": "Il nome non può superare i 32 caratteri"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "ダウンロードを準備中",
"User menu": "ユーザーメニュー",
"Cleanup Warnings": "クリーンアップ警告",
"Failed to load objects": "オブジェクトの読み込みに失敗しました"
"Failed to load objects": "オブジェクトの読み込みに失敗しました",
"Access Key cannot contain spaces": "アクセスキーにスペースを含めることはできません",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "名前には英字、数字、アンダースコア、ハイフンのみ使用でき、英字で始まる必要があります",
"Name must be at most 32 characters": "名前は最大32文字です"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "다운로드 준비 중",
"User menu": "사용자 메뉴",
"Cleanup Warnings": "정리 경고",
"Failed to load objects": "객체를 불러오지 못했습니다"
"Failed to load objects": "객체를 불러오지 못했습니다",
"Access Key cannot contain spaces": "액세스 키에는 공백을 포함할 수 없습니다",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "이름은 영문자, 숫자, 밑줄, 하이픈만 사용할 수 있으며 영문자로 시작해야 합니다",
"Name must be at most 32 characters": "이름은 최대 32자입니다"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Preparando download",
"User menu": "Menu do usuário",
"Cleanup Warnings": "Avisos de limpeza",
"Failed to load objects": "Falha ao carregar objetos"
"Failed to load objects": "Falha ao carregar objetos",
"Access Key cannot contain spaces": "A chave de acesso não pode conter espaços",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "O nome só pode conter letras, números, sublinhados e hifens, e deve começar com uma letra",
"Name must be at most 32 characters": "O nome não pode ter mais de 32 caracteres"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Подготовка загрузки",
"User menu": "Меню пользователя",
"Cleanup Warnings": "Предупреждения очистки",
"Failed to load objects": "Не удалось загрузить объекты"
"Failed to load objects": "Не удалось загрузить объекты",
"Access Key cannot contain spaces": "Ключ доступа не может содержать пробелы",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Имя может содержать только буквы, цифры, символы подчёркивания и дефисы и должно начинаться с буквы",
"Name must be at most 32 characters": "Имя не должно превышать 32 символа"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "İndirme hazırlanıyor",
"User menu": "Kullanıcı menüsü",
"Cleanup Warnings": "Temizleme uyarıları",
"Failed to load objects": "Nesneler yüklenemedi"
"Failed to load objects": "Nesneler yüklenemedi",
"Access Key cannot contain spaces": "Erişim anahtarı boşluk içeremez",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Ad yalnızca harf, rakam, alt çizgi ve tire içerebilir ve bir harfle başlamalıdır",
"Name must be at most 32 characters": "Ad en fazla 32 karakter olmalıdır"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "Đang chuẩn bị tải xuống",
"User menu": "Menu người dùng",
"Cleanup Warnings": "Cảnh báo dọn dẹp",
"Failed to load objects": "Không thể tải đối tượng"
"Failed to load objects": "Không thể tải đối tượng",
"Access Key cannot contain spaces": "Khóa truy cập không được chứa khoảng trắng",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "Tên chỉ được chứa chữ cái, số, dấu gạch dưới và dấu gạch nối, và phải bắt đầu bằng một chữ cái",
"Name must be at most 32 characters": "Tên không được dài quá 32 ký tự"
}
+4 -1
View File
@@ -1335,5 +1335,8 @@
"Preparing download": "正在准备下载",
"User menu": "用户菜单",
"Cleanup Warnings": "清理警告",
"Failed to load objects": "加载对象失败"
"Failed to load objects": "加载对象失败",
"Access Key cannot contain spaces": "访问密钥不能包含空格",
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "名称只能包含字母、数字、下划线和连字符,并且必须以字母开头",
"Name must be at most 32 characters": "名称最多 32 个字符"
}