# Inline Account Editing 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:** Replace the sales summary wizard's flat data-grid with a two-column debit/credit layout matching the embedded grid, and add HTMX-based inline account editing (click pencil → typeahead → confirm → swap back to display mode).

**Architecture:** Each item's account cell renders in "display mode" (account name + hidden input + pencil icon). Clicking the pencil fires an HTMX GET that swaps in a typeahead + confirm/cancel buttons. Confirm fires an HTMX PUT that swaps back to display mode with an updated hidden input. No DB writes until the wizard form is submitted.

**Tech Stack:** Clojure, Hiccup, HTMX, Alpine.js (for typeahead), form-cursor, multi-modal wizard middleware, Datomic.

---

### Task 1: Add route keys to route definitions

**Files:**
- Modify: `src/cljc/auto_ap/routes/pos/sales_summaries.cljc`

- [ ] **Step 1: Add three new route keys**

Add these routes inside the existing `routes` map, alongside `"/edit/sales-summary-item"`:

```clojure
"/edit/item-account"       ::edit-item-account
"/edit/save-item-account"  ::save-item-account
"/edit/cancel-item-account" ::cancel-item-account
```

The full routes map should become:

```clojure
(def routes {""       {:get ::page
                       :put ::edit-wizard-submit}
             "/table" ::table
             ["/" [#"\d+" :db/id]] {:get ::edit-wizard}
             "/edit/navigate"          ::edit-wizard-navigate
             "/edit/sales-summary-item" ::new-summary-item
             "/edit/item-account"       ::edit-item-account
             "/edit/save-item-account"  ::save-item-account
             "/edit/cancel-item-account" ::cancel-item-account})
```

- [ ] **Step 2: Verify the route file parses**

Run: `clj -M:check` or similar. If no checker is available, move on — the Clojure compiler will catch errors at load time.

---

### Task 2: Add account display cell helper and account edit cell helper

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

These are pure rendering functions — no routes, no handlers, just hiccup.

- [ ] **Step 1: Make `account-typeahead*` public**

Change `defn-` to `defn` for `account-typeahead*` so it can be used from the new handlers:

```clojure
(defn account-typeahead*
  [{:keys [name value client-id]}]
  [:div.flex.flex-col
   (com/typeahead {:name name
                   :placeholder "Search..."
                   :url (hu/url (bidi/path-for ssr-routes/only-routes :account-search)
                                {:client-id client-id
                                 :purpose "invoice"})
                   :value value
                   :content-fn (fn [value]
                                 (:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
                                                                      client-id)))})])
```

- [ ] **Step 2: Add `account-display-cell` function**

This renders the display-mode account cell: account name (or "Missing acct" pill), hidden input, and pencil icon. Insert after the `truncate` defn:

```clojure
(defn account-display-cell [{:keys [item field-name-prefix client-id]}]
  (let [account-id (:ledger-mapped/account item)
        account-name (when account-id
                       (:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read account-id)
                                                            client-id)))]
    [:div.flex.items-center.gap-2
     (com/hidden {:name (str field-name-prefix "[ledger-mapped/account]")
                  :value (or account-id "")})
     (if account-id
       [:span.text-sm account-name]
       (com/pill {:color :red} "Missing acct"))
     (com/a-icon-button {:hx-get (bidi/path-for ssr-routes/only-routes ::route/edit-item-account)
                         :hx-target "closest td"
                         :hx-swap "innerHTML"
                         :hx-vals (hx/json {:item-index (or (:item-index item) 0)
                                            :client-id client-id
                                            :current-account-id (or account-id "")})}
                        svg/pencil)]))
```

- [ ] **Step 3: Add `account-edit-cell` function**

This renders the edit-mode account cell: typeahead + confirm/cancel buttons. This is what `::route/edit-item-account` returns:

