--- name: polish-forms description: Improve form validation across Archipelago UI with real-time feedback, input sanitization, disabled states during submission, and consistent error messaging. Use when user says "polish forms", "form validation", "input validation", or "fix forms". --- # Skill: Polish Form Validation Improve all form inputs to have real-time validation feedback, proper trimming, disabled states during submission, and consistent error messaging. ## Forms to Polish ### 1. Login.vue — Password Setup - Real-time validation as user types (debounced 300ms): - Length >= 8 chars (show checkmark/X) - Passwords match (show match indicator) - Trim input on submit - Disable submit button while `isSubmitting` - Clear error on new input ### 2. Login.vue — TOTP Verification - `inputmode="numeric"` + `pattern="[0-9]*"` - Auto-submit when 6 digits entered - Show session timeout countdown if applicable - Trim and strip non-numeric characters on paste ### 3. Settings.vue — Password Change - Real-time strength validation: - 12+ characters - Has uppercase, lowercase, digit, special char - New password matches confirmation - Show strength meter (weak/medium/strong) - Disable button during submission - Show spinner in button during async operation ### 4. Any other form inputs found across views ## Validation Pattern ```typescript const password = ref('') const confirmPassword = ref('') const isSubmitting = ref(false) const passwordErrors = computed(() => { const errors: string[] = [] if (password.value.length > 0 && password.value.length < 8) errors.push('Must be at least 8 characters') return errors }) const passwordsMatch = computed(() => confirmPassword.value.length > 0 && password.value === confirmPassword.value ) async function submit() { if (isSubmitting.value) return isSubmitting.value = true try { await rpcClient.call(...) } catch (err) { errorMessage.value = formatError(err) } finally { isSubmitting.value = false } } ``` ## Template Pattern ```vue ``` ## Input Trimming All text inputs should be trimmed before submission: ```typescript const trimmed = password.value.trim() ``` ## Error Message Consistency Create or use a `formatError` utility: ```typescript function formatError(err: unknown): string { if (err instanceof Error) { if (err.message.includes('fetch') || err.message.includes('network')) return 'Unable to reach server. Check your connection.' if (err.message.includes('401') || err.message.includes('unauthorized')) return 'Session expired. Please log in again.' return err.message } return 'Something went wrong. Please try again.' } ``` ## Verification For each form: - [ ] Real-time validation shows feedback as user types - [ ] Submit button disabled during operation - [ ] Submit button disabled when validation fails - [ ] Inputs trimmed before submission - [ ] Error messages are user-friendly (no raw error strings) - [ ] Success feedback shown after completion ## Deploy After Fixes ```bash ./scripts/deploy-to-target.sh --live ``` Test each form with: valid input, invalid input, empty input, whitespace-only input, rapid double-click on submit.