Skip to content

Commit 55079b7

Browse files
fix(access-keys): show framed, per-field, localized create errors (#137)
1 parent 8e3161e commit 55079b7

15 files changed

Lines changed: 102 additions & 15 deletions

File tree

components/access-keys/new-item.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
3737
const [policy, setPolicy] = React.useState("")
3838
const [impliedPolicy, setImpliedPolicy] = React.useState(true)
3939
const [submitting, setSubmitting] = React.useState(false)
40+
const [submitError, setSubmitError] = React.useState("")
4041
const [errors, setErrors] = React.useState({
4142
accessKey: "",
4243
secretKey: "",
@@ -60,6 +61,7 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
6061
setExpiry(null)
6162
setImpliedPolicy(true)
6263
setErrors({ accessKey: "", secretKey: "", name: "" })
64+
setSubmitError("")
6365
api
6466
.get("/accountinfo")
6567
.then((userInfo: { policy?: unknown; Policy?: unknown }) => {
@@ -92,6 +94,8 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
9294
newErrors.accessKey = t("Please enter Access Key")
9395
} else if (accessKey.length < 3 || accessKey.length > 20) {
9496
newErrors.accessKey = t("Access Key length must be between 3 and 20 characters")
97+
} else if (/\s/.test(accessKey)) {
98+
newErrors.accessKey = t("Access Key cannot contain spaces")
9599
}
96100
if (!secretKey) {
97101
newErrors.secretKey = t("Please enter Secret Key")
@@ -100,12 +104,42 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
100104
}
101105
if (!name) {
102106
newErrors.name = t("Please enter name")
107+
} else if (name.length > 32) {
108+
newErrors.name = t("Name must be at most 32 characters")
109+
} else if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(name)) {
110+
newErrors.name = t(
111+
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter",
112+
)
103113
}
104114
setErrors(newErrors)
105115
return !newErrors.accessKey && !newErrors.secretKey && !newErrors.name
106116
}
107117

118+
// Map a server-side validation error to the field it belongs to, so the
119+
// message renders directly under the relevant input. Returns false when the
120+
// error is not field-specific (caller falls back to a general message).
121+
const applyServerError = (reason: string): boolean => {
122+
const lowered = reason.toLowerCase()
123+
if (lowered.includes("access key") && lowered.includes("space")) {
124+
setErrors((prev) => ({ ...prev, accessKey: t("Access Key cannot contain spaces") }))
125+
return true
126+
}
127+
if (lowered.includes("name must contain only") || lowered.includes("name must start")) {
128+
setErrors((prev) => ({
129+
...prev,
130+
name: t("Name can only contain letters, numbers, underscores and hyphens, and must start with a letter"),
131+
}))
132+
return true
133+
}
134+
if (lowered.includes("name must not be longer") || lowered.includes("name is too long")) {
135+
setErrors((prev) => ({ ...prev, name: t("Name must be at most 32 characters") }))
136+
return true
137+
}
138+
return false
139+
}
140+
108141
const submitForm = async () => {
142+
setSubmitError("")
109143
if (!validate()) {
110144
message.error(t("Please fill in the correct format"))
111145
return
@@ -140,7 +174,12 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
140174
onSuccess()
141175
} catch (error) {
142176
console.error(error)
143-
message.error(t("Add failed"))
177+
const reason = error instanceof Error && error.message ? error.message : ""
178+
const handledByField = reason ? applyServerError(reason) : false
179+
if (!handledByField) {
180+
setSubmitError(reason || t("Add failed"))
181+
}
182+
message.error(reason || t("Add failed"))
144183
} finally {
145184
setSubmitting(false)
146185
}
@@ -273,6 +312,12 @@ export function AccessKeysNewItem({ visible, onVisibleChange, onSuccess, onNotic
273312
)}
274313
</div>
275314

315+
{submitError && (
316+
<p role="alert" className="px-2 text-sm text-destructive">
317+
{submitError}
318+
</p>
319+
)}
320+
276321
<DialogFooter className="border-t pt-4">
277322
<Button variant="outline" onClick={closeModal}>
278323
{t("Cancel")}

i18n/locales/ar-MA.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "جار تحضير التنزيل",
13361336
"User menu": "قائمة المستخدم",
13371337
"Cleanup Warnings": "تحذيرات التنظيف",
1338-
"Failed to load objects": "فشل تحميل الكائنات"
1338+
"Failed to load objects": "فشل تحميل الكائنات",
1339+
"Access Key cannot contain spaces": "لا يمكن أن يحتوي مفتاح الوصول على مسافات",
1340+
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "يمكن أن يحتوي الاسم على أحرف وأرقام وشرطات سفلية وشرطات فقط، ويجب أن يبدأ بحرف",
1341+
"Name must be at most 32 characters": "يجب ألا يزيد الاسم عن 32 حرفًا"
13391342
}

i18n/locales/de-DE.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "Download wird vorbereitet",
13361336
"User menu": "Benutzermenü",
13371337
"Cleanup Warnings": "Bereinigungswarnungen",
1338-
"Failed to load objects": "Objekte konnten nicht geladen werden"
1338+
"Failed to load objects": "Objekte konnten nicht geladen werden",
1339+
"Access Key cannot contain spaces": "Der Zugriffsschlüssel darf keine Leerzeichen enthalten",
1340+
"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",
1341+
"Name must be at most 32 characters": "Der Name darf höchstens 32 Zeichen lang sein"
13391342
}

i18n/locales/en-US.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "Preparing download",
13361336
"User menu": "User menu",
13371337
"Cleanup Warnings": "Cleanup Warnings",
1338-
"Failed to load objects": "Failed to load objects"
1338+
"Failed to load objects": "Failed to load objects",
1339+
"Access Key cannot contain spaces": "Access Key cannot contain spaces",
1340+
"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",
1341+
"Name must be at most 32 characters": "Name must be at most 32 characters"
13391342
}

