# PRD: List-View Filter Inputs for System Timestamp Fields

Related issue: https://github.com/pingcap-inc/NexusCRM/issues/1669

Parent epic: https://github.com/pingcap-inc/NexusCRM/issues/1668

Draft status: approved after two grill reviewers; low-priority polish folded in.

## Problem Statement

Users can choose system timestamp fields such as Created Date and Last Modified
Date in the list-view filter panel, but the value control becomes a blank,
non-interactive display box. The user cannot enter a datetime value, so the
filter cannot be applied.

This is a context problem. The same field-input registry path is used by
record-edit UI and by list-view filter criteria. A record edit form must not let
users edit audit timestamp fields, but a filter builder must let users provide
comparison values for those same fields. Today the registry input props do not
carry enough context to tell those cases apart.

Salesforce treats this distinction as real product behavior: system/audit fields
are read-only on records, but date and datetime fields remain valid filter
operands in list views and SOQL-style filters.

## Solution

Introduce an explicit render context for field input components. The default
context remains record editing, preserving existing read-only behavior. The
list-view filter builder passes filter context when rendering criterion value
inputs. In filter context, the default input renderer must render the normal
field-type-specific value input instead of applying record-edit read-only
guards.

For the immediate issue, selecting Created Date or Last Modified Date in the
visual list-view filter builder must render a functional datetime-local input.
Typing or picking a value must update the local filter expression in the same way
other field types do today. Record-edit, inline-edit, and admin layout preview
paths must continue to default to edit-context behavior unless they explicitly
opt into another context.

The fix must also make the resulting date/datetime filter expression usable.
Native date and datetime-local controls emit strings, but those strings are not
text-field values. Date fields must serialize as unquoted YYYY-MM-DD literals.
Datetime fields must serialize as unquoted Salesforce-compatible DateTime
literals with seconds and timezone information, such as UTC `Z` literals or
equivalent explicit-offset literals. This keeps the visual builder aligned with
Salesforce SOQL date/datetime filtering rather than producing quoted string
comparisons.

The apply path must accept those literals end to end. The frontend filter
builder, frontend SOQL composition, backend SOQL parsing, and internal filter
lowering all need to agree on the concrete date/datetime literal form. Restoring
the input control is not sufficient if applying the filter then produces a
malformed-query response.

The fix should remain metadata-driven and app-neutral. It must not introduce
SObject-specific branches or tenant-specific field rules. The platform should
continue to use field metadata to choose field input controls and should use
context only to distinguish the user interaction being rendered.

## User Stories

1. As a list-view user, I want to filter records by Created Date, so that I can
   find records created at or around a specific time.
2. As a list-view user, I want to filter records by Last Modified Date, so that I
   can review recently changed records.
3. As a list-view user, I want audit timestamp filters to show an interactive
   datetime control, so that I know the filter value can be edited.
4. As a list-view user, I want the timestamp value input to behave like other
   filter value inputs, so that applying filters feels consistent.
5. As a list-view user, I want a filter row to clear its value when I switch to a
   different field, so that stale values do not accidentally apply to the wrong
   field.
6. As a list-view user, I want the visual builder and code builder to keep their
   current relationship, so that simple criteria stay editable without blocking
   advanced criteria.
7. As a list-view user, I want Created Date and Last Modified Date to remain
   available in the field picker, so that system timestamps are first-class
   filter fields.
8. As a list-view user, I want empty filter rows to stay ignored, so that
   partially configured criteria do not break record loading.
9. As a list-view user, I want applying a timestamp filter to refresh the visible
   records, so that the list reflects my chosen criteria.
10. As a record editor, I want Created Date and Last Modified Date to remain
    non-editable, so that audit fields cannot be modified through normal record
    forms.
11. As an inline-edit user, I want system timestamp fields to remain protected,
    so that table/detail editing does not expose audit fields for mutation.
12. As an admin arranging layouts, I want field previews to stay non-mutating, so
    that layout editing cannot accidentally create a live audit-field editor.
13. As a developer adding a future field input guard, I want a clear context
    discriminator, so that record-edit guard behavior does not leak into filter
    criteria by accident.
14. As a developer maintaining list-view filters, I want the filter builder to
    declare that it is rendering criterion values, so that field input behavior
    is intentional at the call site.
15. As a developer maintaining record forms, I want omitted context to preserve
    the old edit default, so that existing call sites do not all need mechanical
    updates.
16. As an admin configuring criteria-based sharing, I want criteria value inputs
    to behave like filter values rather than record-edit fields, so that
    read-only fields can still be used as comparison operands when allowed by
    metadata.
17. As a QA engineer, I want a direct regression test for filter-context
    timestamp inputs, so that the blank-box bug cannot return unnoticed.
18. As a QA engineer, I want a direct regression test for edit-context timestamp
    display, so that the fix cannot accidentally make audit fields editable.
19. As a product owner, I want the fix scoped to the missing input behavior, so
    that a P1 usability bug can ship without waiting for a full filter-builder
    redesign.

