Files
Z User e45b68bb8b feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library:
- Connects to IMAP server (IMAPFlow, TLS on port 993)
- Fetches recent messages (configurable limit, default 20)
- Extracts: from, fromEmail, subject, date, snippet (200 chars)
- Thread-to-contact matching (PRD §6.1):
  1. Match sender email against persons table
  2. If no person match, extract domain and match against company.website
  3. Returns matchedCompany + matchedPerson for each email
- Audit-logged (action: crm.mailsync:imap)
- Zod validated (host, port, user, password, folder, limit)
- 502 on IMAP connection failure with detail
- 400 on validation failure

Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
2026-10-27 22:50:50 +00:00

173 lines
4.8 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: state-coverage
description: Required UI states for interactive, data-driven, form, tool, dashboard, and prototype surfaces. Prevents designs that only render the ideal populated state.
mode: reference
platform: web
scenario: horizontal-craft
preview:
type: none
default_for:
- state coverage
- empty state
- loading state
- error state
- forms
- dashboard
- interactive prototype
fidelity: system
sources:
- adapted-from-open-design/craft/state-coverage.md
---
# State Coverage
The single most reliable AI-design failure is shipping only the populated state.
The active design system decides how states look. This file decides which states must exist and what they must contain.
---
## The five required states
Every surface that fetches, transforms, filters, searches, or accepts data must handle all five.
| State | Triggered when | Must contain |
|---|---|---|
| Loading | Data is in flight | skeleton, spinner, shell, or labelled progress |
| Empty | no records yet or query returned nothing | headline, explanation, primary next action |
| Error | fetch failed, server failure, validation rejection | plain cause, recovery action, preserved input |
| Populated | data present | the main designed state |
| Edge | extreme volume, long strings, missing fields, partial network | layout does not break |
Render-and-screenshot test: every list, table, card, form, and panel has these states.
---
## Edge matrix
| Surface | Edge scenario |
|---|---|
| Dashboard / table | 10,000+ rows, numeric columns, sort + filter active |
| Mobile card / list | 200-char title, missing avatar, missing secondary CTA |
| Form | optional fields empty, required fields at max length |
| Search results | one-character query, special-character query, 1,000+ results |
| Detail view | missing optional metadata, mixed Chinese / Latin / long words |
| Tool H5 | invalid input, user retries, result cannot be computed |
---
## Form-specific states
Forms add:
| State | Triggered when | Behavior |
|---|---|---|
| Untouched | field has not had focus | default styling; no validation message |
| Dirty valid | user typed and field passes validation | helper remains; avoid excessive success coloring |
| Submitted pending | submit clicked, waiting | button loading state; prevent duplicate submit |
Validation timing:
- validate on blur by default;
- for password / live fields, validate on keystroke only after first blur;
- remove error when input becomes valid;
- preserve input on server failure.
---
## Empty state composition
Empty is not absence. It has a job.
- **First-use empty:** illustration or simple mark + headline + value sentence + primary CTA.
- **No-results empty:** echo the query, suggest alternatives.
- **Cleared empty:** confirm completion, optional next action.
- **Error-as-empty:** forbidden. Error is its own state.
---
## Error state composition
Every error answers:
1. What happened.
2. Why, if knowable.
3. What the user can do.
Bad:
```text
Something went wrong.
```
Better:
```text
未能保存表单。网络连接中断,请检查后重试。
```
Severity tiers:
- field-level
- form-level
- section-level
- page-level
- app-level
Match severity to scope. A field error does not need a page-level error.
---
## Loading thresholds
| Duration | Indicator |
|---|---|
| 0–300ms | none |
| 300ms–2s | subtle spinner or skeleton |
| 2–10s | skeleton or labelled spinner |
| 10–30s | progress with cancel option when possible |
| 30s+ | explicit longer-than-expected message |
| 60s+ | stop indefinite animation; show error / retry / cancel |
Never leave a spinner running indefinitely.
---
## ARIA and focus rules
| Change | ARIA | Focus action |
|---|---|---|
| Inline error on submit | `role="alert"` | move focus to first error field |
| Toast confirmation | `role="status"` | do not move focus |
| Critical modal | `role="alertdialog"` | move focus to dialog |
| Loading begins | `role="status"` | do not focus spinner |
| User-triggered content appears | usually none | move focus only if it helps continuation |
---
## Common mistakes
- only populated state exists
- empty state says only “No data”
- error says only “Something went wrong”
- spinner has no timeout
- submit clears fields on failure
- validation fires on first keystroke
- full-page loading replaces chrome for a section-level fetch
- color alone conveys error state
- toast cannot be paused on hover/focus
---
## Quality checks
```text
[ ] Loading, empty, error, populated, and edge states exist where relevant.
[ ] Empty state has explanation and next action.
[ ] Error state has cause and recovery.
[ ] Forms preserve input after failure.
[ ] Long strings and missing optional fields do not break layout.
[ ] Loading does not spin forever.
[ ] State changes are announced where appropriate.
```