refactor(ssr): full Selmer migration of Transaction Edit; remove the wizard
Migrate every part of the Transaction Edit modal's HTML to Selmer templates
(zero Hiccup in the render path) and delete the mm multi-modal "wizard"
abstraction entirely -- there was only ever one step.
- New auto-ap.ssr.components.selmer (sc) + ~22 shared component partials under
resources/templates/components/ (typeahead, button-group, radio-card,
data-grid, validated-field, modal, buttons, inputs, SVGs). Each wrapper renders
its own partial; dynamic HTMX/Alpine attrs bridge via attrs->str -> {{attrs|safe}}.
- 15 modal templates under resources/templates/transaction-edit/.
- Delete EditWizard/LinksStep records + all mm/* usage. Plain handlers: flat
wrap-decode-edit (fields renamed off step-params[...], stray keys stripped),
flat wrap-derive-state, *errors*-based field errors, generic wrap-form-4xx-2.
- Drop the edit-wizard-navigate route (routes ~12 -> 5).
- Fix: stray `method` (tab button-group hidden) leaked into the upsert -> 500;
strip decoded map to schema keys.
- e2e selectors updated (#wizard-form->#edit-form, #wizardmodal->#editmodal,
step-params[...] field names). Parity: swap 6/6, edit 8/8, suite 38/1
(1 pre-existing unrelated nav test).
- ssr-form-migration skill updated with the learnings (composition mechanics,
sc/* library, drop-the-wizard recipe, scorecard row, 3 new gotchas).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ async function openEditModal(page: any, transactionIndex: number = 0) {
|
||||
|
||||
// Wait for the modal to open
|
||||
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
|
||||
await page.waitForSelector('#wizardmodal');
|
||||
await page.waitForSelector('#editmodal');
|
||||
|
||||
// The modal is now single-page (Edit Transaction). Click "Manual" tab to ensure
|
||||
// the manual account coding form is active.
|
||||
@@ -144,7 +144,7 @@ async function saveTransaction(page: any) {
|
||||
}
|
||||
|
||||
async function toggleToPercentMode(page: any) {
|
||||
const percentRadio = page.locator('input[name="step-params[amount-mode]"][value="%"]');
|
||||
const percentRadio = page.locator('input[name="amount-mode"][value="%"]');
|
||||
await percentRadio.click();
|
||||
|
||||
// Wait for HTMX to swap the grid body
|
||||
@@ -155,7 +155,7 @@ async function toggleToPercentMode(page: any) {
|
||||
}
|
||||
|
||||
async function toggleToDollarMode(page: any) {
|
||||
const dollarRadio = page.locator('input[name="step-params[amount-mode]"][value="$"]');
|
||||
const dollarRadio = page.locator('input[name="amount-mode"][value="$"]');
|
||||
await dollarRadio.click();
|
||||
|
||||
// Wait for HTMX to swap the grid body
|
||||
@@ -235,7 +235,7 @@ test.describe('Transaction Edit Full Workflow', () => {
|
||||
await openEditModal(page, 0);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const dollarRadio = page.locator('input[name="step-params[amount-mode]"][value="$"]');
|
||||
const dollarRadio = page.locator('input[name="amount-mode"][value="$"]');
|
||||
await expect(dollarRadio).toBeChecked();
|
||||
|
||||
const val0 = await (await findAccountRow(page, 0)).locator('.account-amount-field').inputValue();
|
||||
@@ -272,7 +272,7 @@ test.describe('Transaction Edit Validation', () => {
|
||||
await expect(page.locator('#modal-holder[x-show="open"]')).toBeVisible();
|
||||
|
||||
// The form should still be present
|
||||
const form = page.locator('#wizard-form');
|
||||
const form = page.locator('#edit-form');
|
||||
await expect(form).toBeVisible();
|
||||
|
||||
// Verify the account row is still there with our $50 value
|
||||
@@ -304,7 +304,7 @@ async function openEditModalForTransaction(page: any, description: string) {
|
||||
// navigation), so the action tabs -- including "Link to payment" -- are available
|
||||
// immediately; callers click the tab they need.
|
||||
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
|
||||
await page.waitForSelector('#wizardmodal');
|
||||
await page.waitForSelector('#editmodal');
|
||||
}
|
||||
|
||||
async function selectVendorFromTypeahead(page: any, vendorName: string) {
|
||||
@@ -447,7 +447,7 @@ async function openManualVendorSection(page: any, transactionIndex: number) {
|
||||
await editButton.click();
|
||||
|
||||
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
|
||||
await page.waitForSelector('#wizardmodal');
|
||||
await page.waitForSelector('#editmodal');
|
||||
await page.click('button:has-text("Manual")');
|
||||
await page.waitForSelector('div[hx-vals*="vendor-changed"]');
|
||||
}
|
||||
@@ -472,7 +472,7 @@ test.describe('Transaction Edit Vendor Selection', () => {
|
||||
// The server-rendered hidden input must carry the newly selected vendor id.
|
||||
const hidden = page
|
||||
.locator(
|
||||
'div[hx-vals*="vendor-changed"] input[type="hidden"][name="step-params[transaction/vendor]"]'
|
||||
'div[hx-vals*="vendor-changed"] input[type="hidden"][name="transaction/vendor"]'
|
||||
)
|
||||
.first();
|
||||
await expect(hidden).toHaveValue(vendorId.toString());
|
||||
|
||||
Reference in New Issue
Block a user