i18n/locales/es-ES.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "Preparando descarga",
13361336
"User menu": "Menú de usuario",
13371337
"Cleanup Warnings": "Advertencias de limpieza",
1338-
"Failed to load objects": "No se pudieron cargar los objetos"
1338+
"Failed to load objects": "No se pudieron cargar los objetos",
1339+
"Access Key cannot contain spaces": "La clave de acceso no puede contener espacios",
1340+
"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",
1341+
"Name must be at most 32 characters": "El nombre no puede tener más de 32 caracteres"
13391342
}

i18n/locales/fr-FR.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "Préparation du téléchargement",
13361336
"User menu": "Menu utilisateur",
13371337
"Cleanup Warnings": "Avertissements de nettoyage",
1338-
"Failed to load objects": "Impossible de charger les objets"
1338+
"Failed to load objects": "Impossible de charger les objets",
1339+
"Access Key cannot contain spaces": "La clé d'accès ne peut pas contenir d'espaces",
1340+
"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",
1341+
"Name must be at most 32 characters": "Le nom ne doit pas dépasser 32 caractères"
13391342
}

i18n/locales/id-ID.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "Menyiapkan unduhan",
13361336
"User menu": "Menu pengguna",
13371337
"Cleanup Warnings": "Peringatan pembersihan",
1338-
"Failed to load objects": "Gagal memuat objek"
1338+
"Failed to load objects": "Gagal memuat objek",
1339+
"Access Key cannot contain spaces": "Kunci akses tidak boleh mengandung spasi",
1340+
"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",
1341+
"Name must be at most 32 characters": "Nama tidak boleh lebih dari 32 karakter"
13391342
}

i18n/locales/it-IT.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "Preparazione download",
13361336
"User menu": "Menu utente",
13371337
"Cleanup Warnings": "Avvisi di pulizia",
1338-
"Failed to load objects": "Impossibile caricare gli oggetti"
1338+
"Failed to load objects": "Impossibile caricare gli oggetti",
1339+
"Access Key cannot contain spaces": "La chiave di accesso non può contenere spazi",
1340+
"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",
1341+
"Name must be at most 32 characters": "Il nome non può superare i 32 caratteri"
13391342
}

i18n/locales/ja-JP.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "ダウンロードを準備中",
13361336
"User menu": "ユーザーメニュー",
13371337
"Cleanup Warnings": "クリーンアップ警告",
1338-
"Failed to load objects": "オブジェクトの読み込みに失敗しました"
1338+
"Failed to load objects": "オブジェクトの読み込みに失敗しました",
1339+
"Access Key cannot contain spaces": "アクセスキーにスペースを含めることはできません",
1340+
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "名前には英字、数字、アンダースコア、ハイフンのみ使用でき、英字で始まる必要があります",
1341+
"Name must be at most 32 characters": "名前は最大32文字です"
13391342
}

i18n/locales/ko-KR.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,5 +1335,8 @@
13351335
"Preparing download": "다운로드 준비 중",
13361336
"User menu": "사용자 메뉴",
13371337
"Cleanup Warnings": "정리 경고",
1338-
"Failed to load objects": "객체를 불러오지 못했습니다"
1338+
"Failed to load objects": "객체를 불러오지 못했습니다",
1339+
"Access Key cannot contain spaces": "액세스 키에는 공백을 포함할 수 없습니다",
1340+
"Name can only contain letters, numbers, underscores and hyphens, and must start with a letter": "이름은 영문자, 숫자, 밑줄, 하이픈만 사용할 수 있으며 영문자로 시작해야 합니다",
1341+
"Name must be at most 32 characters": "이름은 최대 32자입니다"
13391342
}

0 commit comments

Comments
 (0)