- Update all skill SSH commands from sshpass to key-based auth (~/.ssh/archipelago-deploy) - Add proxy_connect_timeout 120s to nginx Ollama location blocks - Add new polish/sweep skills for overnight automation - Add demo content (documents, photos) for demo stack - Add .ssh/ to .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.3 KiB
3.3 KiB
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
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
<input v-model="password" type="password" class="glass-input" />
<ul v-if="passwordErrors.length" class="text-red-400 text-xs mt-1 space-y-0.5">
<li v-for="err in passwordErrors" :key="err">{{ err }}</li>
</ul>
<button
class="glass-button"
:disabled="isSubmitting || passwordErrors.length > 0"
@click="submit"
>
<span v-if="isSubmitting">Saving...</span>
<span v-else>Save</span>
</button>
Input Trimming
All text inputs should be trimmed before submission:
const trimmed = password.value.trim()
Error Message Consistency
Create or use a formatError utility:
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
./scripts/deploy-to-target.sh --live
Test each form with: valid input, invalid input, empty input, whitespace-only input, rapid double-click on submit.