# CapEx UX Improvements Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Implement six UX improvements to the CapEx Tracker — cleaner export buttons, alternating row colors, compact popup headers, a char counter layout fix, removal of the manual refresh button, and row anchoring after save.

**Architecture:** All changes are to existing PHP/JS files in `public/finance/capex_v2/`. No new files are created. The row anchoring feature works by adding `data-id` attributes to table rows (PHP), then using those attributes in updated JS modal-close functions to scroll and flash the edited row after the AJAX table refresh resolves.

**Tech Stack:** PHP 8 (strict_types), mysqli, vanilla JS (fetch, Promises), CSS animations. No build step. Test via browser at `http://whitewater-secure/public/finance/capex_v2/tracker.php`.

---

## File Map

| File | What changes |
|---|---|
| `public/finance/capex_v2/api/capex_tracker_table.php` | Add `data-id` to `<tr>` |
| `public/finance/capex_v2/api/capex_planning_table.php` | Add `data-id` to `<tr>` |
| `public/finance/capex_v2/tracker.php` | Remove Export-Reconcile, add tooltip, add stripe CSS + anchor CSS, update `refreshTrackerTable` + `closeTrackerModal` + new `anchorRow` |
| `public/finance/capex_v2/capex_planning_fragment.php` | Remove Export-Reconcile, add tooltip, remove Refresh button, add stripe CSS + anchor CSS, update `refreshPlanningTable` + `closeCapexModal` + new `anchorPlanningRow` |
| `public/finance/capex_v2/tracker_editor.php` | Remove logo `<img>`, `<h2>` → `<h3>`, update save handler close block |
| `public/finance/capex_v2/support/capex_planning_editor.php` | Remove logo `<img>`, `<h2>` → `<h3>`, fix char counter layout, update save handler close block |

---

## Task 1: Add data-id to tracker table rows

**Spec ref:** Section 6
**File:** `public/finance/capex_v2/api/capex_tracker_table.php`

- [ ] **Step 1: Open the file and find line 96**

  Current code:
  ```php
  echo '<tr ' . $row_style . '>';
  ```
  The variable `$id` is set on line 94: `$id = (int)$row['ID'];`

- [ ] **Step 2: Add data-id attribute**

  Replace:
  ```php
  echo '<tr ' . $row_style . '>';
  ```
  With:
  ```php
  echo '<tr data-id="' . $id . '" ' . $row_style . '>';
  ```
  The Grand Total row (line 117, `bgcolor="lightgrey"`) is unchanged — no `data-id` needed.

- [ ] **Step 3: Verify in browser**

  Open `http://whitewater-secure/public/finance/capex_v2/tracker.php`.
  Open DevTools → Elements. Find a row in `#tbl_exporttable_to_xls tbody`. Confirm it has `data-id="123"` (some integer). Grand Total row should have no `data-id`.

---

## Task 2: Add data-id to planning table rows

**Spec ref:** Section 6
**File:** `public/finance/capex_v2/api/capex_planning_table.php`

- [ ] **Step 1: Open the file and find line 55**

  Current code:
  ```php
  echo '<tr data-href="&id=' . (int)$row['id'] . '" ' . $row_style . '>';
  ```

- [ ] **Step 2: Add data-id alongside existing data-href**

  Replace:
  ```php
  echo '<tr data-href="&id=' . (int)$row['id'] . '" ' . $row_style . '>';
  ```
  With:
  ```php
  echo '<tr data-href="&id=' . (int)$row['id'] . '" data-id="' . (int)$row['id'] . '" ' . $row_style . '>';
  ```

- [ ] **Step 3: Verify in browser**

  Open tracker.php → click Planning tab. Open DevTools → Elements. Find a row in `#tbl_exporttable_to_xls2 tbody`. Confirm it has both `data-href` and `data-id` attributes.

---

## Task 3: tracker.php — export button, stripe CSS, anchor logic

**Spec ref:** Sections 1, 2, 6
**File:** `public/finance/capex_v2/tracker.php`

### 3a: Remove Export-Reconcile button and add tooltip to Export

- [ ] **Step 1: Find the Export buttons in the PO tab (around line 528–531)**

  Current code:
  ```html
  <input type="button" id="gobutton_small" value="Export-Reconcile" style="margin-bottom:0;" onclick="ExportToExcel('xlsx')">
  &nbsp;&nbsp;
  <input type="button" id="gobutton_small" value="Export" style="margin-bottom:0;" onclick="window.location.href='export.php' + window.location.search">
  ```

- [ ] **Step 2: Remove Export-Reconcile, add tooltip to Export**

  Replace the entire two-button block with:
  ```html
  <input type="button" id="gobutton_small" value="Export" style="margin-bottom:0;" title="This will export all the data in the view — the filtered data." onclick="window.location.href='export.php' + window.location.search">
  ```

