Add scanner utility, modal, and stock scan page implementation
This commit is contained in:
@@ -3,8 +3,8 @@ import {
|
||||
listGroupedStockEntries,
|
||||
listKitchenChanges,
|
||||
listStockEntries,
|
||||
markStockGone,
|
||||
updateStockItem,
|
||||
useStockItem,
|
||||
} from '../../api/stock.js';
|
||||
import { fetchLocations } from '../../api/locations.js';
|
||||
import { STORAGE_KEYS } from '../../app/config.js';
|
||||
@@ -578,7 +578,8 @@ export function renderStockListPage() {
|
||||
<div class="quick-edit-stack">
|
||||
<template x-if="entry.stock_type === 'binary'">
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="updateBinary(entry, 'gone')">Mark gone</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'consumed')">Mark used</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'spoiled')">Spoilt</button>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="entry.stock_type === 'descriptive'">
|
||||
@@ -589,14 +590,16 @@ export function renderStockListPage() {
|
||||
</template>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-primary" type="button" @click="saveLevel(entry)">Save</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry)">Mark gone</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'consumed')">Mark used</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'spoiled')">Spoilt</button>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="entry.stock_type === 'measured'">
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center">
|
||||
<input class="form-control form-control-sm quick-number" type="number" step="0.01" min="0" x-model="editForms[entry.id].quantity" />
|
||||
<button class="btn btn-sm btn-primary" type="button" @click="saveQuantity(entry)">Save qty</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry)">Mark gone</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'consumed')">Mark used</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'spoiled')">Spoilt</button>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="editErrors[entry.id]">
|
||||
@@ -660,7 +663,8 @@ export function renderStockListPage() {
|
||||
<div class="quick-edit-stack">
|
||||
<template x-if="entry.stock_type === 'binary'">
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="updateBinary(entry, 'gone')">Mark gone</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'consumed')">Mark used</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'spoiled')">Spoilt</button>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="entry.stock_type === 'descriptive'">
|
||||
@@ -672,7 +676,8 @@ export function renderStockListPage() {
|
||||
</select>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-sm btn-primary" type="button" @click="saveLevel(entry)">Save stock level</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry)">Mark gone</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'consumed')">Mark used</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'spoiled')">Spoilt</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -681,7 +686,8 @@ export function renderStockListPage() {
|
||||
<input class="form-control form-control-sm" type="number" step="0.01" min="0" x-model="editForms[entry.id].quantity" />
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-sm btn-primary" type="button" @click="saveQuantity(entry)">Save quantity</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry)">Mark gone</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'consumed')">Mark used</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" :disabled="isItemRefreshing(entry)" @click="markGone(entry, 'spoiled')">Spoilt</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -796,7 +802,8 @@ export function renderStockListPage() {
|
||||
>
|
||||
Details
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-danger grouped-stock-mark-gone" type="button" :disabled="isItemRefreshing(item)" @click="markGoneFromGroup(item, group)">Mark gone</button>
|
||||
<button class="btn btn-sm btn-outline-danger grouped-stock-mark-gone" type="button" :disabled="isItemRefreshing(item)" @click="markGoneFromGroup(item, group, 'consumed')">Mark used</button>
|
||||
<button class="btn btn-sm btn-outline-danger grouped-stock-mark-gone" type="button" :disabled="isItemRefreshing(item)" @click="markGoneFromGroup(item, group, 'spoiled')">Spoilt</button>
|
||||
<div class="small text-body-secondary stock-item-refresh-indicator" x-show="isItemRefreshing(item)">
|
||||
Refreshing...
|
||||
</div>
|
||||
@@ -2202,12 +2209,12 @@ export function stockListPageData(store) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.useEntry(entry);
|
||||
await this.useEntry(entry, 'consumed');
|
||||
},
|
||||
async saveLevel(entry) {
|
||||
const level = this.editForms[entry.id]?.level || 'plenty';
|
||||
if (level === 'gone') {
|
||||
await this.useEntry(entry);
|
||||
await this.useEntry(entry, 'consumed');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2234,14 +2241,14 @@ export function stockListPageData(store) {
|
||||
{ quantity },
|
||||
);
|
||||
},
|
||||
async markGone(entry) {
|
||||
async markGone(entry, reason = 'consumed') {
|
||||
if (this.isItemRefreshing(entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.useEntry(entry);
|
||||
await this.useEntry(entry, reason);
|
||||
},
|
||||
async markGoneFromGroup(item, group) {
|
||||
async markGoneFromGroup(item, group, reason = 'consumed') {
|
||||
if (this.isItemRefreshing(item)) {
|
||||
return;
|
||||
}
|
||||
@@ -2249,8 +2256,9 @@ export function stockListPageData(store) {
|
||||
this.editErrors[item.id] = '';
|
||||
|
||||
try {
|
||||
const result = await useStockItem(store, item.uuid_b64);
|
||||
const result = await markStockGone(store, item.uuid_b64, reason);
|
||||
const alreadyGone = result.status === 'already_gone';
|
||||
const actionLabel = reason === 'spoiled' ? 'marked spoilt' : 'marked used';
|
||||
this.removeGroupedItem(group.id, item.id);
|
||||
this.removeEntryLocally(item.id);
|
||||
delete this.editForms[item.id];
|
||||
@@ -2259,11 +2267,11 @@ export function stockListPageData(store) {
|
||||
type: alreadyGone ? 'info' : 'success',
|
||||
message: alreadyGone
|
||||
? `${item.name} was already out of stock and removed from the group.`
|
||||
: `${item.name} was marked gone and removed from the group.`,
|
||||
: `${item.name} was ${actionLabel} and removed from the group.`,
|
||||
});
|
||||
this.loadGroupedEntries({ expanded: 0, background: true }).catch(() => {});
|
||||
} catch (error) {
|
||||
this.editErrors[item.id] = error.message || 'Mark gone failed.';
|
||||
this.editErrors[item.id] = error.message || 'Removal failed.';
|
||||
}
|
||||
},
|
||||
async saveEntryUpdate(entry, payload, localPatch) {
|
||||
@@ -2281,7 +2289,7 @@ export function stockListPageData(store) {
|
||||
this.editErrors[entry.id] = error.message || 'Update failed.';
|
||||
}
|
||||
},
|
||||
async useEntry(entry) {
|
||||
async useEntry(entry, reason = 'consumed') {
|
||||
if (this.isItemRefreshing(entry)) {
|
||||
return;
|
||||
}
|
||||
@@ -2289,8 +2297,9 @@ export function stockListPageData(store) {
|
||||
this.editErrors[entry.id] = '';
|
||||
|
||||
try {
|
||||
const result = await useStockItem(store, entry.uuid_b64);
|
||||
const result = await markStockGone(store, entry.uuid_b64, reason);
|
||||
const alreadyGone = result.status === 'already_gone';
|
||||
const actionLabel = reason === 'spoiled' ? 'marked spoilt' : 'marked used';
|
||||
this.removeEntryLocally(entry.id);
|
||||
delete this.editForms[entry.id];
|
||||
delete this.editErrors[entry.id];
|
||||
@@ -2298,11 +2307,11 @@ export function stockListPageData(store) {
|
||||
type: alreadyGone ? 'info' : 'success',
|
||||
message: alreadyGone
|
||||
? `${entry.name} was already out of stock and removed from the list.`
|
||||
: `${entry.name} was marked gone and removed from the list.`,
|
||||
: `${entry.name} was ${actionLabel} and removed from the list.`,
|
||||
});
|
||||
this.refreshLoadedViewsInBackground().catch(() => {});
|
||||
} catch (error) {
|
||||
this.editErrors[entry.id] = error.message || 'Mark gone failed.';
|
||||
this.editErrors[entry.id] = error.message || 'Removal failed.';
|
||||
}
|
||||
},
|
||||
removeEntryLocally(entryId) {
|
||||
|
||||
Reference in New Issue
Block a user