```clojure
(defn account-edit-cell [{:keys [field-name-prefix client-id current-account-id]}]
  (let [account-input-name (str field-name-prefix "[ledger-mapped/account]")]
    [:div.flex.flex-col.gap-2
     (account-typeahead* {:name account-input-name
                          :value current-account-id
                          :client-id client-id})
     [:div.flex.gap-1
      (com/a-icon-button {:hx-put (bidi/path-for ssr-routes/only-routes ::route/save-item-account)
                          :hx-target "closest td"
                          :hx-swap "innerHTML"
                          :hx-include "closest td"
                          :hx-vals (hx/json {:field-name-prefix field-name-prefix
                                             :client-id client-id})}
                         svg/check)
      (com/a-icon-button {:hx-get (bidi/path-for ssr-routes/only-routes ::route/cancel-item-account)
                          :hx-target "closest td"
                          :hx-swap "innerHTML"
                          :hx-vals (hx/json {:field-name-prefix field-name-prefix
                                             :client-id client-id
                                             :current-account-id (or current-account-id "")})}
                         svg/x)]]))
```

**Note:** We construct the input name directly from `field-name-prefix` + `[ledger-mapped/account]` instead of using form-cursor, because the HTMX handler doesn't have access to the wizard's form state. The typeahead component accepts a `:name` string directly.

---

### Task 3: Verify svg/check exists

**Files:**
- Check: `src/clj/auto_ap/ssr/svg.clj`

- [ ] **Step 1: Search for check icon**

Run: `rg "def.*check" src/clj/auto_ap/ssr/svg.clj`

If `svg/check` does not exist, look for alternatives like `svg/tick`, `svg/confirm`, or `svg/save`. If none exist, use `svg/pencil` with a different label, or use a simple `[:span "✓"]` instead.

---

### Task 4: Rewrite MainStep render-step to use two-column layout

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

This is the core UI change. Replace the flat data-grid in `render-step` with a two-column layout matching the embedded grid.

- [ ] **Step 1: Replace the MainStep record's render-step body**

Replace the existing `render-step` implementation in the `defrecord MainStep` with:

```clojure
(render-step
  [this {:keys [multi-form-state] :as request}]
  (let [client-id (:db/id (:sales-summary/client (:snapshot multi-form-state)))
        items (sort-items (:sales-summary/items (:step-params multi-form-state)))
        debit-items (filter #(= :ledger-side/debit (:ledger-mapped/ledger-side %)) items)
        credit-items (filter #(= :ledger-side/credit (:ledger-mapped/ledger-side %)) items)
        max-rows (max (count debit-items) (count credit-items))
        padded-debits (concat debit-items (repeat (- max-rows (count debit-items)) nil))
        padded-credits (concat credit-items (repeat (- max-rows (count credit-items)) nil))]
    (mm/default-render-step
     linear-wizard this
     :head [:div.p-2 "Edit Summary"]
     :body (mm/default-step-body
            {}
            [:div
             (fc/with-field :db/id
               (com/hidden {:name (fc/field-name)
                            :value (fc/field-value)}))
             [:div.grid.grid-cols-2.gap-4
              [:div
               [:div.font-semibold.text-sm.mb-2 "Debits"]
               [:div.space-y-1
                (for [[idx item] (map-indexed vector padded-debits)]
                  (if item
                    (let [manual? (:sales-summary-item/manual? item)]
                      [:div.flex.items-center.gap-2.text-sm
                       (com/hidden {:name (str "step-params[sales-summary/items][" idx "][db/id]")
                                    :value (:db/id item)})
                       (com/hidden {:name (str "step-params[sales-summary/items][" idx "][sales-summary-item/category]")
                                    :value (:sales-summary-item/category item)})
                       (when manual?
                         (com/hidden {:name (str "step-params[sales-summary/items][" idx "][sales-summary-item/manual?]")
                                      :value "true"}))
                       [:span.text-gray-500 (truncate (:sales-summary-item/category item) 30)]
                       (account-display-cell {:item (assoc item :item-index idx)
                                              :field-name-prefix (str "step-params[sales-summary/items][" idx "]")
                                              :client-id client-id})
                       [:span.font-mono (format "$%,.2f" (:ledger-mapped/amount item))]])
                    [:div.h-6]))]
               (summary-total-row* request)
               (unbalanced-row* request)]
              [:div
               [:div.font-semibold.text-sm.mb-2 "Credits"]
               [:div.space-y-1
                (for [[idx item] (map-indexed vector padded-credits)]
                  (if item
                    (let [actual-idx (+ (count debit-items) idx)
                          manual? (:sales-summary-item/manual? item)]
                      [:div.flex.items-center.gap-2.text-sm
                       (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][db/id]")
                                    :value (:db/id item)})
                       (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][sales-summary-item/category]")
                                    :value (:sales-summary-item/category item)})
                       (when manual?
                         (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][sales-summary-item/manual?]")
                                      :value "true"}))
                       [:span.text-gray-500 (truncate (:sales-summary-item/category item) 30)]
                       (account-display-cell {:item (assoc item :item-index actual-idx)
                                              :field-name-prefix (str "step-params[sales-summary/items][" actual-idx "]")
                                              :client-id client-id})
                       [:span.font-mono (format "$%,.2f" (:ledger-mapped/amount item))]])
                    [:div.h-6]))]
               (summary-total-row* request)
               (unbalanced-row* request)]]
             [:div.mt-4
              (fc/with-field :sales-summary/items
                (com/data-grid-new-row {:colspan 2
                                        :hx-get (bidi/path-for ssr-routes/only-routes ::route/new-summary-item)
                                        :row-offset 0
                                        :index (count (fc/field-value))
                                        :tr-params {:hx-vals (hx/json {:client-id client-id})}}
                                       "New Summary Item"))]])

     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate)
     :validation-route ::route/edit-wizard-navigate
     :width-height-class "lg:w-[900px] lg:h-[600px]")))
```