- [ ] **Step 3: Verify in browser**

  Reload tracker.php → PO tab. Confirm only one Export button exists. Hover over it — tooltip should appear.

### 3b: Add stripe CSS

- [ ] **Step 4: Add alternating row CSS to the existing `<style>` block**

  In the `<style>` block (after the existing `.capex-filter-cell` rules, around line 272), add:
  ```css
  #tbl_exporttable_to_xls tbody tr:nth-child(even) {
      background-color: #eef4fb;
  }
  ```

- [ ] **Step 5: Verify in browser**

  Reload tracker.php → PO tab. Even-numbered rows should show a soft blue stripe. Rows with yellow (Requested) or red (Denied) status keep their status color — inline styles override the CSS.

### 3c: Add anchor CSS and update JS functions

- [ ] **Step 6: Add anchor CSS to the `<style>` block**

  After the stripe rule added in Step 4, add:
  ```css
  @keyframes rowFlash {
      0%   { background-color: #c5f3e0; }
      100% { background-color: transparent; }
  }
  .row-anchor-flash {
      animation: rowFlash 1.5s ease-out forwards;
  }
  ```

- [ ] **Step 7: Update `refreshTrackerTable` to return the fetch promise**

  Current function (around line 159):
  ```js
  function refreshTrackerTable() {
      const url = "api/capex_tracker_table.php" + window.location.search;
      fetch(url)
          .then(res => res.text())
          .then(html => {
              const tbody = document.getElementById("trackerBody");
              if (tbody) {
                  tbody.innerHTML = html;
              } else {
                  console.warn("trackerBody not found in DOM");
              }
          })
          .catch(err => console.error("Table refresh failed", err));
  }
  ```

  Replace with (only change is adding `return` on the `fetch(...)` line — replace the whole function):
  ```js
  function refreshTrackerTable() {
      const url = "api/capex_tracker_table.php" + window.location.search;
      return fetch(url)  // added return
          .then(res => res.text())
          .then(html => {
              const tbody = document.getElementById("trackerBody");
              if (tbody) {
                  tbody.innerHTML = html;
              } else {
                  console.warn("trackerBody not found in DOM");
              }
          })
          .catch(err => console.error("Table refresh failed", err));
  }
  ```

- [ ] **Step 8: Update `closeTrackerModal` to accept rowId and add `anchorRow`**

  Current `closeTrackerModal` function (around line 192):
  ```js
  function closeTrackerModal() {
      document.getElementById("trackerEditorModal").style.display = "none";
      document.getElementById("trackerEditorFrame").src = "";
  }
  ```

  Replace with:
  ```js
  function closeTrackerModal(rowId) {
      document.getElementById("trackerEditorModal").style.display = "none";
      document.getElementById("trackerEditorFrame").src = "";
      if (rowId) {
          refreshTrackerTable().then(() => anchorRow(rowId));
      }
  }

  function anchorRow(rowId) {
      const row = document.querySelector('#tbl_exporttable_to_xls tr[data-id="' + rowId + '"]');
      if (row) {
          row.scrollIntoView({ behavior: 'smooth', block: 'center' });
          row.classList.add('row-anchor-flash');
          row.addEventListener('animationend', () => row.classList.remove('row-anchor-flash'), { once: true });
      }
  }
  ```

  **Note:** The overlay `onclick="closeTrackerModal()"` and Close button call the function with no argument — `rowId` will be `undefined`, the `if (rowId)` guard skips the anchor. No changes needed to those callers.

- [ ] **Step 9: Verify anchor logic is wired (pre-editor step)**

  In DevTools console on tracker.php, run:
  ```js
  anchorRow(/* paste a real ID from a data-id attr */);
  ```
  The matching row should scroll into view and flash green briefly.

---

## Task 4: capex_planning_fragment.php — export button, stripe CSS, remove refresh, anchor logic

**Spec ref:** Sections 1, 2, 5, 6
**File:** `public/finance/capex_v2/capex_planning_fragment.php`

### 4a: Remove Export-Reconcile and Refresh buttons, add tooltip

- [ ] **Step 1: Find the export/refresh buttons (around line 325–327)**

  Current code:
  ```html
  <input type="button" id="gobutton_small" value="Export-Reconcile" style="margin-bottom:0;" onclick="ExportToExcel_plan('xlsx')">
  <input type="button" id="gobutton_small" value="Export" style="margin-bottom:0;" onclick="window.location.href='export_planning.php' + window.location.search">
  <td><input type="button" value="🔄 Refresh Table" onclick="refreshPlanningTable()" id="gobutton_small"></td>
  ```

