Add preset expiration day picker to label form

This commit is contained in:
2026-04-06 17:47:02 +02:00
parent 218647b2cb
commit d4205f93b8
2 changed files with 125 additions and 9 deletions
+79 -4
View File
@@ -22,6 +22,7 @@ const STOCK_LEVEL_OPTIONS = [
]; ];
const QUANTITY_UNIT_OPTIONS = ['g', 'ml', 'pc']; const QUANTITY_UNIT_OPTIONS = ['g', 'ml', 'pc'];
const EXPIRATION_DAY_OPTIONS = ['3', '5', '8', '10', '15', '20', '25', '30', '45', '60', '90', '120', '150', '180'];
export function renderLabelCreatePage() { export function renderLabelCreatePage() {
return ` return `
@@ -300,15 +301,47 @@ export function renderLabelCreatePage() {
</div> </div>
<div class="row g-2"> <div class="row g-2">
<div class="col-5"> <div class="col-5">
<div
class="position-relative"
x-ref="expireDaysPicker"
@focusin="expireDaysPickerOpen = true"
@focusout="handleExpireDaysFocusOut($event)"
>
<input <input
class="form-control" class="form-control"
type="number" type="text"
min="0" inputmode="numeric"
step="1"
x-model="form.expireDays" x-model="form.expireDays"
@input="syncExpireDateFromDays()" @input="onExpireDaysInput()"
@click="openExpireDaysPicker()"
@keydown.escape="expireDaysPickerOpen = false"
placeholder="30" placeholder="30"
autocomplete="off"
/> />
<template x-if="expireDaysPickerOpen">
<div class="shadow-sm position-absolute start-0 end-0 z-3 mt-1 quantity-unit-picker expiration-days-picker">
<template x-if="filteredExpireDayOptions.length">
<div class="expiration-days-grid">
<template x-for="days in filteredExpireDayOptions" :key="days">
<button
class="btn btn-outline-secondary btn-sm expiration-days-option"
type="button"
@click="pickExpireDays(days)"
:class="{ 'active': form.expireDays === days }"
>
<span class="fw-semibold" x-text="days"></span>
</button>
</template>
</div>
</template>
<template x-if="!filteredExpireDayOptions.length">
<div class="text-body-secondary small p-3">
No matching day values found.
</div>
</template>
</div>
</template>
</div>
</div> </div>
<div class="col-7"> <div class="col-7">
<input <input
@@ -466,11 +499,13 @@ export function labelCreatePageData(store) {
stockTypeOptions: STOCK_TYPE_OPTIONS, stockTypeOptions: STOCK_TYPE_OPTIONS,
stockLevelOptions: STOCK_LEVEL_OPTIONS, stockLevelOptions: STOCK_LEVEL_OPTIONS,
quantityUnitOptions: QUANTITY_UNIT_OPTIONS, quantityUnitOptions: QUANTITY_UNIT_OPTIONS,
expirationDayOptions: EXPIRATION_DAY_OPTIONS,
suggestions: [], suggestions: [],
locations: [], locations: [],
locationSearch: '', locationSearch: '',
locationPickerOpen: false, locationPickerOpen: false,
quantityUnitPickerOpen: false, quantityUnitPickerOpen: false,
expireDaysPickerOpen: false,
previewUrl: '', previewUrl: '',
successMessage: '', successMessage: '',
submitError: '', submitError: '',
@@ -580,6 +615,26 @@ export function labelCreatePageData(store) {
return this.quantityUnitOptions; return this.quantityUnitOptions;
}, },
get filteredExpireDayOptions() {
const query = this.form.expireDays.trim();
if (!query) {
return this.expirationDayOptions;
}
if (this.expirationDayOptions.includes(query)) {
return this.expirationDayOptions;
}
const matches = this.expirationDayOptions.filter((days) =>
days.includes(query),
);
if (matches.length) {
return matches;
}
return this.expirationDayOptions;
},
get selectedLocation() { get selectedLocation() {
return this.locations.find( return this.locations.find(
(location) => String(location.id) === String(this.form.locationId), (location) => String(location.id) === String(this.form.locationId),
@@ -606,6 +661,9 @@ export function labelCreatePageData(store) {
openQuantityUnitPicker() { openQuantityUnitPicker() {
this.quantityUnitPickerOpen = true; this.quantityUnitPickerOpen = true;
}, },
openExpireDaysPicker() {
this.expireDaysPickerOpen = true;
},
onLocationInput() { onLocationInput() {
this.locationPickerOpen = true; this.locationPickerOpen = true;
if (this.selectedLocation && this.locationSearch !== this.selectedLocation.name) { if (this.selectedLocation && this.locationSearch !== this.selectedLocation.name) {
@@ -631,10 +689,27 @@ export function labelCreatePageData(store) {
this.quantityUnitPickerOpen = false; this.quantityUnitPickerOpen = false;
}, },
onExpireDaysInput() {
this.expireDaysPickerOpen = true;
this.syncExpireDateFromDays();
},
handleExpireDaysFocusOut(event) {
const nextTarget = event.relatedTarget;
if (nextTarget && this.$refs.expireDaysPicker?.contains(nextTarget)) {
return;
}
this.expireDaysPickerOpen = false;
},
pickQuantityUnit(unit) { pickQuantityUnit(unit) {
this.form.uom = unit; this.form.uom = unit;
this.quantityUnitPickerOpen = false; this.quantityUnitPickerOpen = false;
}, },
pickExpireDays(days) {
this.form.expireDays = days;
this.expireDaysPickerOpen = false;
this.syncExpireDateFromDays();
},
syncLocationSelection() { syncLocationSelection() {
if (!this.form.locationId) { if (!this.form.locationId) {
this.locationSearch = ''; this.locationSearch = '';
+41
View File
@@ -182,6 +182,47 @@ body {
.quantity-unit-picker { .quantity-unit-picker {
z-index: 4; z-index: 4;
border-radius: 0.9rem;
border: 1px solid var(--lonc-border);
background: rgba(255, 255, 255, 0.96);
}
.expiration-days-picker {
padding: 0.75rem;
min-width: 18rem;
width: max-content;
max-width: min(22rem, calc(100vw - 2rem));
}
.expiration-days-grid {
display: grid;
grid-template-columns: repeat(4, minmax(3.25rem, 1fr));
gap: 0.5rem;
}
.expiration-days-option {
padding: 0.55rem 0.4rem;
border-radius: 0.7rem;
min-height: 2.75rem;
white-space: nowrap;
line-height: 1.1;
}
.expiration-days-option.active {
color: #fff;
background: var(--lonc-primary);
border-color: var(--lonc-primary);
}
@media (max-width: 575.98px) {
.expiration-days-picker {
min-width: 0;
width: min(100vw - 2rem, 18rem);
}
.expiration-days-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
} }
.location-level-badge { .location-level-badge {