**Important note on item indexing:** The padded lists are for display alignment only. The hidden inputs must use the *actual* index in the `:sales-summary/items` vector, not the display index. Debit items keep their original indices; credit items' indices start after all debit items. This is a simplification — if items are interspersed (debit, credit, debit), this approach breaks. We need to compute actual indices from the sorted list, not from the filtered sublists. See Step 2.

- [ ] **Step 2: Fix index calculation to use actual sorted position**

The approach in Step 1 has an indexing bug. Items in the form are stored as a vector and submitted by index. We must preserve the actual vector index for each item. Replace the layout logic with:

```clojure
(render-step
  [this {:keys [multi-form-state] :as request}]
  (let [client-id (:db/id (:sales-summary/client (:snapshot multi-form-state)))
        items (sort-items (:sales-summary/items (:step-params multi-form-state)))
        indexed-items (map-indexed vector items)
        debit-items (filter #(= :ledger-side/debit (:ledger-mapped/ledger-side (second %))) indexed-items)
        credit-items (filter #(= :ledger-side/credit (:ledger-mapped/ledger-side (second %))) indexed-items)
        max-rows (max (count debit-items) (count credit-items))
        padded-debits (concat debit-items (repeat (- max-rows (count debit-items)) nil))
        padded-credits (concat credit-items (repeat (- max-rows (count credit-items)) nil))]
    (mm/default-render-step
     linear-wizard this
     :head [:div.p-2 "Edit Summary"]
     :body (mm/default-step-body
            {}
            [:div
             (fc/with-field :db/id
               (com/hidden {:name (fc/field-name)
                            :value (fc/field-value)}))
             [:div.grid.grid-cols-2.gap-4
              [:div
               [:div.font-semibold.text-sm.mb-2 "Debits"]
               [:div.space-y-1
                (for [[actual-idx item] padded-debits]
                  (if item
                    (let [manual? (:sales-summary-item/manual? item)]
                      [:div.flex.items-center.gap-2.text-sm
                       (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][db/id]")
                                    :value (:db/id item)})
                       (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][sales-summary-item/category]")
                                    :value (:sales-summary-item/category item)})
                       (when manual?
                         (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][sales-summary-item/manual?]")
                                      :value "true"}))
                       [:span.text-gray-500 (truncate (:sales-summary-item/category item) 30)]
                       (account-display-cell {:item (assoc item :item-index actual-idx)
                                              :field-name-prefix (str "step-params[sales-summary/items][" actual-idx "]")
                                              :client-id client-id})
                       [:span.font-mono (format "$%,.2f" (:ledger-mapped/amount item))]])
                    [:div.h-6]))]
              [:div
               [:div.font-semibold.text-sm.mb-2 "Credits"]
               [:div.space-y-1
                (for [[actual-idx item] padded-credits]
                  (if item
                    (let [manual? (:sales-summary-item/manual? item)]
                      [:div.flex.items-center.gap-2.text-sm
                       (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][db/id]")
                                    :value (:db/id item)})
                       (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][sales-summary-item/category]")
                                    :value (:sales-summary-item/category item)})
                       (when manual?
                         (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][sales-summary-item/manual?]")
                                      :value "true"}))
                       [:span.text-gray-500 (truncate (:sales-summary-item/category item) 30)]
                       (account-display-cell {:item (assoc item :item-index actual-idx)
                                              :field-name-prefix (str "step-params[sales-summary/items][" actual-idx "]")
                                              :client-id client-id})
                       [:span.font-mono (format "$%,.2f" (:ledger-mapped/amount item))]])
                    [:div.h-6]))]]]
             [:div.mt-4
              (fc/with-field :sales-summary/items
                (com/data-grid-new-row {:colspan 2
                                        :hx-get (bidi/path-for ssr-routes/only-routes ::route/new-summary-item)
                                        :row-offset 0
                                        :index (count (fc/field-value))
                                        :tr-params {:hx-vals (hx/json {:client-id client-id})}}
                                       "New Summary Item"))]])

     :footer
     (mm/default-step-footer linear-wizard this :validation-route ::route/edit-wizard-navigate)
     :validation-route ::route/edit-wizard-navigate
     :width-height-class "lg:w-[900px] lg:h-[600px]")))
```

