Skip to content

Record list fields#2927

Open
brady-lamansky-gtt wants to merge 13 commits into
DiscipleTools:developfrom
brady-lamansky-gtt:record-list-fields
Open

Record list fields#2927
brady-lamansky-gtt wants to merge 13 commits into
DiscipleTools:developfrom
brady-lamansky-gtt:record-list-fields

Conversation

@brady-lamansky-gtt

@brady-lamansky-gtt brady-lamansky-gtt commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This resolves #2845 to replace record-list fields with dt-web-components. Filtering functionality was updated to accommodate the new changes & appeared to work properly, but more testing is likely needed.

I updated these things:

  • Custom Filter Creation
  • Bulk Edit
  • Field Selection (for the displayed fields)

@cairocoder01 let me know any suggestions!

Related Components PR: DiscipleTools/disciple-tools-web-components#208

@cairocoder01

cairocoder01 commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

After a brief review manually and bigger Claude review, here's some preliminary things to fix:

The issue may have been misleading about replacing all components in the filters, since some of the filter controls don't match the component normally used for that field.

Filters

Regressions

  • Date fields no longer support range selection
  • key_select fields no longer support selecting multiple values to search for
  • boolean fields no longer support selecting multiple values to search for
  • Filter chips display raw keys instead of labels (e.g. Contact Status: active instead of expected Contact Status: Active

Bugs

  • Connection fields show the button to add a new connection. No need to add from the filter selection

@cairocoder01

Copy link
Copy Markdown
Collaborator

Here is a full Claude review I did locally while it had access to a dev site running the code:

Code Review: Record List Fields → Web Components (record-list-fields)

Reviewed the diff against origin/develop (source files: archive-template.php,
dt-assets/js/modular-list.js, dt-assets/js/modular-list-bulk.js,
dt-core/utilities/dt-components.php, dt-assets/scss/_components.scss) and
verified the runtime behavior live on dtagent.local (deployed via wp-deploy)
with network capture and the PHP debug log.


🔴 High

1. Date filters are silently broken (return unfiltered results)

archive-template.php:994 renders a single dt-date, and in
modular-list.js:1453-1469 date/datetime fields fall through into the generic
"single select" branch, producing val = [customComponent.value].

The list query builder in dt-posts/posts.php:874-884 only applies a date
constraint when the value is an object keyed by start/end. An array key (0)
matches neither, so no WHERE clause is emitted — the filter is silently ignored
and all records are returned.

Verified live — applying a Baptism Date filter posts:

{"fields":[{"baptism_date":["2024-01-15"]}, ...]}
// expected: {"baptism_date":{"start":"2024-01-15"}}

This is also a capability regression: the old UI had Range Start / Range End;
the new one renders one date input, so date-range filtering is gone entirely.

Fix: add a dedicated date branch that emits { [field]: { start, end } } and a
start/end UI (two dt-dates).


🟠 Medium

2. render_toggle throws PHP warnings on every list load

dt-components.php:325 does checked( $post[$field_key], '1', false ), but the
new call sites pass an empty $post ([]) — archive-template.php:466 (archived
toggle) and archive-template.php:1005 (every boolean filter field). Confirmed in
debug.log:

PHP Warning: Undefined array key "archivedToggle" in .../dt-components.php on line 325
PHP Warning: Undefined array key "favorite" ...
PHP Warning: Undefined array key "requires_update" ...

Fix: checked( $post[$field_key] ?? '', '1', false ) in render_toggle.

3. key_select and boolean filters regressed to single-value

archive-template.php:964 renders key_select as dt-single-select and
boolean as dt-toggle. Previously these were multi-checkbox groups.
Consequences:

  • key_select (e.g. Contact Status): can no longer filter for multiple statuses
    at once, and the explicit "None Set" option is gone. The backend still
    supports value arrays (dt-posts/posts.php:886) — only the UI regressed.
  • boolean: the old Yes/No checkboxes are replaced by one toggle, so filtering
    for "No"/unset (0) is no longer expressible.

4. Field-selection save persists stale - delete markers

modular-list.js:890-898 reads $('#field_search_input').val() directly. When a
user removes a column, dt-multi-select marks it as -fieldkey rather than
removing it. Verified live — after removing "Name", the saved value is:

["-name","last_modified","favorite","overall_status","assigned_to","milestones","groups","seeker_path"]

That -name gets written to the fields_to_show_in_table cookie and sent as
fields_to_return. The filter-change handler already strips these
(modular-list.js:1449-1452); save_column_choices should do the same
(.filter(k => k.charAt(0) !== '-')). The now-dead try/catch around it (no JSON
parsing anymore) should also be removed.

5. Filter chips display raw keys instead of labels

For key_select/multi_select/tags, create_name_value_label
(modular-list.js:1735) interpolates the value directly. Verified live the chips
read "Contact Status: active" and "Sources: web,phone" instead of the
translated option labels ("Active", "Web, Phone"). Connections/users/locations are
fine (they use item.label). The change handler should resolve option keys to
labels before building the chip text.


🟡 Low / cleanup

(the issue explicitly asks to remove now-unused code)

  • Dead field-picker code left behind after the template rewrite: the
    .remove-field-btn handler and #selected_fields_input /
    #enabled_fields_container references (modular-list.js:866-888) and in
    reset_column_choices (modular-list.js:908) — none of those elements exist
    anymore.
  • Dead .text-comms-filter-input keyup handler (modular-list.js:1774) — that
    class is no longer rendered (fields are now dt-text), so the old "auto-select
    the default radio while typing" behavior is silently gone. Text filtering still
    works, but confirm this UX loss is intended.
  • Dead .clear-date-picker handler (modular-list.js:2323) — those buttons
    were removed from the date template.
  • Duplicate/mismatched selector in the modal-close reset
    (modular-list.js:2115): dt-location is listed twice, and dt-location-map is
    in the reset branch's tag list but not in the query selector.
  • dt-assets/scss/_components.scss has no trailing newline (line 17) —
    PHPCS/prettier nitpick.

What I verified vs. inferred

Live-confirmed on dtagent.local: date query shape (#1), PHP warnings (#2), stale
- markers (#4), raw-key chips (#5), single-select rendering (#3). The
double-labeling I initially suspected for text/comms fields turned out not to
be a bug — the radio handler correctly replaces the change-handler chip (net one
chip). Connection/user/location/multi-select/tags query building looks correct.

@cairocoder01

cairocoder01 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Regressions

  • key_select fields no longer support selecting "none set" in order to filter by records with no value (screenshot from current theme version before these changes)
old new
image image
  • Connection and users-connections filter previously had a placeholder "Type to search" that we can add back in. Also, we are now showing the field label above that is duplicated and not needed
old new
image image
  • Connections filter previously disabled the input field when the all or none filter was selected. We should re-implement that. That happened on 1691 with the line typeahead.prop('disabled', true); which no longer does anything since there is no typeahead.
  • Location or Address shows not unsupported error. Previously showed the standard location grid filter.
old new
image image
  • multi_select previously displayed a list selection instead of a button group. We should match the display type of the field, whether it should show as a standard list select or a button group.
old new
image image
  • Tags filter is showing the option to add a new tag:
image
  • Communication channel and text are missing placeholders
  • If I create a date range filter and save it as a custom filter, when I click the edit icon next to the saved custom filter, the filter modal doesn't have the values of the saved filter. The same seems to be true of other saved filters. Claude eventually found it and gave this detail:

modular-list.js:2152 edit_saved_filter still runs on the old typeahead machinery:

  • Line 2081 declares let typeaheads_loaded = null; and the code that used to assign it (typeaheads_loaded = load_multi_select_typeaheads()…) was deleted in this PR. It's never reassigned, so line 2154 typeaheads_loaded.then(…) throws TypeError: Cannot read properties of null (reading 'then') the instant you click the edit (pencil) icon on a saved custom filter. The modal opens blank and nothing populates.
  • Even if you guard that, the callback body still calls removed APIs: .datepicker('setDate', …) at 2184 and window.Typeahead['.js-typeahead-…'].addMultiselectItemLayout(…) at 2198–2213 — the typeaheads are never created now, so those are undefined.addMultiselectItemLayout() → another throw.

So edit_saved_filter needs to be reworked to populate the dt-* components (set .value), not just cleaned up. This is in-scope for "Custom Filter Creation" and is a genuine regression — worth adding to the review as High.

@cairocoder01 cairocoder01 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good. A few things to clean up yet.

Comment thread archive-template.php
Comment thread archive-template.php
Comment thread archive-template.php
Comment thread dt-assets/js/modular-list.js
} else if (type === 'text' || type === 'communication_channel') {
val = customComponent.value;

switch ($('.filter-by-text-comms-option:checked').val()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like an existing bug, but let's quick address it. This jquery selector will select the first checked option across ALL fields. So if you're setting filters on both email and phone, it will use the selection for the first one it finds. We need to scope this selector to the field container. Each tab-panel has an id of the field_id. We can follow the pattern of the connections field logic just before this that uses: $(`#${field} .all-connections`) to do something more like $(`#${field} .filter-by-text-comms-option:checked`). This makes sure we're selecting the options within the field we're actually processing at this point.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Those options are rendered outside of the field itself, so I added them to a div of id="filter_by_text_comms_option_" which I believe should resolve it! Thanks for noticing this, too. The switch now checks for an option under the element with the id from the new div. Let me know if you have a better suggestion.

Comment thread dt-assets/js/modular-list.js
Comment thread dt-assets/js/modular-list.js
Comment on lines +1458 to +1462
val = val.map((v) =>
typeof v === 'string'
? v.replace(/</g, '<').replace(/>/g, '>')
: v,
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Claude, this is only needed if the tags field has allowAdd. Since we're removing that, I think we can remove this, right? Is there another reason it's here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially was mistakenly rendering the single-select labels to show the value itself: "Under 18 years old" would display: "<18" instead, so I had to handle the special characters in order to render them properly. When I changed the labels to show the name correctly, I didn't realize that was no longer needed. Thanks!

typeof value === 'string' &&
(value.includes('<') || value.includes('>'))
) {
value = value.replace(/</g, '<').replace(/>/g, '>');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Claude, this is only needed if the tags field has allowAdd. Since we're removing that, I think we can remove this, right? Is there another reason it's here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the above comment. I initially was handled special characters "<", but that was when I was rendering the value in the label rather than the name.

const tagName = this.tagName.toLowerCase();

if (tagName === 'dt-date') {
this.reset();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we use reset for all components? That is supposed to work for every component.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see! I set up the other ones before, just trying to manually set the value to be empty. I handled date separately at the end, since it had both a "Start" and "End", I couldn't just reset one single value. I then found the .reset() function and didn't check if the others had it. Thanks!

@cairocoder01

Copy link
Copy Markdown
Collaborator

Some additional dead code that can be removed after you confirm it is for the old UI and not needed anymore:

Dead code, safe to remove

  • 906–909 — #reset_column_choices sets #selected_fields_input and #enabled_fields_container; both were removed from the template (picker is now dt-multi-select). Reset still works via the cookie clear + reload below it.
  • 2081 — let typeaheads_loaded = null; (only referenced by the broken line above; drop it as part of fixing edit_saved_filter).
  • 2085–2087 — $('#filter-modal input.dt_date_picker').each(...); no .dt_date_picker inputs exist anymore (dates are dt-date, reset in the closed.zf.reveal handler).
  • 2094–2096 — .typeahead__query.disabled removeClass loop; no such elements remain.
  • 2098–2101 — $('.typeahead__query input').each(function () { let typeahead = …; }); assigns a local and does nothing with it, and matches nothing.
  • 2246–2247 — window.Typeahead['.js-typeahead-${field}'].adjustInputSize() in the #filter-tabs change handler; guarded so it won't throw, but it's dead.

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.

Replace components: record list

3 participants