---
title: Improve NFL week parameter selector UX/UI in data views
type: task
status: completed
description: >-
  Replace the flat checkbox list for nfl_week parameter selection with a dedicated component
  featuring a structured builder, grouped selection summary, and expandable full list. Rename param
  key from nfl_week to nfl_week_id, remove year limit, fix default value, add dynamic year-range
  values, and improve builder UX.
base_uri: user:task/league/improve-nfl-week-selector-ux.md
created_at: '2026-03-28T06:14:24.757Z'
entity_id: 969d0b83-cb52-4061-817e-313e9f09fed6
finished_at: '2026-03-28T06:23:48.889Z'
observations: []
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - >-
    succeeds
    [[user:task/github/mistakia/league/309-refactor-implement-a-date-format-to-describe-a-nfl-season-week.md]]
started_at: '2026-03-28T06:16:42.773Z'
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-03-28T19:19:08.990Z'
visibility_analyzed_at: '2026-11-05T23:49:38.583Z'
---

# Implementation Plan: NFL Week Parameter Selector UX Improvement

## Overview

- Replace the flat checkbox list for `nfl_week` parameter selection with a dedicated component featuring a structured builder and grouped selection summary
- Add a generic custom param filter extension point to react-table so domain-specific filter components live in consuming apps
- The NFL week filter component lives in the league project, not in react-table
- Rename the `nfl_week` param key to `nfl_week_id` for clarity, with backward compatibility for persisted state
- Remove the artificial year limit on available values
- Fix default value behavior so "ALL" is shown when no explicit selection is made
- Add missing dynamic values (last_n_nfl_years, next_n_nfl_years) to match the year param pattern
- Improve builder UX: always-visible week rows, clear buttons, drag hint

## Background

- The `nfl_week` parameter is defined in `libs-shared/common-column-params.mjs` (isomorphic, runs on client and server)
- React-table is a standalone generic library consumed by the league app via npm; it should not contain domain-specific code
- React-table already supports custom cell rendering via `column.component` (React component reference); the same pattern extends naturally to param filter components
- Column param definitions flow unchanged from `common-column-params.mjs` through field definitions to react-table's internal routing components
- The server never accesses UI-specific properties (`component`, `param_override_config`) on param definitions
- The league app's webpack config includes react-table source in babel transpilation, so league components can import react-table internals (e.g., `react-table/src/filter-base`)
- Shared utilities (`group_nfl_weeks`, `format_week_ranges`, `get_postseason_week_label`, `get_max_weeks_for_season_type`) are already implemented in `libs-shared/nfl-week-identifier.mjs`
- The param key `nfl_week` is ambiguous -- it encodes year + season type + week as a composite identifier (e.g., `2025_REG_WEEK_5`) but the name suggests just a week number; the database column is already `nfl_week_id`
- Param values are limited to 5 recent years via `get_nfl_week_identifiers_for_recent_years({ count: 5 })` while the `year` param offers all years (2000-present)
- The `default_value` is a single week identifier; removing it lets `null` mean "all selected" per the existing SELECT filter convention where `null`/undefined = all, `[]` = clear, `[...values]` = explicit selection
- The `year` param has `last_n_years`/`next_n_years` dynamic values; the `nfl_week` param lacks equivalents
- Persisted state (database `user_data_views.table_state` and `user_plays_views.table_state` JSON, localStorage snapshots, URLs) stores `nfl_week` as the param key and requires migration

## Design

### Generic Extension Point in React-table

React-table's 3 param filter routing files gain a single check before the `data_type` switch: if `column_param_definition.component` is a function (React component), render it directly. This mirrors the existing cell rendering pattern and requires no new props, context, or abstractions.

### Component Location

The NFL week filter component lives in `app/views/components/column-param-nfl-week-filter/` in the league project, where it can import from `@libs-shared` directly.

### Enhancement Point

`data-views-fields/index.js` enhances `column_params.nfl_week_id` with the React component reference after field assembly. This is a single point of change that covers all field definitions.

### Param Rename: `nfl_week` -> `nfl_week_id`

Rename the export and all references across libs-shared, libs-server, app/core, and tests. Add server-side fallback (`params.nfl_week_id = params.nfl_week_id || params.nfl_week`) in the central `resolve_nfl_week_params` processing function to handle persisted state during transition. Run a SQL migration to update stored JSON keys in `user_data_views` and `user_plays_views`. Add browser localStorage migration on state restore.

### Remove Year Limit

Add `get_all_nfl_week_identifiers()` to `nfl-week-identifier.mjs` that generates identifiers for all years from MIN_YEAR (2000) to `current_season.year`. Use it in the param definition instead of the count-limited function.

### Default Value

Remove `default_value` from the param definition entirely. The UI displays "ALL" when `selected_param_values` is null/undefined. Server-side `get_default_params()` functions already handle unset params with their own defaults.

### Dynamic Values

Add `last_n_nfl_years` and `next_n_nfl_years` to the param's `dynamic_values` array. Add corresponding handlers in `process_dynamic_nfl_week_param()` that generate all week identifiers for the requested year range.

### Builder UX

- Always render week rows for all 3 season types (PRE, REG, POST) regardless of type selection state
- Type toggle buttons control which types are included when "Add to Selection" is clicked (not visibility of week rows)
- Add "Clear" button per type row alongside "All"
- Add hint text "Drag to select ranges" below the Builder section header

### Data Flow

```
common-column-params.mjs (shared) defines nfl_week_id param data
  -> field definitions assemble column_params: { nfl_week_id }
  -> data-views-fields/index.js enhances nfl_week_id.component = ColumnParamNflWeekFilter
  -> <Table all_columns={fields} />
  -> react-table routing: typeof component === 'function' -> renders custom component
  -> Server: process_dynamic_nfl_week_param() expands dynamic values including new year-range types
```

## Notes

- The league component imports `FilterBase` from `react-table/src/filter-base` following the established pattern for importing react-table internals
- The component uses `@libs-shared` imports for NFL week parsing/formatting utilities instead of vendoring them
- The `column_name: 'nfl_week_id'` property on the param definition does NOT change -- it already maps to the correct database column
- The `process_dynamic_nfl_week_param` and `resolve_nfl_week_params` function names remain unchanged -- they describe what they do, not the param key name
- Dynamic value type strings (`current_nfl_week`, `last_n_nfl_weeks`) are stored in persisted state and stay unchanged to avoid additional migration complexity
- The `nfl_plays_column_params` object in `nfl-plays-column-params.mjs` spreads the param into a larger object keyed by param name -- this key must change to `nfl_week_id`
- All `.whereIn('nfl_week_id', ...)` database queries remain unchanged -- they reference the column name, not the param key
- Generating all years (2000-present) produces ~783 identifiers (27 years x 29 weeks); the server already handles `.whereIn()` with arrays of this size

## Plan

### Phase 1: Component and Extension Point (Completed)

- [x] **Remove NFL week filter component from react-table**

  - Delete `src/column-param-nfl-week-filter/` directory (all files: index.js, column-param-nfl-week-filter.js, nfl-week-builder.js, nfl-week-selection-summary.js, nfl-week-utils.js, nfl-week-filter.styl)

- [x] **Revert string-based routing in react-table**

  - In `src/column-controls-column-param-item/column-controls-column-param-item.js`: remove `ColumnParamNflWeekFilter` import and `component === 'nfl_week_select'` check
  - In `src/filter-controls-selected-columns-parameters/filter-controls-selected-columns-parameters.js`: same removal
  - In `src/column-controls-selected-columns-parameters/column-controls-selected-columns-parameters.js`: same removal

- [x] **Remove string identifier from param definition**

  - In `libs-shared/common-column-params.mjs`: remove `component: 'nfl_week_select'` from the `nfl_week` export

- [x] **Add generic custom component check to param filter routing**

  - In `src/column-controls-column-param-item/column-controls-column-param-item.js`: before the `data_type` switch, add check: if `typeof column_param_definition.component === 'function'`, render it with `param_props`
  - In `src/filter-controls-selected-columns-parameters/filter-controls-selected-columns-parameters.js`: same check before `data_type` switch
  - In `src/column-controls-selected-columns-parameters/column-controls-selected-columns-parameters.js`: same check before `data_type` switch

- [x] **Create ColumnParamNflWeekFilter component in league project**

  - Directory: `app/views/components/column-param-nfl-week-filter/`
  - Files: `index.js`, `column-param-nfl-week-filter.js`, `nfl-week-builder.js`, `nfl-week-selection-summary.js`, `column-param-nfl-week-filter.styl`
  - Adapts the existing component code but imports from `@libs-shared` instead of vendored utils and from `react-table/src/filter-base` for the FilterBase wrapper
  - Same props interface as `ColumnParamSelectFilter`: `column_param_name`, `column_param_definition`, `selected_param_values`, `handle_change`, `mixed_state`, `splits`

- [x] **Enhance nfl_week param at field assembly point**

  - In `app/core/data-views-fields/index.js`: import `ColumnParamNflWeekFilter`, after field assembly loop set `field.column_params.nfl_week.component = ColumnParamNflWeekFilter` for any field that has `column_params.nfl_week`

- [x] **Update component property test**
  - In `test/data-views-nfl-week.spec.mjs`: adjust the test asserting `common_column_params.nfl_week.component` since the string identifier is removed from the shared definition

### Phase 2: Shared Utilities

- [x] **Add `get_all_nfl_week_identifiers` function**
  - In `libs-shared/nfl-week-identifier.mjs`: add function that loops `get_nfl_week_identifiers_for_year` from MIN_YEAR to `current_season.year`
  - Export the new function

### Phase 2: Param Definition Update

- [x] **Rename export and update param properties**

  - In `libs-shared/common-column-params.mjs`:
    - Rename `export const nfl_week` to `export const nfl_week_id`
    - Add `label: 'NFL Week'`
    - Replace `values: get_nfl_week_identifiers_for_recent_years({ count: 5 })` with `values: get_all_nfl_week_identifiers()`
    - Remove the `default_value` property entirely
    - Add `last_n_nfl_years` (default: 3) and `next_n_nfl_years` (default: 1) to `dynamic_values` with `has_value_field: true`
    - Update import to include `get_all_nfl_week_identifiers`

- [x] **Update nfl-plays-column-params.mjs**
  - Rename import from `nfl_week` to `nfl_week_id`
  - Update the key in the exported params object from `nfl_week` to `nfl_week_id`

### Phase 2: Client-Side Updates

- [x] **Update data-views-fields references**

  - In `app/core/data-views-fields/index.js`: change `column_params?.nfl_week` to `column_params?.nfl_week_id` (2 references)
  - In `app/core/data-views-fields/projected-table-fields.js`: update destructure and usage from `nfl_week` to `nfl_week_id`
  - In `app/core/data-views-fields/game-table-fields.js`: update destructure and usage from `nfl_week` to `nfl_week_id`

- [x] **Improve builder UX in nfl-week-builder.js**
  - Always show week rows for all 3 season types regardless of type selection
  - Type toggle buttons control which types are included in "Add to Selection" (not visibility)
  - Add "Clear" button per type row alongside "All"
  - Add hint text "Drag to select ranges" below the Builder section header

### Phase 2: Server-Side Updates

- [x] **Update central param processing with backward compatibility**

  - In `libs-server/get-data-view-results.mjs`:
    - In `resolve_nfl_week_params`: add fallback `params.nfl_week_id = params.nfl_week_id || params.nfl_week` at the top for backward compatibility
    - Rename all `params.nfl_week` references to `params.nfl_week_id`
    - Update the year/week conditional checks
  - In `process_dynamic_nfl_week_param`: add handlers for `last_n_nfl_years` and `next_n_nfl_years` dynamic types that generate all week identifiers for the requested year range

- [x] **Update column definition files**

  - In `libs-server/data-views-column-definitions/player-practice-column-definitions.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views-column-definitions/player-rankings-column-definitions.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views-column-definitions/game-column-definitions.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views-column-definitions/player-dfs-salaries-column-definitions.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views-column-definitions/player-projected-column-definitions.mjs`: rename `params.nfl_week` to `params.nfl_week_id`

- [x] **Update rate-type files**

  - In `libs-server/data-views/rate-type/rate-type-per-game.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views/rate-type/rate-type-per-player.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views/rate-type/rate-type-per-player-play.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views/rate-type/rate-type-per-team-play.mjs`: rename `params.nfl_week` to `params.nfl_week_id`
  - In `libs-server/data-views/rate-type/rate-type-per-player-route.mjs`: rename `params.nfl_week` to `params.nfl_week_id`

- [x] **Update play-by-play column param processing**

  - In `libs-server/apply-play-by-play-column-params-to-query.mjs`: rename `'nfl_week'` string check to `'nfl_week_id'`

- [x] **Update add-team-stats with-statement**
  - In `libs-server/data-views/add-team-stats-play-by-play-with-statement.mjs`: rename `default_params.nfl_week` to `default_params.nfl_week_id`

### Phase 2: Backward Compatibility and Migration

- [x] **Add database migration for stored table state**

  - Write SQL to update `user_data_views.table_state` and `user_plays_views.table_state` JSON: rename the `nfl_week` key to `nfl_week_id` within `params` objects in both `columns` and `where` arrays
  - Run on production database, then export schema via `yarn export:schema`

- [x] **Add browser localStorage migration**
  - In `app/core/data-views/browser-storage.mjs`: add a migration function that renames `nfl_week` to `nfl_week_id` in stored snapshots on load
  - Call migration during state restoration

### Phase 2: Testing

- [x] **Update and add tests**
  - In `test/data-views-nfl-week.spec.mjs`: rename all `common_column_params.nfl_week` references to `common_column_params.nfl_week_id`
  - Update test for removed `component` property to reference `nfl_week_id`
  - Add tests for `get_all_nfl_week_identifiers()`
  - Add tests for `last_n_nfl_years` and `next_n_nfl_years` dynamic value types
  - Verify no `default_value` exists on the param definition

### Phase 2: Documentation

- [x] **Update data views documentation**
  - In `docs/data-views-system.md`: rename `nfl_week` references to `nfl_week_id`