**Key design decisions:**
- `map-indexed vector items` preserves actual vector position for hidden input names
- `padded-debits` / `padded-credits` are sequences of `[actual-idx item]` or `nil` for padding rows
- Padding rows render as empty `[:div.h-6]` to maintain alignment
- Total/unbalanced rows are not repeated per column — they go below the two-column grid, shared

- [ ] **Step 3: Move total/unbalanced rows outside the two-column grid**

The `summary-total-row*` and `unbalanced-row*` functions currently render as `<tr>` elements inside a data-grid. In the new layout, these should be simple flex rows below the grid, not table rows. For now, keep them as-is but render them in a single section below both columns (remove the duplicate from the credit column). Adjust the `:body` content:

After the `[:div.grid.grid-cols-2.gap-4 ...]` block, add:

```clojure
[:div.mt-2.border-t.pt-2
 (summary-total-row* request)
 (unbalanced-row* request)]
```

But since `summary-total-row*` and `unbalanced-row*` currently return `<tr>` elements, they won't render correctly outside a table. For the initial implementation, replace them with inline hiccup that renders the same info in a flex layout. See Task 5.

---

### Task 5: Rewrite total and unbalanced display as non-table hiccup

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

The existing `summary-total-row*` and `unbalanced-row*` return `<tr>` / `<td>` elements for the data-grid. The new layout is not a table, so these need to be simple div-based layouts.

- [ ] **Step 1: Add `summary-total-display` function**

Insert after `unbalanced-row*`:

```clojure
(defn summary-total-display [request]
  (let [total-credits (-> request
                          :multi-form-state
                          :step-params
                          :sales-summary/items
                          (total-credits))
        total-debits (-> request
                         :multi-form-state
                         :step-params
                         :sales-summary/items
                         (total-debits))]
    [:div.flex.justify-between.text-sm.py-1
     [:span.font-semibold "Total"]
     [:div.flex.gap-8
      [:span.font-mono (format "$%,.2f" total-debits)]
      [:span.font-mono (format "$%,.2f" total-credits)]]]))
```

- [ ] **Step 2: Add `unbalanced-display` function**