- [ ] **Step 2: Remove Export-Reconcile and Refresh buttons, add tooltip to Export**

  Replace with:
  ```html
  <input type="button" id="gobutton_small" value="Export" style="margin-bottom:0;" title="This will export all the data in the view — the filtered data." onclick="window.location.href='export_planning.php' + window.location.search">
  ```
  Also remove the `<td>` cell containing the Refresh Table button entirely.

- [ ] **Step 3: Verify in browser**

  Open tracker.php → Planning tab. Confirm only one Export button with tooltip. No Refresh button.

### 4b: Add stripe CSS and anchor CSS

- [ ] **Step 4: Add CSS to the existing `<style>` block at the top of the fragment (around line 13)**

  After the existing `.filterbar1` rule, add:
  ```css
  #tbl_exporttable_to_xls2 tbody tr:nth-child(even) {
      background-color: #eef4fb;
  }
  @keyframes rowFlash {
      0%   { background-color: #c5f3e0; }
      100% { background-color: transparent; }
  }
  .row-anchor-flash {
      animation: rowFlash 1.5s ease-out forwards;
  }
  ```

- [ ] **Step 5: Verify stripe in browser**

  Planning tab rows should alternate with soft blue. Status-colored rows (light blue Requested, green Ordered) keep their colors.

### 4c: Update JS functions for anchoring

- [ ] **Step 6: Update `refreshPlanningTable` to return the fetch promise**

  Replace the entire existing `refreshPlanningTable` function body (around line 390) with the version below — do not add `return` in place, replace the whole function:

  Current function (around line 390):
  ```js
  function refreshPlanningTable() {
      // ...
      fetch(url)
          .then(res => res.text())
          .then(html => {
              const tbody = document.getElementById("planningBody");
              if (tbody) tbody.innerHTML = html;
          })
          .catch(err => console.error("Planning table refresh failed", err));
  }
  ```

  Replace with (add `return`):
  ```js
  function refreshPlanningTable() {
      const allowed = ['plan_status', 'plan_requestor', 'plan_location', 'plan_type',
                       'plan_msa', 'plan_commitment', 'plan_ad', 'plan_rd', 'plan_category',
                       'plan_deploy', 'plan_quote_confirmed', 'plan_priority', 'plan_year',
                       'plan_po', 'plan_startdate_post', 'plan_enddate_post'];
      const params = new URLSearchParams(window.location.search);
      const planParams = new URLSearchParams();
      allowed.forEach(k => {
          params.getAll(k).forEach(v => planParams.append(k, v));
      });
      const url = "api/capex_planning_table.php?" + planParams.toString();
      return fetch(url)
          .then(res => res.text())
          .then(html => {
              const tbody = document.getElementById("planningBody");
              if (tbody) tbody.innerHTML = html;
          })
          .catch(err => console.error("Planning table refresh failed", err));
  }
  ```

- [ ] **Step 7: Update `closeCapexModal` to accept rowId and add `anchorPlanningRow`**

  Current `closeCapexModal` (around line 441):
  ```js
  function closeCapexModal() {
      document.getElementById('capexModal').style.display = 'none';
      document.getElementById('capexModalFrame').src = '';
  }
  ```

  Replace with:
  ```js
  function closeCapexModal(rowId) {
      document.getElementById('capexModal').style.display = 'none';
      document.getElementById('capexModalFrame').src = '';
      if (rowId) {
          refreshPlanningTable().then(() => anchorPlanningRow(rowId));
      }
  }

  function anchorPlanningRow(rowId) {
      const row = document.querySelector('#tbl_exporttable_to_xls2 tr[data-id="' + rowId + '"]');
      if (row) {
          row.scrollIntoView({ behavior: 'smooth', block: 'center' });
          row.classList.add('row-anchor-flash');
          row.addEventListener('animationend', () => row.classList.remove('row-anchor-flash'), { once: true });
      }
  }
  ```

  **Note:** The backdrop click and ESC keydown listeners call `closeCapexModal()` with no argument — safe, `if (rowId)` guards the anchor.

---

## Task 5: tracker_editor.php — header cleanup + save handler

**Spec ref:** Sections 3, 6
**File:** `public/finance/capex_v2/tracker_editor.php`

- [ ] **Step 1: Remove the logo and change h2 to h3**

  Find (around line 102–103):
  ```html
  <img width="210" height="155" src="https://portal.whitewatercw.com/branding/LOGO_Shadow-RegisteredMark.png"><br>
  <h2 style="display:inline-block; font-weight: bold">Capex Tracker</h2>
  ```

  Replace with:
  ```html
  <h3 style="display:inline-block; font-weight: bold">Capex Tracker</h3>
  ```

