mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
several shopping fixes
This commit is contained in:
@@ -66,11 +66,11 @@
|
||||
{{ $t('PostponedUntil') }} {{ DateTime.fromJSDate(e.delayUntil).toLocaleString(DateTime.DATETIME_SHORT) }}
|
||||
</v-list-item-subtitle>
|
||||
|
||||
<template #append>
|
||||
<v-btn size="small" color="delete" icon="$delete" v-if="!e.recipeMealplan">
|
||||
<v-icon icon="$delete"></v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<!-- <template #append>-->
|
||||
<!-- <v-btn size="small" color="delete" icon="$delete" v-if="!e.recipeMealplan">-->
|
||||
<!-- <v-icon icon="$delete"></v-icon>-->
|
||||
<!-- </v-btn>-->
|
||||
<!-- </template>-->
|
||||
|
||||
<!-- TODO make properly reactive or delete from the food instance in this component as well | ADD functionality once reactive -->
|
||||
<model-edit-dialog model="ShoppingListEntry" :item="e" @delete="useShoppingStore().entries.delete(e.id!);" v-if="!e.recipeMealplan"></model-edit-dialog>
|
||||
@@ -126,6 +126,10 @@ const isShoppingLineDelayed = computed(() => {
|
||||
return isShoppingListFoodDelayed(shoppingListFood.value)
|
||||
})
|
||||
|
||||
/**
|
||||
* change category of food and update via API
|
||||
* @param category
|
||||
*/
|
||||
function categoryUpdate(category: SupermarketCategory) {
|
||||
const api = new ApiApi()
|
||||
shoppingListFood.value.food.supermarketCategory = category
|
||||
|
||||
@@ -15,23 +15,25 @@
|
||||
<span>
|
||||
<i class="fas fa-check text-warning" v-if="a.checked && !isChecked"></i>
|
||||
<i class="fas fa-hourglass-half text-primary" v-if="a.delayed && !a.checked"></i> <b>
|
||||
{{ a.amount }}
|
||||
<span :class="{'text-decoration-line-through': a.checked}">
|
||||
{{ a.amount }}
|
||||
</span>
|
||||
<span v-if="a.unit">{{ a.unit.name }}</span>
|
||||
</b>
|
||||
</span>
|
||||
<br/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-grow-1 align-self-center">
|
||||
{{ food.name }} <br/>
|
||||
<span v-if="info_row"><small class="text-disabled">{{ info_row }}</small></span>
|
||||
<div class="d-flex flex-column flex-grow-1 align-self-center" :class="{'text-decoration-line-through': isChecked}">
|
||||
{{ shoppingListFood.food.name }} <br/>
|
||||
<span v-if="infoRow"><small class="text-disabled">{{ infoRow }}</small></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #append>
|
||||
<v-btn color="success" @click="useShoppingStore().setEntriesCheckedState(entries, !isChecked, true)"
|
||||
:class="{'btn-success': !isChecked, 'btn-warning': isChecked}" icon="fa-solid fa-check" variant="plain">
|
||||
<v-btn color="success" @click.native.stop="useShoppingStore().setEntriesCheckedState(entries, !isChecked, true);"
|
||||
:class="{'btn-success': !isChecked, 'btn-warning': isChecked}" :icon="actionButtonIcon" variant="plain">
|
||||
</v-btn>
|
||||
<!-- <i class="d-print-none fa-fw fas" :class="{'fa-check': !isChecked , 'fa-cart-plus': isChecked }"></i>-->
|
||||
</template>
|
||||
@@ -63,6 +65,9 @@ const props = defineProps({
|
||||
shoppingListFood: {type: {} as PropType<IShoppingListFood>, required: true},
|
||||
})
|
||||
|
||||
/**
|
||||
* ID of outer container, used by swipe system
|
||||
*/
|
||||
const itemContainerId = computed(() => {
|
||||
let id = 'id_sli_'
|
||||
for (let i in props.entries) {
|
||||
@@ -71,6 +76,10 @@ const itemContainerId = computed(() => {
|
||||
return id
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* tests if all entries of the given food are checked
|
||||
*/
|
||||
const isChecked = computed(() => {
|
||||
for (let i in props.entries) {
|
||||
if (!props.entries[i].checked) {
|
||||
@@ -80,15 +89,24 @@ const isChecked = computed(() => {
|
||||
return true
|
||||
})
|
||||
|
||||
/**
|
||||
* determine if any entry in a given IShoppingListFood is delayed, if so return true
|
||||
*/
|
||||
const isShoppingLineDelayed = computed(() => {
|
||||
return isShoppingListFoodDelayed(props.shoppingListFood)
|
||||
})
|
||||
|
||||
|
||||
const food = computed(() => {
|
||||
return props.entries[Object.keys(props.entries)[0]]['food']
|
||||
/**
|
||||
* style action button depending on if all items are checked or not
|
||||
*/
|
||||
const actionButtonIcon = computed(() => {
|
||||
if (isChecked.value){
|
||||
return 'fa-solid fa-plus'
|
||||
}
|
||||
return 'fa-solid fa-check'
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* calculate the amounts for the given line
|
||||
* can combine 1 to n entries with the same unit
|
||||
@@ -128,7 +146,7 @@ const amounts = computed((): Map<number, ShoppingLineAmount> => {
|
||||
return unitAmounts
|
||||
})
|
||||
|
||||
const info_row = computed(() => {
|
||||
const infoRow = computed(() => {
|
||||
let info_row = []
|
||||
|
||||
let authors = []
|
||||
@@ -170,25 +188,6 @@ const info_row = computed(() => {
|
||||
return info_row.join(' - ')
|
||||
})
|
||||
|
||||
// TODO implement
|
||||
/**
|
||||
* update the food after the category was changed
|
||||
* handle changing category to category ID as a workaround
|
||||
* @param food
|
||||
*/
|
||||
function updateFoodCategory(food: Food) {
|
||||
// if (typeof food.supermarketCategory === "number") { // not the best solution, but as long as generic multiselect does not support caching, I don't want to use a proper model
|
||||
// food.supermarket_category = this.useShoppingListStore().supermarket_categories.filter(sc => sc.id === food.supermarket_category)[0]
|
||||
// }
|
||||
//
|
||||
// let apiClient = new ApiApiFactory()
|
||||
// apiClient.updateFood(food.id, food).then(r => {
|
||||
//
|
||||
// }).catch((err) => {
|
||||
// StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
|
||||
// })
|
||||
}
|
||||
|
||||
/**
|
||||
* set food on_hand status to true and check all associated entries
|
||||
* @param food
|
||||
|
||||
@@ -92,6 +92,28 @@
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="4">
|
||||
<v-card>
|
||||
<v-card-title>Sync Queue Debug</v-card-title>
|
||||
<v-card-text>
|
||||
Length: {{ useShoppingStore().itemCheckSyncQueue.length }} <br/>
|
||||
Has Failed Items: {{ useShoppingStore().hasFailedItems()}}
|
||||
<v-list>
|
||||
<v-list-item v-for="i in useShoppingStore().itemCheckSyncQueue" :key="i">{{ i }}</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col cols="4">
|
||||
<v-card>
|
||||
<v-card-title>Undo Debug</v-card-title>
|
||||
<v-card-text>
|
||||
<v-list>
|
||||
<v-list-item v-for="i in useShoppingStore().undoStack" :key="i">{{ i.type }} {{ i.entries.flatMap(e => e.food.name)}}</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-window-item>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {acceptHMRUpdate, defineStore} from "pinia"
|
||||
import {ApiApi, Food, ShoppingListEntry, Supermarket, SupermarketCategory} from "@/openapi";
|
||||
import {ApiApi, Food, ShoppingListEntry, ShoppingListEntryBulk, Supermarket, SupermarketCategory} from "@/openapi";
|
||||
import {computed, ref} from "vue";
|
||||
import {
|
||||
IShoppingExportEntry,
|
||||
@@ -346,7 +346,6 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
||||
let entryIdList: number[] = []
|
||||
entries.forEach(entry => {
|
||||
entry.checked = checked
|
||||
// TODO used to set updatedAt but does not make sense on client, rethink solution (as above)
|
||||
entryIdList.push(entry.id!)
|
||||
})
|
||||
|
||||
@@ -367,34 +366,30 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
||||
function _replaySyncQueue() {
|
||||
if (navigator.onLine || document.location.href.includes('localhost')) {
|
||||
let api = new ApiApi()
|
||||
let promises = []
|
||||
let promises: Promise<void>[] = []
|
||||
|
||||
for (let i in itemCheckSyncQueue.value) {
|
||||
let entry = itemCheckSyncQueue.value[i]
|
||||
itemCheckSyncQueue.value.forEach((entry, index) => {
|
||||
entry['status'] = ((entry['status'] === 'waiting') ? 'syncing' : 'syncing_failed_before')
|
||||
itemCheckSyncQueue.value[i] = entry
|
||||
|
||||
// TODO set timeout for request (previously was 15000ms) or check that default timeout is similar
|
||||
let p = api.apiShoppingListEntryBulkCreate({shoppingListEntryBulk: entry}, {}).then((r) => {
|
||||
let p = api.apiShoppingListEntryBulkCreate({shoppingListEntryBulk: entry}, {}).then((r) => {
|
||||
entry.ids.forEach(id => {
|
||||
let e = entries.value.get(id)
|
||||
e.updatedAt = r.timestamp
|
||||
entries.value.set(id, e)
|
||||
})
|
||||
delete itemCheckSyncQueue.value[i]
|
||||
itemCheckSyncQueue.value.splice(index,1)
|
||||
}).catch((err) => {
|
||||
if (err.code === "ERR_NETWORK" || err.code === "ECONNABORTED") {
|
||||
entry['status'] = 'waiting_failed_before'
|
||||
itemCheckSyncQueue.value[i] = entry
|
||||
} else {
|
||||
delete itemCheckSyncQueue.value[i]
|
||||
itemCheckSyncQueue.value.splice(index,1)
|
||||
console.error('Failed API call for entry ', entry)
|
||||
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
|
||||
}
|
||||
})
|
||||
promises.push(p)
|
||||
}
|
||||
|
||||
})
|
||||
// TODO verify this all settled works
|
||||
Promise.allSettled(promises).finally(() => {
|
||||
runSyncQueue(500)
|
||||
})
|
||||
@@ -542,6 +537,8 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
||||
currentlyUpdating,
|
||||
getFlatEntries,
|
||||
hasFailedItems,
|
||||
itemCheckSyncQueue,
|
||||
undoStack,
|
||||
refreshFromAPI,
|
||||
autoSync,
|
||||
createObject,
|
||||
|
||||
Reference in New Issue
Block a user