Add preset expiration day picker to label form
This commit is contained in:
@@ -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 = '';
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user