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
4.8 KiB
Executable File
4.8 KiB
Executable File
name, description, mode, platform, scenario, preview, default_for, fidelity, sources
| name | description | mode | platform | scenario | preview | default_for | fidelity | sources | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| state-coverage | Required UI states for interactive, data-driven, form, tool, dashboard, and prototype surfaces. Prevents designs that only render the ideal populated state. | reference | web | horizontal-craft |
|
|
system |
|
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:
- What happened.
- Why, if knowable.
- What the user can do.
Bad:
Something went wrong.
Better:
未能保存表单。网络连接中断,请检查后重试。
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
[ ] 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.