---
id: CMP-CRM-ENT-004
title: Affiliation Editor Drawer
product_area: crm
module: entities
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
purpose: Lets staff prepare one affiliation’s organization, type, dates, roles and information links before applying
  it to the contact draft.
reuse: The form-engine affiliation adapter opens the drawer in Create Contact and Edit Contact. Organization-side
  Add Person uses a different dialog and is outside this drawer’s contract.
supports:
- CAP-CRM-ENT-009
- CAP-CRM-ENT-011
- CAP-CRM-ENT-006
realises:
- CON-CRM-ENT-003
- CON-CRM-ENT-005
props:
- name: open
  type: boolean
  required: true
  default: none
  meaning: Controls drawer visibility.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: onOpenChange
  type: handler receiving visibility
  required: true
  default: none
  meaning: Lets the caller close or open the drawer.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: mode
  type: add or edit
  required: true
  default: none
  meaning: Selects heading, organization editability and default-link preview.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: draft
  type: affiliation draft
  required: true
  default: none
  meaning: Contains organization identity and name, affiliation type, primary flag, job title, start/end dates,
    notes and individually identified dated role rows.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: onDraftChange
  type: handler receiving replacement draft
  required: true
  default: none
  meaning: Receives every field or role edit.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: onApply
  type: event handler
  required: true
  default: none
  meaning: Commits the prepared draft in the caller, not to the service.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: renderOrganizationPicker
  type: rendering slot
  required: true
  default: none
  meaning: Receives control identity, held organization, change callback and invalid flag.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: affiliationTypes
  type: type choices
  required: true
  default: none
  meaning: Supplies active options and any retained inactive choice already labeled.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: contactRoles
  type: role choices
  required: true
  default: none
  meaning: Supplies permitted choices plus held roles; restriction filtering belongs to the caller.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: rolesLoaded
  type: boolean
  required: false
  default: true
  meaning: Distinguishes pending roles from a genuinely empty catalog.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: rolesNote
  type: text
  required: false
  default: none
  meaning: Explains why role choices are restricted.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: roleMessages
  type: messages aligned with draft roles
  required: false
  default: []
  meaning: Supplies role and end-date warnings or blocking errors.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: onTypeTouched
  type: event handler
  required: false
  default: none
  meaning: Notifies the host when the type selector opens so it can stop following a moving default.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: onEndAndAddAnother
  type: event handler
  required: false
  default: none
  meaning: Offers the edit-mode transition to a new organization.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: supplies
  type: list of text
  required: true
  default: none
  meaning: Names the contact-information destinations supplied by this affiliation.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: defaultLinks
  type: link preview rows
  required: false
  default: []
  meaning: Provides key, address/phone kind, label, detail, new/replaces/skipped/missing state and selection.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: onToggleDefaultLink
  type: handler receiving key and selection
  required: false
  default: none
  meaning: Changes preview selection in the caller.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: linksWaitForStart
  type: boolean
  required: false
  default: false
  meaning: Changes empty-preview wording for a future affiliation.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: messages
  type: messages keyed by control
  required: true
  default: none
  meaning: Supplies warning or error tone and text for organization, type, dates and primary.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: primaryDisabledReason
  type: text
  required: false
  default: none
  meaning: Disables and explains the primary switch; it also displays an unchecked state.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: primaryNotice
  type: text
  required: false
  default: none
  meaning: Displays the consequence of changing the primary affiliation.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: stopsResolving
  type: list of destination labels
  required: false
  default: none
  meaning: Warns which linked details will stop resolving.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
events:
- name: draft change
  when: A field, role row or period changes.
  payload: A complete replacement affiliation draft.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: apply
  when: Add Affiliation or Apply is activated without supplied errors.
  payload: No payload; the caller already owns the draft.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: open change
  when: Cancel or sheet visibility changes.
  payload: The requested boolean visibility.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: type touched
  when: The type dropdown opens.
  payload: No payload.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: default link toggle
  when: An unlocked preview checkbox changes.
  payload: Preview key and selected flag.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- name: end and add another
  when: The optional edit-mode action is activated.
  payload: No payload.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
states:
- state: add
  experience: Organization selection and Link When Added preview are available.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- state: edit
  experience: Organization is text; an optional End and Add Another action appears.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- state: roles loading
  experience: Loading text appears, Add Role is disabled, and unresolved held roles display a dash.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- state: roles ready
  experience: Configured choices, held role periods and optional restriction note appear.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- state: blocked apply
  experience: Any supplied error in control or role messages disables Apply; warnings do not.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- state: default source missing or occupied
  experience: Missing and skipped preview rows are unchecked and disabled with explanatory badges.
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
validation:
- BR-CAP-CRM-ENT-009-09
- BR-CAP-CRM-ENT-011-13
- BR-CAP-CRM-ENT-006-11
accessibility: The sheet has a title and description; inputs have associated labels, Notes has an accessible name,
  and remove-role buttons identify their row numbers. Control errors use alert semantics and warnings use status
  semantics. Some control descriptions are explicitly linked. Focus trapping and complete error-announcement behavior
  have not been verified.
