Preset out/qty=1 and use button group for movement type

When arriving at /movements/new with ?part_id=…, default to an 'out'
movement of 1 — recording a sale is the common reason to follow that
link from a part page. Also fix the part option not appearing selected
in the listbox (option value was a number, bound value a string).

Replace the type <select> with three large segmented buttons so the
most-used choice on this page is a single click.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
David Beccue
2026-05-16 11:52:58 +05:00
parent 6b78fc7593
commit b22630a870

View File

@ -11,7 +11,9 @@
// Reference data/form directly here — the reactive `$: values = ...` above // Reference data/form directly here — the reactive `$: values = ...` above
// hasn't run yet at component init. // hasn't run yet at component init.
let movementType = form?.values?.movement_type ?? 'in'; // When arriving with ?part_id=… (from a part detail page), default to an
// 'out' movement of 1 — the common case is recording a sale.
let movementType = form?.values?.movement_type ?? (data?.presetPartId ? 'out' : 'in');
let partId = String(form?.values?.part_id ?? data?.presetPartId ?? ''); let partId = String(form?.values?.part_id ?? data?.presetPartId ?? '');
let partSearch = ''; let partSearch = '';
@ -59,7 +61,8 @@
// Quantity: for 'adjust' we pre-fill with the part's current on-hand so // Quantity: for 'adjust' we pre-fill with the part's current on-hand so
// the user can edit to the new total. Same don't-clobber-manual-edits // the user can edit to the new total. Same don't-clobber-manual-edits
// rule as unit price. // rule as unit price.
let quantity = form?.values?.quantity ?? ''; // When arriving from a part detail page, start at 1 (the typical sale).
let quantity = form?.values?.quantity ?? (data?.presetPartId ? '1' : '');
let lastAutoQuantity = ''; let lastAutoQuantity = '';
$: { $: {
@ -76,14 +79,21 @@
<h1>{$t('movements.new')}</h1> <h1>{$t('movements.new')}</h1>
<form class="stack" method="POST"> <form class="stack" method="POST">
<label> <fieldset class="type-group">
{$t('movements.type')} <legend>{$t('movements.type')}</legend>
<select name="movement_type" bind:value={movementType}> <input type="hidden" name="movement_type" value={movementType} />
<option value="in">{$t('movements.type_in')}</option> <div class="type-buttons" role="radiogroup">
<option value="out">{$t('movements.type_out')}</option> {#each ['in', 'out', 'adjust'] as opt}
<option value="adjust">{$t('movements.type_adjust')}</option> <button type="button"
</select> class="type-btn"
</label> class:active={movementType === opt}
aria-pressed={movementType === opt}
on:click={() => (movementType = opt)}>
{$t('movements.type_' + opt)}
</button>
{/each}
</div>
</fieldset>
<label> <label>
{$t('movements.part')} * {$t('movements.part')} *
@ -94,7 +104,7 @@
<select name="part_id" bind:value={partId} required size={Math.min(8, Math.max(3, visibleParts.length + 1))}> <select name="part_id" bind:value={partId} required size={Math.min(8, Math.max(3, visibleParts.length + 1))}>
<option value=""></option> <option value=""></option>
{#each visibleParts as p} {#each visibleParts as p}
<option value={p.id}> <option value={String(p.id)}>
{p.sku}{localized(p, 'name', lang)} ({$t('parts.quantity_on_hand')}: {p.quantity_on_hand}) {p.sku}{localized(p, 'name', lang)} ({$t('parts.quantity_on_hand')}: {p.quantity_on_hand})
</option> </option>
{/each} {/each}
@ -165,4 +175,40 @@
.actions { display: flex; gap: 1rem; align-items: center; } .actions { display: flex; gap: 1rem; align-items: center; }
.field-error { color: #8a1f1b; font-size: 0.8rem; } .field-error { color: #8a1f1b; font-size: 0.8rem; }
.part-search { margin-bottom: 0.35rem; } .part-search { margin-bottom: 0.35rem; }
.type-group {
border: none;
padding: 0;
margin: 0;
}
.type-group legend {
padding: 0;
margin-bottom: 0.35rem;
font-weight: 500;
}
.type-buttons {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.type-btn {
flex: 1 1 0;
min-width: 7rem;
padding: 0.85rem 1rem;
font-size: 1rem;
font-weight: 600;
background: #f3f4f7;
color: #2a2f3a;
border: 2px solid #d8dbe3;
border-radius: 6px;
cursor: pointer;
transition: background 0.1s, border-color 0.1s, color 0.1s;
}
.type-btn:hover { background: #e7eaf0; }
.type-btn.active {
background: #006a4e;
border-color: #006a4e;
color: #fff;
}
.type-btn.active:hover { background: #00553e; }
</style> </style>