Enforce required label form fields before preview and create

This commit is contained in:
2026-04-06 17:54:45 +02:00
parent d4205f93b8
commit 96b263c899
+51 -5
View File
@@ -45,7 +45,7 @@ export function renderLabelCreatePage() {
<div class="col-12 col-xl-7"> <div class="col-12 col-xl-7">
<div class="card border-0 shadow-sm"> <div class="card border-0 shadow-sm">
<div class="card-body p-4"> <div class="card-body p-4">
<form class="vstack gap-3" @submit.prevent="create()" autocomplete="off"> <form class="vstack gap-3" @submit.prevent="create()" autocomplete="off" x-ref="labelForm">
<div class="position-relative search-field-with-clear"> <div class="position-relative search-field-with-clear">
<label class="form-label">Search item definitions</label> <label class="form-label">Search item definitions</label>
<input class="form-control pe-5" type="text" x-model="form.search" @input="onSearchInput()" placeholder="Search by item name" autocomplete="off" /> <input class="form-control pe-5" type="text" x-model="form.search" @input="onSearchInput()" placeholder="Search by item name" autocomplete="off" />
@@ -98,8 +98,8 @@ export function renderLabelCreatePage() {
</button> </button>
</div> </div>
<div class="col-12 col-md-4"> <div class="col-12 col-md-4">
<label class="form-label">Stock type</label> <label class="form-label">Stock type <span class="text-danger">*</span></label>
<select class="form-select" x-model="form.stockType" x-ref="stockTypeSelect" autocomplete="off"> <select class="form-select" x-model="form.stockType" x-ref="stockTypeSelect" autocomplete="off" required>
<template x-for="option in stockTypeOptions" :key="option.value"> <template x-for="option in stockTypeOptions" :key="option.value">
<option :value="option.value" x-text="option.label"></option> <option :value="option.value" x-text="option.label"></option>
</template> </template>
@@ -232,6 +232,7 @@ export function renderLabelCreatePage() {
class="form-control pe-5" class="form-control pe-5"
type="text" type="text"
x-model="locationSearch" x-model="locationSearch"
x-ref="locationInput"
@input="onLocationInput()" @input="onLocationInput()"
@keydown.escape="locationPickerOpen = false" @keydown.escape="locationPickerOpen = false"
@click="openLocationPicker()" @click="openLocationPicker()"
@@ -284,8 +285,8 @@ export function renderLabelCreatePage() {
<div class="form-text" x-show="selectedLocationPath" x-text="selectedLocationPath"></div> <div class="form-text" x-show="selectedLocationPath" x-text="selectedLocationPath"></div>
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<label class="form-label">Production date</label> <label class="form-label">Production date <span class="text-danger">*</span></label>
<input class="form-control" type="date" x-model="form.productionDate" /> <input class="form-control" type="date" x-model="form.productionDate" required />
</div> </div>
<div class="col-12 col-md-6 grouped-field-with-clear"> <div class="col-12 col-md-6 grouped-field-with-clear">
<div class="grouped-field-footer mb-1"> <div class="grouped-field-footer mb-1">
@@ -647,12 +648,14 @@ export function labelCreatePageData(store) {
this.form.locationId = String(location.id); this.form.locationId = String(location.id);
this.locationSearch = location.name; this.locationSearch = location.name;
this.locationPickerOpen = false; this.locationPickerOpen = false;
this.syncLocationValidity();
this.persistDraft(); this.persistDraft();
}, },
clearLocation() { clearLocation() {
this.form.locationId = ''; this.form.locationId = '';
this.locationSearch = ''; this.locationSearch = '';
this.locationPickerOpen = true; this.locationPickerOpen = true;
this.syncLocationValidity();
this.persistDraft(); this.persistDraft();
}, },
openLocationPicker() { openLocationPicker() {
@@ -669,6 +672,7 @@ export function labelCreatePageData(store) {
if (this.selectedLocation && this.locationSearch !== this.selectedLocation.name) { if (this.selectedLocation && this.locationSearch !== this.selectedLocation.name) {
this.form.locationId = ''; this.form.locationId = '';
} }
this.syncLocationValidity();
}, },
handleLocationFocusOut(event) { handleLocationFocusOut(event) {
const nextTarget = event.relatedTarget; const nextTarget = event.relatedTarget;
@@ -721,6 +725,7 @@ export function labelCreatePageData(store) {
); );
this.locationSearch = selected ? selected.name : ''; this.locationSearch = selected ? selected.name : '';
this.syncLocationValidity();
}, },
applyItemLocation(locationUuidB64) { applyItemLocation(locationUuidB64) {
if (!locationUuidB64) { if (!locationUuidB64) {
@@ -737,6 +742,34 @@ export function labelCreatePageData(store) {
this.form.locationId = String(location.id); this.form.locationId = String(location.id);
this.locationSearch = location.name; this.locationSearch = location.name;
this.syncLocationValidity();
},
syncLocationValidity() {
const input = this.$refs.locationInput;
if (!input) {
return;
}
if (!this.locationSearch.trim()) {
input.setCustomValidity('Please select a storage location.');
return;
}
if (!this.form.locationId) {
input.setCustomValidity('Please choose a location from the list.');
return;
}
input.setCustomValidity('');
},
validateBeforeSubmit() {
this.syncLocationValidity();
const form = this.$refs.labelForm;
if (!form) {
return true;
}
return form.reportValidity();
}, },
syncExpireDateFromDays() { syncExpireDateFromDays() {
if (this.form.expireDays === '') { if (this.form.expireDays === '') {
@@ -826,6 +859,14 @@ export function labelCreatePageData(store) {
}; };
}, },
async preview() { async preview() {
this.submitError = '';
this.fieldErrors = {};
if (!this.validateBeforeSubmit()) {
this.previewState.error = 'Please fill out the required fields before previewing the label.';
return;
}
await runAsyncState(this.previewState, async () => { await runAsyncState(this.previewState, async () => {
this.successMessage = ''; this.successMessage = '';
const result = await previewLabel(store, this.buildPayload()); const result = await previewLabel(store, this.buildPayload());
@@ -840,6 +881,11 @@ export function labelCreatePageData(store) {
this.submitError = ''; this.submitError = '';
this.fieldErrors = {}; this.fieldErrors = {};
if (!this.validateBeforeSubmit()) {
this.submitError = 'Please fill out the required fields before creating the stock entry.';
return;
}
await runAsyncState(this.createState, async () => { await runAsyncState(this.createState, async () => {
try { try {
const entry = await createStockEntry(store, this.buildPayload()); const entry = await createStockEntry(store, this.buildPayload());