Files
2026-05-27 23:29:53 +01:00

5.4 KiB

Tasks: enterprise-react-contacts

Change ID: enterprise-react-contacts Package: @suaveplan/react-contacts Target path: react/platform/react-contacts Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/contacts, @suaveplan/react-translate, @suaveplan/react-virtual, @suaveplan/react-error-boundary


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory tree. Write package.json at 0.1.0, tsconfig.json, vitest.config.ts, scripts.
  • Dependencies: @suaveplan/error workspace:^, @suaveplan/contacts workspace:^, @suaveplan/react-translate workspace:^, @suaveplan/react-virtual workspace:^, @suaveplan/react-error-boundary workspace:^, @opentelemetry/api catalog:observability, zod catalog:forms, tslib catalog:build.

2. Error codes and types

  • Define ContactsUiError; codes: CONTACTS_LOAD_FAILED, CONTACTS_SAVE_FAILED, AUDIENCE_RESOLVE_FAILED.
  • Define all prop types: ContactDetailProps, ContactListProps, ContactTagInputProps, GroupMembershipPanelProps, GroupTreeEditorProps, UseContactsConfig, UseContactsResult, UseGroupsConfig, UseGroupsResult, UseGroupTreeResult, UseAudienceResolverConfig, UseAudienceResolverResult.

3. Hooks

useContacts

  • CRUD + server search via ContactManager. OTel spans: contacts.create, contacts.update, contacts.delete, contacts.search. Zod-validates create/update payloads via @suaveplan/contacts schemas.
  • Write use-contacts.test.ts (≤500 lines): CRUD, search, error states, Zod rejection. { timeout: 10000 }.
  • Write use-contacts.md.

useGroups

  • Group CRUD via ContactManager. OTel groups.rename counter.
  • Write use-groups.test.ts (≤500 lines). { timeout: 10000 }.
  • Write use-groups.md.

useGroupTree

  • Converts flat group list to tree; maintains visible-node flattening for virtual rendering.
  • Write test + md.

useAudienceResolver

  • Resolves group → contact list with preview count. OTel audience.resolve span. Throws ContactsUiError(AUDIENCE_RESOLVE_FAILED) on engine error.
  • Write use-audience-resolver.test.ts (≤500 lines): resolves contacts, count preview, error state. { timeout: 10000 }.
  • Write use-audience-resolver.md.

4. Components

ContactsErrorBoundary

  • Wrapper via @suaveplan/react-error-boundary; localised error message + retry button.
  • Write test + md.

ContactTagInput

  • Token-style tag input with autocomplete suggestions from useContacts. All labels via useTranslation.
  • Write test, stories (default, with-suggestions, max-tags, playground), md.

GroupMembershipPanel

  • Add/remove contacts in a group with membership count. All labels via useTranslation.
  • Write group-membership-panel.test.tsx (≤500 lines): renders members, add fires useGroups, remove fires useGroups. { timeout: 5000 }.
  • Write stories (default, empty, readonly, playground), md.

GroupTreeEditor

  • Hierarchical tree with role="tree" / role="treeitem". Keyboard navigation: Up/Down/Left/Right/Home/End. indentPx prop + --contacts-tree-indent CSS variable. All labels via useTranslation.
  • Write test, stories (default, deep-tree, collapsed, keyboard, playground), md.

ContactDetail

  • Full contact view with tag-apply flow, group membership list, channel info. All labels via useTranslation.
  • Write contact-detail.test.tsx (≤500 lines): renders contact fields, tag apply calls useContacts, group list renders. { timeout: 5000 }.
  • Write stories (default, with-tags, with-groups, playground), md.

ContactList

  • Virtualised via @suaveplan/react-virtual for >500 rows. Searchable (fuzzy on displayName/firstName/lastName), paginated. Select/edit/delete handlers. All labels via useTranslation.
  • Write contact-list.test.tsx (≤500 lines): renders contacts, search filters list, virtualisation renders visible rows, pagination. { timeout: 10000 }.
  • Write stories (default, filtered, large-dataset, empty, playground), md.

5. E2E

  • e2e/contacts.spec.ts — Playwright:
    • Filter contact list by name; verify results narrow.
    • Expand and rename a group in GroupTreeEditor using keyboard.
    • Add a contact to a group via GroupMembershipPanel; verify count updates.
    • Resolve audience in AudienceSelector; verify preview count.
    • Navigate the full list and detail using keyboard only.
    • Verify mobile viewport single-column layout.

6. Verify and publish

  • bunx turbo lint typecheck test --filter=@suaveplan/react-contacts — green.
  • bunx turbo test:coverage --filter=@suaveplan/react-contacts — 100%.
  • bun scripts/publish.ts --package @suaveplan/react-contacts.
  • Archive change folder.