design_system:
- sheet
- select
- switch
- checkbox
- button
- badge
- loading line
- date input
- text area
depends_on:
- CMP-CRM-ENT-008
implementation: CMI-CRM-ENT-004
acceptance:
- id: AC-CMP-CRM-ENT-004-01
  proved_by: []
  statement: Edit mode shows the organization as text and does not render the organization selection slot.
  level: unit
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- id: AC-CMP-CRM-ENT-004-02
  proved_by: []
  statement: Add Period inserts a new identified row with the selected role and blank dates immediately after the
    original.
  level: unit
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- id: AC-CMP-CRM-ENT-004-03
  proved_by: []
  statement: Pending role loading disables Add Role and does not display No contact roles configured.
  level: unit
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- id: AC-CMP-CRM-ENT-004-04
  proved_by: []
  statement: Any supplied error blocks Apply while warning-only messages leave Apply enabled.
  level: unit
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- id: AC-CMP-CRM-ENT-004-05
  proved_by: []
  statement: Skipped and missing default-link preview rows are unchecked and disabled.
  level: unit
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
controls: []
evidence:
- react-console:components/affiliations/affiliation-editor.tsx
narrative_sources:
- section: What It Is For
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- section: Where It Is Used
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- section: How It Behaves
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- section: Its Contract
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- section: States
  provenance: observed
  source: react-console:components/affiliations/affiliation-editor.tsx
- section: Accessibility
  provenance: unknown
  source: FND-CRM-ENT-161
- section: Open Design Questions
  provenance: unknown
  source: FND-CRM-ENT-161; FND-CRM-ENT-162; FND-CRM-ENT-163
used_by: []
---

# Affiliation Editor Drawer

## What It Is For

Lets staff prepare one affiliation’s organization, type, dates, roles and information links before applying it to the contact draft. A controlled sheet edits one affiliation and dated role collection. A small exported wording helper distinguishes an unchosen type, an unchosen organization, a future start and a type with no defaults. The drawer itself neither loads records nor persists the contact.

## Where It Is Used

The form-engine affiliation adapter opens the drawer in Create Contact and Edit Contact. Organization-side Add Person uses a different dialog and is outside this drawer’s contract. Create Contact and Edit Contact invoke the drawer from their shared affiliation form adapter. The caller supplies the CRM organization picker slot. Organization 360’s Add Person dialog does not use this drawer.

## How It Behaves

Add mode offers organization selection through the supplied slot; Edit mode keeps that organization fixed. Fields update the controlled draft immediately. Each role can be removed, and a selected role offers Add Period, inserting another independently identified row for that role immediately after it. Pending catalog reads disable Add Role rather than declaring that no roles exist. The host supplies validation messages and primary eligibility; the drawer disables Apply only for errors. Primary, linked-information consequences and default-link choices remain visible explanations rather than hidden save effects.

## Its Contract

The caller owns open state, the working draft, catalog choices and all validation. Apply requests a change to the contact draft; saving the contact remains a separate screen action. The organization slot receives a nullable identifier and reports both identifier and name. Preview selections are independent callbacks rather than fields inside the affiliation draft. Warning tone does not block Apply, while an error on either an ordinary field or a role row does. Cancel asks the caller to close, without specifying how it discards its working draft.

## States

Add: Organization selection and Link When Added preview are available. Edit: Organization is text; an optional End and Add Another action appears. Roles loading: Loading text appears, Add Role is disabled, and unresolved held roles display a dash. Roles ready: Configured choices, held role periods and optional restriction note appear. Blocked apply: Any supplied error in control or role messages disables Apply; warnings do not. Default source missing or occupied: Missing and skipped preview rows are unchecked and disabled with explanatory badges.

## Accessibility

The sheet has a title and description; inputs have associated labels, Notes has an accessible name, and remove-role buttons identify their row numbers. Control errors use alert semantics and warnings use status semantics. Some control descriptions are explicitly linked. Focus trapping and complete error-announcement behavior have not been verified. Accessibility verification is an open obligation recorded in the component accessibility finding.

## Open Design Questions

The recorded interaction is observed implementation, with proposed design intent. Component criteria need complete executable bindings and browser accessibility review. Screen owners must wire the registered components into their designs; this slice records usage without changing those screen contracts.