```clojure
(defn unbalanced-display [request]
  (let [total-credits (-> request
                          :multi-form-state
                          :step-params
                          :sales-summary/items
                          (total-credits))
        total-debits (-> request
                         :multi-form-state
                         :step-params
                         :sales-summary/items
                         (total-debits))
        delta (- total-debits total-credits)]
    (when-not (dollars-0? delta)
      [:div.flex.justify-between.text-sm.py-1
       [:span.font-semibold {:class (if (pos? delta) "text-red-600" "text-green-600")} "Unbalanced"]
       [:div.flex.gap-8
        [:span.font-mono (when (pos? delta) (format "$%,.2f" delta))]
        [:span.font-mono (when (neg? delta) (format "$%,.2f" (Math/abs delta)))]]]])))
```

- [ ] **Step 3: Use these in the render-step body**

In Task 4's render-step, replace the total/unbalanced section at the bottom with:

```clojure
[:div.mt-2.border-t.pt-2
 (summary-total-display request)
 (unbalanced-display request)]
```

---

### Task 6: Add `edit-item-account` handler

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

- [ ] **Step 1: Write the handler**

Insert before `key->handler`:

```clojure
(defn edit-item-account [request]
  (let [{:keys [item-index client-id current-account-id]} (:query-params request)
        item-index (if (string? item-index) (Integer/parseInt item-index) item-index)
        field-name-prefix (str "step-params[sales-summary/items][" item-index "]")]
    (html-response
     (account-edit-cell {:field-name-prefix field-name-prefix
                         :client-id (if (string? client-id) (Long/parseLong client-id) client-id)
                         :current-account-id (when (and current-account-id
                                                        (not= current-account-id ""))
                                               (if (string? current-account-id)
                                                 (Long/parseLong current-account-id)
                                                 current-account-id))}))))
```

- [ ] **Step 2: Add it to key->handler**

Add to the handler map inside `key->handler`:

```clojure
::route/edit-item-account (-> edit-item-account
                              (wrap-schema-enforce :query-schema [:map
                                                                   [:item-index nat-int?]
                                                                   [:client-id {:optional true} [:maybe entity-id]]
                                                                   [:current-account-id {:optional true} [:maybe :string]]]))
```

---

### Task 7: Add `save-item-account` handler

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

- [ ] **Step 1: Write the handler**

This handler receives the typeahead's selected value via `hx-include "closest td"`, which includes the hidden input from the typeahead. The typeahead's hidden input name will be something like `step-params[sales-summary/items][2][ledger-mapped/account]`. We need to extract the selected account ID from the form params and return a display cell with the updated value.

```clojure
(defn save-item-account [request]
  (let [{:keys [field-name-prefix client-id]} (some-> request :query-params)
        account-input-name (str field-name-prefix "[ledger-mapped/account]")
        account-id-str (get-in request [:form-params account-input-name])
        account-id (when (and account-id-str (not= account-id-str ""))
                     (Long/parseLong account-id-str))
        item {:ledger-mapped/account account-id
              :item-index (second (re-find #"\[(\d+)\]" field-name-prefix))}]
    (html-response
     (account-display-cell {:item item
                            :field-name-prefix field-name-prefix
                            :client-id (if (string? client-id) (Long/parseLong client-id) client-id)}))))
```

**Note:** `field-name-prefix` comes from `hx-vals` in the confirm button. `account-input-name` is constructed by appending `[ledger-mapped/account]` to the prefix. The typeahead's hidden input will have this name.

- [ ] **Step 2: Add it to key->handler**

```clojure
::route/save-item-account (-> save-item-account
                              (mm/wrap-wizard edit-wizard)
                              (mm/wrap-decode-multi-form-state))
```

Wait — we said no DB writes until wizard submit. So we should NOT wrap with `wrap-wizard` and `wrap-decode-multi-form-state`. The handler just returns HTML. It doesn't need the wizard state. The form params contain the typeahead value, and the query params contain the field name prefix and client-id. Simple.

```clojure
::route/save-item-account save-item-account
```

---

### Task 8: Add `cancel-item-account` handler

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

- [ ] **Step 1: Write the handler**

