Skip to content

CRM: custom fields in column picker + role overview cards - #4

Merged
ddon merged 3 commits into
BeamLabEU:mainfrom
timujinne:followup/crm-user-view-and-format
May 5, 2026
Merged

CRM: custom fields in column picker + role overview cards#4
ddon merged 3 commits into
BeamLabEU:mainfrom
timujinne:followup/crm-user-view-and-format

Conversation

@timujinne

Copy link
Copy Markdown
Contributor

Summary

  • Custom fields in column picker. ColumnConfig.available_columns/1 now pulls enabled definitions from PhoenixKit.Users.CustomFields.list_enabled_field_definitions/0 and emits custom_<key> entries under :custom for both role and organizations scopes. Previously :custom was always %{}, so admin-defined custom fields never appeared in the "Customize columns" modal.
  • Custom-field rendering. RoleView and OrganizationsView got a render_cell("custom_" <> _, user) clause that reads user.custom_fields[key] and formats by type (boolean/checkbox/date/datetime/string/list).
  • Overview cards. The CRM landing page now lists each CRM-enabled role as a clickable card showing the role name and the number of users assigned to it (via Roles.count_users_with_role/1). Cards navigate to the role's CRM users page; an empty-state card is shown when no roles are connected.

All changes are confined to lib/phoenix_kit_crm/... — no edits to PhoenixKit Core (deps/phoenix_kit/...); we only call public APIs.

Test plan

  • Define a custom field in PhoenixKit user settings → it appears in the "Custom" section of the column picker on both a role page and the Organizations page.
  • Add the custom column → values from user.custom_fields render in both table and card views, with sensible formatting for each field type.
  • Visit the CRM overview while several roles are CRM-enabled → cards show correct names and user counts; clicking a card navigates to the role page.
  • Disable all roles → overview shows the empty-state card.
  • Existing tests still pass: mix test (column_config tests still expect empty :custom when no DB / no enabled fields, which holds).

timujinne and others added 3 commits May 5, 2026 06:39
ColumnConfig.available_columns/1 now reads
PhoenixKit.Users.CustomFields.list_enabled_field_definitions/0 and
emits "custom_<key>" entries under :custom for both the role and
organizations scopes. RoleView and OrganizationsView render those
columns by reading user.custom_fields[key] with type-aware formatting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The overview page now lists each role connected to the CRM module as a
clickable card showing the role name and the number of users assigned
to it. Each card links to the role's CRM users page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…atter

- Narrow custom-field rescue to UndefinedFunctionError; log other errors
  via Logger.warning instead of silently swallowing them.
- Switch ColumnConfig.available_columns/1 :standard and :custom to
  ordered lists of {id, meta} tuples so custom columns surface in the
  position order returned by PhoenixKit.Users.CustomFields (the previous
  Map.new collapsed it to alphabetical-by-key). Update column_modal,
  all_column_ids/1, and get_column_metadata/2 to consume the list shape.
- Extract render_custom_cell/3 and format_custom_value/2 into a shared
  PhoenixKitCRM.Web.CellFormat module; remove the duplicated formatter
  ladder from RoleView and OrganizationsView.
- Pass the actual scope assign through render_cell/3 instead of relying
  on the {:role, nil} shortcut that worked only because available_columns
  ignored the role uuid.
- Add CellFormatTest covering all format_custom_value/2 branches and
  update column_config_test for the new list-based shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ddon
ddon merged commit f74eea1 into BeamLabEU:main May 5, 2026
1 check passed
ddon added a commit that referenced this pull request May 5, 2026
Retrospective Claude review for the merged "custom fields in column
picker + role overview cards" PR. Flags the Iron-Law violation in
CRMLive.mount/3 (DB queries fire twice per connect), the N+1 across
count_users_with_role/1, and the per-cell available_columns/1
recomputation in the custom-cell render path. Also notes positives:
ordering-preserving list-tuple shape, narrowed rescue+log fallback,
shared CellFormat module, scope threading.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddon added a commit that referenced this pull request May 5, 2026
Fixes the issues flagged in the PR #4 review.

- CRMLive: move role-stat loading out of mount/3 into handle_params/3
  gated on connected?/1. mount/3 fires twice per connect (HTTP +
  WebSocket), so the previous code ran 1 + N queries × 2 per page load.
- RoleSettings: add list_enabled_with_user_counts/0 — single GROUP BY
  with a left join over role_assignments — replacing the N+1 loop of
  Roles.count_users_with_role/1 calls. Roles with zero users still
  surface (count = 0) thanks to the left join.
- ColumnConfig: add column_metadata_map/1 returning a flat
  %{column_id => meta} map. RoleView and OrganizationsView now compute
  it once in mount + handle_params and assign as :column_meta. Threaded
  through render_cell/3, card_field/3, column_label/2, and
  CellFormat.render_custom_cell/3 — eliminating per-cell
  available_columns/1 rebuilds (previously O(rows × custom_cols) calls
  per render). ColumnModal does the same lookup once for the
  draggable-list item template.
- ColumnConfig.custom_field_columns/0: filter for binary "key" upstream
  of the Enum.map so a malformed definition can't crash the page with
  ArgumentError on `"custom_" <> nil`.
- CRMLive: switch long-form Gettext.gettext/dngettext calls to the
  short gettext/ngettext form already in scope via
  `use PhoenixKitWeb, :live_view`, matching the rest of the views.
- Tests: cover render_custom_cell/3 with the new (column_meta, id, user)
  signature including unknown-column / nil-custom_fields branches; add
  column_metadata_map/1 assertions to ColumnConfigTest.
- mix.lock: bump bandit, ecto, jason, leaf, phoenix, phoenix_kit,
  phoenix_live_view, postgrex via `mix deps.update --all`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddon added a commit that referenced this pull request May 5, 2026
Adds a "Handoff: fixes landed on main" section to the existing review
record pointing at the cherry-picked fix commit (8e089db), what's in
it per issue, what tests are covered, what's left for Tim to verify
manually (mix test / mix dialyzer / UI walkthrough), and how to revert
if needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddon added a commit that referenced this pull request May 5, 2026
Patch release shipping the PR #4 review fixes already on main
(8e089db): CRMLive lifecycle, N+1 collapse via list_enabled_with_user_counts/0,
column-metadata-map memoization in views, custom-field key guard, and
gettext call-style alignment in CRMLive.

- Bump @Version to 0.2.1 in mix.exs.
- Add 0.2.1 CHANGELOG entry with Fixed / Added / Changed sections.
- Append precommit results table (compile / format / credo / dialyzer
  all clean at 8e089db) and the 0.2.1 version-bump rationale to the
  PR #4 review record.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@timujinne
timujinne deleted the followup/crm-user-view-and-format branch August 6, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants