---
title: 'Data-View UI Polish: Save Tooltip and Clear-Cache Menu'
type: task
status: completed
description: >-
  Land the two UI affordances deferred from the data-view storage rework: a visibly-disabled Save
  button with tooltip on system/default views in react-table, and a Clear-local-view-cache menu
  entry with confirmation dialog on the league data-views page.
base_uri: user:task/league/data-view-ui-polish-save-tooltip-and-clear-cache.md
created_at: '2026-04-24T23:21:30.532Z'
entity_id: d1a53a60-b1ae-446c-8373-62496ea4a3d3
finished_at: '2026-04-25T14:15:24.149Z'
observations:
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - follows [[user:task/league/rework-data-view-browser-storage.md]]
  - references [[user:text/league/data-view-storage-architecture.md]]
started_at: '2026-04-25T13:47:01.617Z'
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-06-01T16:13:33.176Z'
visibility_analyzed_at: '2026-11-05T23:48:42.383Z'
---

# Implementation Plan: Data-View UI Polish — Save Tooltip and Clear-Cache Menu

## Overview

- Land the two UI affordances deferred from the data-view storage rework (parent task `[[user:task/league/rework-data-view-browser-storage.md]]`):
  - Default/system-view Save: replace the current "hide Save when not editable" behavior in react-table's `Table` with a visibly-disabled Save button carrying tooltip text `Duplicate this view to save your changes` (no terminal period; matches the existing `Unsaved local edits` tooltip in `table-view-controller.js`). Click on the disabled button is a no-op; the existing per-view Duplicate flows in `ViewItem` and `column-controls-selected-column` remain the action sites.
  - Clear local view cache: surface a league-side entry point that opens a confirmation dialog and dispatches the already-shipped `clear_local_view_cache()` action creator.
- Expected outcomes:
  - Users editing a system view see a discoverable path forward (visible-but-disabled Save + tooltip) instead of a vanished button.
  - Users hitting cache-shaped issues (stale snapshot, quota recovery aftermath, cross-device divergence) have a one-click escape hatch behind a confirmation guard.
  - No further react-table version drift; the league pin moves forward by one commit-SHA bump.

## Background

### Storage rework (parent) — what already shipped

- League commit `abc03c3f` and react-table commit `1a2e7a2` closed the structural rework: versioned migration registry, TTL+LRU eviction, stricter write gates, debounced persistence, `REVERT_DATA_VIEW`, `reconcile_server_views`, unsaved-edit indicator on `ViewItem`, and quota-callback toast.
- All redux + storage plumbing for both deferred items is in place:
  - `clear_local_view_cache()` action creator and `CLEAR_LOCAL_VIEW_CACHE` constant in `app/core/data-views/actions.js`.
  - `watch_clear_local_view_cache` saga registered in `data_views_sagas`, calling `clear_all()`.
  - `clear_all()` primitive in `libs-shared/data-view-storage/storage.mjs` (removes `data_view_history_*` keys, `data_view_metadata`, and `data_view_last_active`).
  - Container `app/views/pages/data-views/index.js` already wires `clear_local_view_cache` through `map_dispatch_to_props`.
- Only the two UI entry points are missing.

### Save-button current state (react-table `src/table/table.js:827–853`)

- Save renders only when `is_table_state_changed && is_selected_view_editable`. When the selected view is system-owned (`view_username === 'system'` per the league container's logic at `data-views.js:347–349`, `is_selected_view_editable={isLoggedIn && selected_data_view.user_id === user_id}`), the button is hidden entirely.
- Reset is gated by `Boolean(saved_table_state)` and remains correct.
- The currently selected view is in scope at `table.js:189` and `table.js:275` (used for save/edit metadata).
- Existing duplicate flow: `table-view-controller.js:55–70` `handle_duplicate_click` calls `on_view_change(...)` with a freshly-generated `view_id`, name suffix `(copy)`, current `table_state`, and `{ view_state_changed: true, is_new_view: true }`. `column-controls-selected-column.js:133` repeats the same pattern.

### Clear-cache landing surface (league)

- `app/views/pages/data-views/data-views.js` body has three children inside `page-table__container`: the request-status banner (`render_request_status()` at line 320), the `page-table__container-help` row (`InfoOutlinedIcon` + "Visit the guide..." span at lines 321–327), and the `Table` component (line 328). There is no separate toolbar.
- Confirmation-dialog precedent: `app/views/components/{deactivate,reserve,waiver,activate,reserve-long-term,rookie,restricted-free-agency}-confirmation/`. Standard shape: MUI `Dialog` + `DialogTitle` + `DialogContent` + `DialogContentText` + `DialogActions`, with `@components/button` Cancel/Confirm pair. None ship a colocated spec.

### Cross-repo flow

- League pins react-table by GitHub commit-SHA: `package.json` line 187 `"react-table": "mistakia/react-table#fe8bb86954b77e45bae5b864023261befbe3c620"`. There is no semver bump.
- `repository/active/react-table/dist/react-table.js` is committed; `yarn build` regenerates it. The build artifact must be committed alongside the source change so `yarn install` in league resolves to a SHA whose build reflects the source change.
- React-table CLAUDE.md prescribes `yarn lint`, `yarn prettier`, `yarn build`. MUI is a peerDependency; `Tooltip` is available without adding deps.

## Design

### Approach

Two cohesive UI surfaces, one task, two worktrees, sequenced. Surface 2 (clear-cache) lands first as the faster, self-contained win. Surface 1 (Save tooltip) follows as a paired commit: react-table commit on `mistakia/react-table` main, then a league commit bumping the `package.json` SHA pin and refreshing `yarn.lock`.

### Surface 1: Default/system-view Save tooltip (react-table)

- Remove the `is_selected_view_editable && (...)` gate on the Save button at `src/table/table.js:837–843`. Render Save unconditionally inside the existing `is_table_state_changed` branch.
- When `is_selected_view_editable` is true: behave as today — `onClick={save_table_state_change}`, no tooltip wrapper.
- When `is_selected_view_editable` is false: render the same button with a `-disabled` modifier class, no `onClick`, wrapped in a MUI `Tooltip` whose `title` is exactly `Duplicate this view to save your changes` (no terminal period; canonical string used in every reference in this plan and in the JSX attribute). MUI tooltips do not fire on disabled native elements; wrapping the disabled `<div>` in a `<span>` keeps hover detection intact (the existing button is already a `<div>`, but the wrapper pattern is documented to avoid future surprises).
- Add a `.table-top-lead-button.save.-disabled` rule in `src/table/table.styl` (cursor default, opacity reduced, no hover state).
- No prop additions, no context changes, no exports change. Behavior is fully determined by the existing `is_selected_view_editable` prop.
- Rejected: option (b) where clicking the disabled Save triggers Duplicate. The existing per-view Duplicate menu in `ViewItem` and column-controls already covers one-click duplication; conflating Save and Duplicate at a third site is UX cost without a real shortcut.

### Surface 2: Clear-local-view-cache menu item (league)

- Placement: extend the existing `page-table__container-help` row at `data-views.js:321–327`. Right side of the row, opposite the info-icon "Visit the guide..." span. Stretch the existing row to `space-between` in `data-views.styl`. No new chrome.
- Component: `app/views/components/clear-data-view-cache-confirmation/` with `index.js` (default re-export of the component) and `clear-data-view-cache-confirmation.js`. Mirrors the established league confirmation-component pattern. No styl file unless the dialog needs custom layout.
- Component contract: `ClearDataViewCacheConfirmation({ onClose, clear_local_view_cache })`. Renders MUI `Dialog open onClose={onClose}` with title "Clear local view cache," `DialogContentText` calling out that locally-cached snapshot history for all views is wiped, including unsaved local edits not yet pushed to the server, and that server-stored views are not affected. `DialogActions` has Cancel (`onClose`) and Confirm (calls `clear_local_view_cache()` then `onClose()`).
- Page wiring (`data-views.js`): add `clear_local_view_cache` to the destructured props in the `DataViewsPage` function signature; add `useState` for `cache_clear_dialog_open`; render a small text button "Clear local cache" inside the help row; conditionally render the confirmation component when the dialog is open.
- Container wiring (`index.js`): no change. Already passes `clear_local_view_cache` through `map_dispatch_to_props`.

### Knowledge Base Impact

Per `[[user:guideline/knowledge-base/persist-knowledge.md#planning-time-kb-impact]]`:

- Domain entry document: `user:text/league/ABOUT.md` — no change. Already references `data-view-storage-architecture.md`.
- Affected ABOUT.md files: none beyond the existing pointer.
- Existing entity update: `user:text/league/data-view-storage-architecture.md` — append a "User-facing affordances" subsection covering (1) the Clear-local-view-cache menu entry (page location, confirmation guard, action it dispatches, scope of `clear_all`); (2) the disabled-Save tooltip behavior on system/default views and its pointer to the per-view Duplicate flow. Single load-on-modification source for the subsystem stays consolidated; no new text entity.
- Tag implications: none. `user:tag/league-xo-football.md` covers this.
- Observation candidates: on this task, `[plan-completed]` and `[plan-revised]` per the standard convention. On the parent task `[[user:task/league/rework-data-view-browser-storage.md]]` after both surfaces merge, add `[plan-completed-followup] <date> deferred UI polish landed: Save tooltip + clear-cache menu`.
- Guideline candidates: none. The MUI `Tooltip`-on-disabled pattern and the confirmation-dialog pattern are both already represented in the codebase.
- Connectivity: existing `text/league/ABOUT.md` → `data-view-storage-architecture.md` link satisfies the connectivity floor; the appended subsection inherits that connectivity.

## Notes

- Recommended ship order: Surface 2 first (faster, validates parent saga + action wiring on a real surface), Surface 1 second (requires cross-repo coordination). Either can proceed independently if convenient.
- Worktrees:
  - Surface 2: `repository/active/league-worktrees/clear-data-view-cache-menu` on branch `feat/clear-data-view-cache-menu`.
  - Surface 1 — react-table: `repository/active/react-table-worktrees/data-view-save-tooltip` on branch `feat/data-view-save-tooltip`.
  - Surface 1 — league SHA bump: `repository/active/league-worktrees/data-view-save-tooltip-bump` on branch `feat/data-view-save-tooltip-bump`.
- The committed `dist/react-table.js` makes the build step part of the commit; running `yarn build` and including the regenerated artifact in the same commit as the source change is mandatory. Otherwise league `yarn install` resolves to a SHA whose build does not reflect the source change.
- `clear_all()` is destructive and irreversible from the UI. The confirmation dialog copy must explicitly call out "unsaved local edits not yet pushed to the server will be lost." Server-stored views are unaffected — call this out too.
- No new redux, saga, selector, or storage code is expected. If implementation surfaces a missing wire, that is a finding worth flagging back to the parent task.
- No new tests. The saga + storage primitives already have coverage from the parent task. League's `*-confirmation` components do not have associated specs; following precedent means no new test file.
- Per `[[user:guideline/software/simplify-software-plan.md]]`: the change is a thin UI layer over already-shipped plumbing, no new abstraction, no new module boundary, no speculative configurability. Per `[[user:guideline/write/write-javascript.md]]`: imports use existing `@components`, `@core/selectors`, and react-table's existing import patterns; no new package aliases needed. Per `[[user:guideline/write/write-software.md]]`: descriptive, searchable names (`clear_data_view_cache_confirmation`, `cache_clear_dialog_open`).
- Relevant guidelines: `[[user:guideline/write/write-javascript.md]]`, `[[user:guideline/write/write-software.md]]`, `[[user:guideline/software/simplify-software-plan.md]]`, `[[user:guideline/software/simplify-software-implementation.md]]`, `[[user:guideline/knowledge-base/persist-knowledge.md]]`.

## Implementation Status

- Worktree (Surface 2): `/Users/trashman/user-base/repository/active/league-worktrees/clear-data-view-cache-menu` on branch `feat/clear-data-view-cache-menu`. Code changes in place; ready for merge-worktree.
- Worktree (Surface 1a, react-table): `/Users/trashman/user-base/repository/active/react-table-worktrees/data-view-save-tooltip` on branch `feat/data-view-save-tooltip`. Code changes in place. `yarn lint` + `yarn prettier` pass clean. `yarn build` is moot here: `dist/react-table.js` is not tracked in git (only `dist/react-table.js.LICENSE.txt` exists in history) and league imports `react-table/index.js` (source path) directly, bypassing `main`. The build also has pre-existing missing peer-dep failures (`highcharts`, `highcharts-react-official`) unrelated to this change.
- Surface 1b (league SHA bump) deferred: requires the react-table feat branch to be pushed first to obtain a SHA. Run after `merge-worktree` for `feat/data-view-save-tooltip`.

## Tasks

### Surface 2 — Clear Local View Cache Menu (league, lands first)

- [x] **Create `app/views/components/clear-data-view-cache-confirmation/clear-data-view-cache-confirmation.js`**
  - File: `repository/active/league/app/views/components/clear-data-view-cache-confirmation/clear-data-view-cache-confirmation.js`
  - Purpose: confirmation dialog gating the destructive `clear_local_view_cache()` dispatch.
  - Changes:
    - Default-export functional component `ClearDataViewCacheConfirmation({ onClose, clear_local_view_cache })`.
    - Render MUI `Dialog open onClose={onClose}` with `DialogTitle` "Clear local view cache."
    - `DialogContent` / `DialogContentText` body: explain that all locally-cached snapshot history will be wiped, including unsaved local edits not yet pushed to the server, and that server-stored views are not affected.
    - `DialogActions` with `@components/button` Cancel (`onClick={onClose}`, text variant) and Confirm (`onClick` calls `clear_local_view_cache()` then `onClose()`, text variant).
    - PropTypes: `onClose: PropTypes.func`, `clear_local_view_cache: PropTypes.func`.
    - Imports follow the existing `deactivate-confirmation.js` pattern (MUI `Dialog`, `DialogTitle`, `DialogContent`, `DialogContentText`, `DialogActions` from `@mui/material/...`; `Button` from `@components/button`).

- [x] **Create `app/views/components/clear-data-view-cache-confirmation/index.js`**
  - File: `repository/active/league/app/views/components/clear-data-view-cache-confirmation/index.js`
  - Purpose: barrel-style default re-export matching the sibling-component pattern. Component is unconnected (props supplied by parent page); no `connect()` wrapping.
  - Changes:
    - `export { default } from './clear-data-view-cache-confirmation.js'` (or equivalent default re-export).

- [x] **`app/views/pages/data-views/data-views.js`**
  - File: `repository/active/league/app/views/pages/data-views/data-views.js`
  - Purpose: add the toolbar entry and dialog open/close state.
  - Changes:
    - Add `useState` to the existing React import on line 1 (currently `import React, { useEffect, useMemo, useCallback } from 'react'` — extend to include `useState`). Forgetting this is a runtime error.
    - Add `clear_local_view_cache` to the destructured props in the `DataViewsPage` function signature (currently absent; container already passes it).
    - Add `useState` for `cache_clear_dialog_open` (default `false`).
    - In the `page-table__container-help` row (lines 321–327), keep the info-icon + "Visit the guide..." span on the left. Add a right-aligned text button "Clear local cache" with `onClick` setting `cache_clear_dialog_open` to true.
    - Render `<ClearDataViewCacheConfirmation onClose={...} clear_local_view_cache={...} />` conditionally when `cache_clear_dialog_open` is true. Place the conditional render immediately after the closing `</div>` of `page-table__container-help` and before `<Table>` so the JSX site is unambiguous. `onClose` sets state back to false. (MUI `Dialog` is portal-rendered, so DOM placement does not affect visual output, but pinning the JSX site removes implementer ambiguity.)
    - Import `ClearDataViewCacheConfirmation` from `@components/clear-data-view-cache-confirmation`.
    - Add `clear_local_view_cache: PropTypes.func` to the existing PropTypes block.

- [x] **`app/views/pages/data-views/data-views.styl`**
  - File: `repository/active/league/app/views/pages/data-views/data-views.styl`
  - Purpose: stretch the help row to `space-between` so the right-aligned button sits opposite the info-icon span.
  - Changes:
    - The existing `.page-table__container-help` rule (line 76) already declares `display flex` and `align-items center`. Add ONLY `justify-content space-between` to the existing rule; do not redeclare `display` or `align-items`.
    - Optional: small style for the new "Clear local cache" button if the default `@components/button` text style does not match the help-row visual weight.

### Surface 1 — Default-View Save Tooltip (react-table → league SHA bump)

- [x] **`repository/active/react-table/src/table/table.js`**
  - File: `repository/active/react-table/src/table/table.js`
  - Purpose: replace the hidden-when-non-editable Save with a visibly-disabled tooltip-wrapped Save.
  - Changes:
    - Remove the `is_selected_view_editable && (...)` gate on the Save button at lines 837–843. Render Save unconditionally inside the `is_table_state_changed` branch.
    - When `is_selected_view_editable` is true: render today's `<div className='table-top-lead-button save' onClick={save_table_state_change}>Save</div>` unchanged.
    - When `is_selected_view_editable` is false: render the same element with class `'table-top-lead-button save -disabled'`, no `onClick`. Wrap the disabled element in a `<span>` (MUI `Tooltip` requires a wrapper element capable of receiving hover events when its child is non-interactive) and that span in `<Tooltip title="Duplicate this view to save your changes">` — string MUST be exact, no terminal period.
    - Add a new import: `import Tooltip from '@mui/material/Tooltip'` (MUI is a peerDependency; `Tooltip` is not currently imported in this file — confirmed via grep). The import is unconditional; do not guard on existing presence.
    - No new props, no PropTypes additions.

- [x] **`repository/active/react-table/src/table/table.styl`**
  - File: `repository/active/react-table/src/table/table.styl`
  - Purpose: visually distinguish the disabled Save state.
  - Changes:
    - Add a rule under `.table-top-lead-button.save.-disabled` (or sibling under `.table-top-lead-button` with a `&.-disabled` modifier) setting `cursor: default`, reduced `opacity` (~0.5), and removing the existing hover state for the disabled variant. Keep the existing `.save` color scheme so the button still reads as Save semantically.

- [ ] **React-table commit and build**
  - Worktree: `repository/active/react-table-worktrees/data-view-save-tooltip` on branch `feat/data-view-save-tooltip`.
  - Run `yarn lint`, `yarn prettier`, `yarn build` (per `repository/active/react-table/CLAUDE.md`).
  - Commit the JS, styl, AND regenerated `dist/react-table.js` in the same commit. Push to `mistakia/react-table` main per the repo's normal flow. Capture the resulting commit SHA.

- [ ] **League SHA bump for react-table**
  - Worktree: `repository/active/league-worktrees/data-view-save-tooltip-bump` on branch `feat/data-view-save-tooltip-bump`.
  - File: `repository/active/league/package.json` line 187 — replace the existing `react-table` SHA pin (`fe8bb86954b77e45bae5b864023261befbe3c620`) with the SHA from the react-table commit.
  - Run `yarn install` to refresh `yarn.lock`. Commit `package.json` and `yarn.lock` together.
  - Run `yarn lint` and `yarn test --reporter min` to confirm no regressions.

### Knowledge Base Updates

- [x] **Update `user:text/league/data-view-storage-architecture.md`**
  - Purpose: document the two new user-facing affordances under the same load-on-modification entity that already covers the storage subsystem.
  - Changes:
    - Append a "User-facing affordances" subsection (or extend an existing UX/affordances section) covering:
      - The "Clear local cache" entry on the data-views page help row, the confirmation-dialog guard, the action it dispatches, and the scope of `clear_all` (wipes all `data_view_history_*`, `data_view_metadata`, `data_view_last_active`; server views unaffected).
      - The disabled-Save tooltip behavior on system/default views (`is_selected_view_editable === false`), tooltip text `Duplicate this view to save your changes` (no terminal period), and the pointer to the per-view Duplicate flow as the action site.
    - Use `base entity update` (or direct edit) — no new entity required.

- [ ] **Add followup observation to parent task on merge**
  - Purpose: link this task's completion back to the parent rework so the lineage is discoverable from either entity.
  - Changes:
    - On `[[user:task/league/rework-data-view-browser-storage.md]]`, add an observation: `[plan-completed-followup] <YYYY-MM-DD> deferred UI polish landed: Save tooltip + clear-cache menu` referencing this task's `base_uri`.

### Testing & Validation

- [ ] **Manual verification — Surface 2 (clear-cache)**
  - Open the data-views page, confirm "Clear local cache" button appears in the help row opposite the info-icon span.
  - Click the button, confirm the dialog opens with the destructive-action copy.
  - Click Cancel: dialog closes, `localStorage` `data_view_history_*` keys are unchanged.
  - Click Confirm: dialog closes, `localStorage` `data_view_history_*` keys, `data_view_metadata`, and `data_view_last_active` are removed; server-fetched views still render after a reload.
  - Reload the page: views still load from server, no `data_view_history_*` snapshots are restored until new edits occur.

- [ ] **Manual verification — Surface 1 (Save tooltip)**
  - Open a system/default view (e.g., a built-in projections view), edit the table state to dirty it.
  - Confirm Save button is visible, disabled-styled (reduced opacity, default cursor), and hovering surfaces tooltip `Duplicate this view to save your changes` (no terminal period — assert the exact string).
  - Confirm clicking the disabled Save is a no-op (no save attempt, no error, no redux action dispatched).
  - Open a user-owned editable view, edit the table state, confirm Save is enabled, no tooltip wrapper visible, and clicks save as today.
  - Confirm Reset button behavior is unchanged in both cases (gated by `Boolean(saved_table_state)`).
  - Run `yarn lint` and `yarn prettier` in the react-table worktree before committing (per `repository/active/react-table/CLAUDE.md`); the new `Tooltip` import and JSX changes must pass both gates.

### Configuration

- [ ] **Verify no new package aliases or dependencies needed**
  - League: `@components`, `@core/selectors`, `@mui/material/...` all already resolved. No `package.json` `imports` change.
  - React-table: `@mui/material` peerDependency already present; `Tooltip` import resolves without adding deps.