## Implementation Decisions

- Add a named field-input render context contract:
  `FieldInputRenderContext = 'edit' | 'filter'`.
- Add `context?: FieldInputRenderContext` to the shared field-input props. The
  prop is optional, and an omitted value means edit context.
- Pass the context prop through to custom registered field inputs. Custom inputs
  may ignore it when their rendering has no context-sensitive behavior, but they
  must accept the shared prop shape.
- Apply record-edit read-only guards only when the input is not in filter
  context. The behavior should be phrased as a context rule, not a special-case
  exemption for two timestamp fields.
- Pass filter context from the visual filter-condition value renderer when it
  obtains a field input from the UI registry.
- Treat the shared visual criteria editor as a filter/criteria context, not a
  record-edit context. The editor is used by list-view filters and
  criteria-based sharing rules; both use field values as comparison operands and
  should not inherit record-edit read-only display guards.
- Preserve existing field-type mapping for the immediate fix: datetime fields
  render a native datetime-local input, date fields render a native date input,
  number-like fields render number inputs, and unknown scalar fields render text
  inputs.
- Keep record-form editability controlled by field metadata and the existing
  write-capability gates. Do not use filter context in record create/edit forms.
- Keep inline edit protected by its existing system-field/editability gate. Do
  not add a second inline-edit branch for audit timestamps.
- Keep admin layout preview in edit/default context unless there is a separate
  product decision to preview filter-specific controls.
- Do not introduce object-name branches, CRM-specific policy, profile literals,
  or tenant-specific field names. CreatedDate and LastModifiedDate are allowed
  here as generic Salesforce audit fields.
- Do not migrate the list-view filter builder to the separate criteria-value
  input component in this P1 fix. That component is useful prior art for
  filter-specific value controls, but changing the shared builder to a new input
  family is broader than the bug requires.
- Normalize filter-expression serialization by field type. Date and datetime
  values from native controls must not be quoted merely because they arrive in
  JavaScript as strings. Date values should emit unquoted date literals.
  Datetime values should emit unquoted DateTime literals with seconds and
  timezone information. Text, textarea, email, phone, URL, picklist, and similar
  textual field types should continue to quote and escape string values.
- Extend the backend SOQL parse/lower path to accept the concrete date and
  datetime literal forms emitted by the visual builder. The parser must treat a
  date or datetime literal as one right-hand-side value in a WHERE comparison,
  not as a number followed by punctuation and identifiers.
- Ensure the internal filter-expression lowering path accepts the preserved
  date/datetime literal representation when query execution converts filters to
  SQL predicates.
- Keep backend endpoint response shapes unchanged. Parser support is a
  compatibility fix for valid Salesforce-style WHERE literals, not a new public
  endpoint shape.
- Treat the current "between" operator expectation as out of scope unless the
  issue is expanded. The visual list-view filter builder currently models one
  scalar value per criterion row and does not expose a between operator in its
  operator list. Supporting between correctly would require a range value model
  and two bound inputs.
- Keep relative date literals out of this P1 fix. If a user types a relative
  literal in code mode, preserve existing code-mode behavior; the visual native
  controls only need to handle concrete date and datetime values.

## Testing Decisions

- Good tests should assert user-visible behavior: which control renders, whether
  it is interactive, and whether the filter expression changes when the user
  edits it. Avoid tests that only assert internal prop plumbing.
- Add a registry-level test through the exported registry lookup for a readable
  datetime audit field in filter context. The expected behavior is an input with
  type datetime-local.
- Add a registry-level test for the same field with omitted/default context. The
  expected behavior is non-interactive display output, matching current
  edit-context behavior.
- Add a mandatory filter-builder-level test. The test can either drive the field
  picker or seed the visual builder with a simple CreatedDate/LastModifiedDate
  criterion. It must assert that the visual builder renders an editable
  `input[type="datetime-local"]`.
- Add a mandatory serialization assertion for date/datetime filter values. When
  the datetime input changes, the emitted filter expression must contain an
  unquoted DateTime literal with seconds and timezone information. When a date
  field is covered, the expression must contain an unquoted YYYY-MM-DD literal.
- Add backend parser coverage for the exact date and datetime literal forms the
  frontend emits. The parsed request must preserve a valid internal filter
  expression and must not fail with an unexpected-token parse error.
- Add an end-to-end or highest-available integration test for the apply path:
  visual builder value change, frontend SOQL construction, backend SOQL parsing,
  and record query execution or query-request construction. The important
  behavior is that a timestamp filter can be applied successfully, not merely
  rendered.
- Add or preserve record-edit regression coverage at the highest existing seam.
  If a record form does not render audit fields because field metadata marks them
  non-writable, that is acceptable and should not be weakened just to test the
  registry guard.
- Run the focused frontend test files that cover the UI registry and filter
  builder.
- Run focused backend tests for SOQL parsing and query filter lowering because
  the accepted scope now touches both layers.