- [ ] **Step 2: Update the save handler close block**

  In the save handler's `.then()` success block (around line 547–551), find:
  ```js
  if (window.parent && typeof window.parent.closeTrackerModal === 'function') {
      window.parent.refreshTrackerTable();
      window.parent.closeTrackerModal();
  } else {
      window.close();
  }
  ```

  Replace with:
  ```js
  if (window.parent && typeof window.parent.closeTrackerModal === 'function') {
      window.parent.closeTrackerModal(<?= $edit_id ?>);
  } else {
      window.close();
  }
  ```

- [ ] **Step 3: Verify in browser**

  Open tracker.php → click View on any row. The modal should open without the large logo at the top, with a smaller "Capex Tracker" heading. Save a change — modal should close, table should refresh, and the edited row should scroll into view and flash green.

---

## Task 6: capex_planning_editor.php — header cleanup, char counter fix, save handler

**Spec ref:** Sections 3, 4, 6
**File:** `public/finance/capex_v2/support/capex_planning_editor.php`

- [ ] **Step 1: Remove the logo and change h2 to h3**

  Find (around line 183–184):
  ```html
  <img width="210" height="155" src="https://portal.whitewatercw.com/branding/LOGO_Shadow-RegisteredMark.png"><br>
  <h2 style="display:inline-block; font-weight: bold">Capex Planning</h2>
  ```

  Replace with:
  ```html
  <h3 style="display:inline-block; font-weight: bold">Capex Planning</h3>
  ```

- [ ] **Step 2: Fix the char counter / Add Note button layout**

  Find the notes section (around line 361–363):
  ```html
  <textarea size="85" id="edit_notes_maintenance_history_plan" name="notes_maintenance_history" class="inputs" style="min-width:90%" maxlength="500" readonly><?php echo htmlspecialchars($edit_entry['notes_maintenance_history'] ?? '');?></textarea>
  <button type="button" id="addNoteBtn" class="save" style="margin-left:15px; padding:5px 15px;" disabled>Add Note</button>
  <div id="charCount" style="font-size: 0.75em; color: #666; text-align: left; margin-top: -28px;">500 characters remaining</div>
  ```

  Replace with (remove `margin-top: -28px`, reorder to textarea → counter → button):
  ```html
  <textarea size="85" id="edit_notes_maintenance_history_plan" name="notes_maintenance_history" class="inputs" style="min-width:90%" maxlength="500" readonly><?php echo htmlspecialchars($edit_entry['notes_maintenance_history'] ?? '');?></textarea>
  <div id="charCount" style="font-size: 0.75em; color: #666; text-align: left; margin-top: 2px;">500 characters remaining</div>
  <button type="button" id="addNoteBtn" class="save" style="margin-top: 6px; padding:5px 15px;" disabled>Add Note</button>
  ```

- [ ] **Step 3: Update the save handler close block**

  In the save handler's `.then()` success block (around line 443–451), find:
  ```js
  if (window.parent !== window) {
      window.parent.closeCapexModal();
      window.parent.refreshPlanningTable();
  } else {
      localStorage.setItem('capexRefresh', 'planning');
      window.close();
  }
  ```

  Replace with:
  ```js
  if (window.parent !== window) {
      window.parent.closeCapexModal(<?= $edit_id ?>);
  } else {
      localStorage.setItem('capexRefresh', 'planning');
      window.close();
  }
  ```

- [ ] **Step 4: Verify in browser**

  Open tracker.php → Planning tab → click View on any row. Confirm:
  - No logo at the top; smaller "Capex Planning" heading
  - Notes section shows: textarea → char counter on its own line → Add Note button below it (no overlap)
  - Save a change → modal closes, planning table refreshes, edited row scrolls into view and flashes green

---

## Task 7: Final smoke test

- [ ] **Step 1: Tracker tab — full check**

  Open `http://whitewater-secure/public/finance/capex_v2/tracker.php`
  - [ ] Only one Export button visible; hover shows tooltip
  - [ ] Even rows have soft blue (#eef4fb) background; status-colored rows unaffected
  - [ ] Click View → modal opens with compact header, no logo
  - [ ] Save a change → modal closes → table refreshes → edited row flashes green and is centered in view
  - [ ] Click X or press ESC on modal without saving → closes cleanly, no refresh, no flash

- [ ] **Step 2: Planning tab — full check**

  Click Planning tab:
  - [ ] Only one Export button visible; hover shows tooltip; no Refresh button
  - [ ] Even rows have soft blue stripe
  - [ ] Click View → modal opens with compact header, no logo
  - [ ] Notes section: char counter below textarea, Add Note button below counter, no overlap
  - [ ] Save a change → modal closes → planning table refreshes → edited row flashes green
  - [ ] Click X / ESC / backdrop without saving → closes cleanly, no refresh

- [ ] **Step 3: Hand off to Eric for commit**

  All changes are local edits only. Eric commits when ready per project workflow.