```clojure
(defn cancel-item-account [request]
  (let [{:keys [field-name-prefix client-id current-account-id]} (:query-params request)
        account-id (when (and current-account-id (not= current-account-id ""))
                     (if (string? current-account-id)
                       (Long/parseLong current-account-id)
                       current-account-id))
        item {:ledger-mapped/account account-id
              :item-index (second (re-find #"\[(\d+)\]" field-name-prefix))}]
    (html-response
     (account-display-cell {:item item
                            :field-name-prefix field-name-prefix
                            :client-id (if (string? client-id) (Long/parseLong client-id) client-id)}))))
```

- [ ] **Step 2: Add it to key->handler**

```clojure
::route/cancel-item-account cancel-item-account
```

---

### Task 9: Wire routes in key->handler

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

- [ ] **Step 1: Add all three new handlers to key->handler**

The final additions to the handler map (before the closing `}`):

```clojure
::route/edit-item-account (-> edit-item-account
                              (wrap-schema-enforce :query-schema [:map
                                                                   [:item-index nat-int?]
                                                                   [:client-id {:optional true} [:maybe entity-id]]
                                                                   [:current-account-id {:optional true} [:maybe :string]]]))
::route/save-item-account save-item-account
::route/cancel-item-account cancel-item-account
```

These get the same middleware applied via `apply-middleware-to-all-handlers` at the bottom of `key->handler`.

---

### Task 10: Handle manual items in the two-column layout

**Files:**
- Modify: `src/clj/auto_ap/ssr/pos/sales_summaries.clj`

Manual items have editable category text inputs and debit/credit money inputs. In the two-column layout, manual items need to stay in "edit mode" with their inputs visible.

- [ ] **Step 1: Add manual item rendering in the debit/credit columns**

In the render-step `for` loop, when `manual?` is true, render the editable fields instead of display-mode:

For a debit manual item:

```clojure
[:div.flex.items-center.gap-2.text-sm
 (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][db/id]")
              :value (:db/id item)})
 (com/hidden {:name (str "step-params[sales-summary/items][" actual-idx "][sales-summary-item/manual?]")
              :value "true"})
 (fc/start-form-with-prefix [(str "step-params[sales-summary/items][" actual-idx "]")]
                            item []
   (fc/with-field :sales-summary-item/category
     (com/text-input {:placeholder "Category/Explanation"
                      :name (fc/field-name)
                      :value (fc/field-value)
                      :class "w-32 text-sm"}))
   (account-display-cell {:item (assoc item :item-index actual-idx)
                           :field-name-prefix (str "step-params[sales-summary/items][" actual-idx "]")
                           :client-id client-id}))
 (fc/start-form-with-prefix [(str "step-params[sales-summary/items][" actual-idx "]")]
                            item []
   (fc/with-field :debit
     (com/money-input {:class "w-24 text-sm"
                       :name (fc/field-name)
                       :value (fc/field-value)})))]
```

For a credit manual item, replace `:debit` with `:credit`.

---

### Task 11: Test the complete flow end-to-end

**Files:**
- Manual testing

- [ ] **Step 1: Open a sales summary row in the wizard**

Verify the two-column layout renders correctly with debits on the left, credits on the right.

- [ ] **Step 2: Verify display-mode account cells**

Each item should show the account name (or "Missing acct" pill) + hidden input + pencil icon.

- [ ] **Step 3: Click a pencil icon**

The cell should swap to show a typeahead search + confirm (check) and cancel (X) buttons.

- [ ] **Step 4: Search and select an account in the typeahead**

After selecting, click confirm. The cell should swap back to display mode with the updated account name and hidden input value.

- [ ] **Step 5: Click cancel**

The cell should swap back to the original display mode.

- [ ] **Step 6: Submit the wizard**

All hidden inputs (including the updated account) should be submitted. Verify the transaction updates the correct accounts.

- [ ] **Step 7: Test manual items**

Add a new summary item. Verify it renders with editable category + money inputs. Verify the account cell still uses the pencil-to-typeahead pattern.

- [ ] **Step 8: Test total/unbalanced display**

Verify totals and unbalanced indicators update correctly (if the `expense-account-total` route is fixed — out of scope for this plan but note if broken).