- For the PR, also run the standard frontend and backend verification commands
  used by the repository.
- If a future implementation expands scope to support between, add tests that
  assert both lower and upper datetime inputs render and serialize correctly.
  Without that implementation, a between test should not be added as part of
  this bug fix.

## Out of Scope

- Redesigning the list-view filter panel layout.
- Adding relative date literals such as TODAY, LAST WEEK, or LAST_N_DAYS.
- Adding between/not-between support to the visual list-view filter builder.
- Changing backend list-view describe, list-view result, or SOQL query endpoint
  shapes.
- Implementing relative date literal support in the backend parser.
- Adding user-timezone preference plumbing beyond the concrete DateTime literal
  normalization needed for the visual builder output.
- Changing which fields are considered system/audit fields.
- Changing record create, record edit, or inline edit permissions.
- Migrating every criteria builder in the frontend onto one shared value-input
  component.
- Adding Salesforce mobile list-view filter behavior.
- Changing list-view persistence or sharing.

## Further Notes

### Status-Quo Understanding

- The list-view header opens a filter panel, and that panel renders a visual/code
  criteria editor.
- The same visual criteria editor is also used by criteria-based sharing-rule
  configuration, so any context decision in the editor affects more than the
  list-view panel.
- The visual criteria editor stores rows with one field, one operator, and one
  scalar value. It can parse and emit simple SOQL-style expressions joined with
  AND.
- Current filter-expression serialization treats any JavaScript string value as
  a quoted string literal. Native date and datetime controls also emit strings,
  so the serialization path must use field metadata rather than JavaScript value
  type alone.
- The backend query endpoint uses a custom SOQL parser before dispatching record
  queries. That parser currently has string, number, identifier, keyword,
  operator, and punctuation tokens, but no dedicated date/datetime literal token;
  unquoted Salesforce date/datetime literals therefore need parser/lowering
  support before the visual builder can safely emit them.
- Query execution lowers the parser-produced filter expression into SQL through
  the formula/expression SQL walker, so parser acceptance and lowering
  acceptance must be verified together.
- The visual criteria editor obtains value controls through the UI registry by
  field type.
- The UI registry default input chooses native input types for date, datetime,
  and number-like fields, but then applies an unconditional display-only guard
  for CreatedDate and LastModifiedDate.
- Because the filter builder does not pass render context, the default input
  cannot tell a filter value input from a record-edit input.
- Record forms already have independent metadata-driven write gates. Inline edit
  also has an independent system-field guard. These paths should not need filter
  context.
- There is an unused or separately used criteria-value input component that
  already treats date and datetime as normal filter values. It confirms the
  product concept but is not currently wired into the list-view filter builder.

### Salesforce Implementation Understanding

- Salesforce Lightning list views have editable filters with field, operator,
  and value choices. The value options depend on the selected field/operator.
- Salesforce UI API list metadata models filters as field API name, operator,
  and operand values. That separates list-view filter operands from record-edit
  field mutability.
- Salesforce documentation describes date fields in filters as accepting
  locale-formatted dates and special relative date values.
- Salesforce SOQL WHERE clauses support date and dateTime comparisons. Date
  values use YYYY-MM-DD. DateTime values use ISO-style datetime forms with
  timezone offset or Z.
- Salesforce stores dateTime field values in UTC while accepting an explicit
  timezone offset in query literals.
- Salesforce date literals represent relative ranges, and equality against a
  date literal means "within that range" rather than scalar string equality.
- Salesforce UI/API filtering models date and datetime fields as filterable
  operator-bearing field types, separate from whether those fields are editable
  in record forms.
- Salesforce audit fields on custom objects include CreatedDate and
  LastModifiedDate and are read-only system-managed fields, with narrow data
  import exceptions outside normal record editing.

### External References

- Salesforce UI Lists API:
  https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-ui-lists-api.html
- Salesforce Help, Edit List View Filters in Lightning Experience:
  https://help.salesforce.com/s/articleView?id=customviews_edit_filters_lex.htm&language=en_US
- Salesforce Help, Notes about Filtering on Types of Fields and Values:
  https://help.salesforce.com/s/articleView?id=sf.notes_on_entering_filter_criteria.htm&language=en_US&type=5
- Salesforce SOQL and SOSL Reference, Date Formats and Date Literals in WHERE:
  https://resources.docs.salesforce.com/latest/latest/en-us/sfdc/pdf/salesforce_soql_sosl.pdf
- Salesforce User Interface API Developer Guide, list filter metadata:
  https://resources.docs.salesforce.com/latest/latest/en-us/sfdc/pdf/api_ui.pdf
- Salesforce GraphQL API Field Operators:
  https://developer.salesforce.com/docs/platform/graphql/guide/filter-fields.html
- Salesforce Object Reference, Audit Fields for Custom Objects:
  https://resources.docs.salesforce.com/latest/latest/en-us/sfdc/pdf/object_reference.pdf
