Shared helpers for the commercial party-role UI (badges on the list pages, the Roles checkbox section on the company/contact forms).
Summary
Functions
Active role values currently held by a company/contact — the initial checkbox state for the edit forms.
daisyUI badge modifier for a role value.
Human label for a role value.
Reads the checked roles[] checkboxes from a raw form event payload,
keeping only known roles (a forged payload can't invent one).
Reconciles checkbox state with stored roles: grants what's newly checked, revokes active roles that were unchecked. Both operations are idempotent, so re-saving an unchanged form is a no-op.
Functions
Active role values currently held by a company/contact — the initial checkbox state for the edit forms.
daisyUI badge modifier for a role value.
Human label for a role value.
Reads the checked roles[] checkboxes from a raw form event payload,
keeping only known roles (a forged payload can't invent one).
@spec sync_roles(struct(), [String.t()], UUIDv7.t() | String.t() | nil) :: :ok | {:partial, [String.t()]}
Reconciles checkbox state with stored roles: grants what's newly checked, revokes active roles that were unchecked. Both operations are idempotent, so re-saving an unchanged form is a no-op.
Returns :ok when every role reconciled, or {:partial, failed_roles} if
one or more grant/revoke calls failed (each is also logged). Callers should
surface a warning instead of reporting unqualified success — a checked role
that silently didn't apply is a real user-visible correctness gap.
Pass the acting user's uuid as actor_uuid so grant/revoke activity log
entries record who made the change (mirrors every other logged CRM
mutation instead of landing with actor_uuid: nil).