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
This commit is contained in:
1 parent
7acc0c5451
commit
e45b68bb8b
1089 files changed
+260614
No files matched your search
+172
@@ -0,0 +1,172 @@
|
||||
---
|
||||
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.
|
||||
```
|
||||
Reference in new issue
Block a user