# 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 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:
```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.
