new datastructure

This commit is contained in:
vabene1111 2023-12-27 11:34:55 +01:00
parent b0c561661b
commit d3376b33d8
5 changed files with 87 additions and 50 deletions

View File

@ -1,6 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="vaben">
<words>
<w>mealplan</w>
<w>pinia</w>
<w>selfhosted</w>
<w>unapplied</w>

View File

@ -53,7 +53,7 @@
<!-- shopping list table -->
<b-row v-for="c in shopping_list_store.category_food_entries" v-bind:key="c.id" class="pr-4 pl-4">
<b-row v-for="c in shopping_list_store.get_entries_by_group" v-bind:key="c.id" class="pr-4 pl-4">
<b-col cols="12">
<b-button-group class="w-100 mt-1">
<b-button variant="light" block class="btn btn-block text-left">
@ -750,7 +750,7 @@ export default {
handler() {
this.$cookies.set(SETTINGS_COOKIE_NAME, {ui: this.ui, settings: {entrymode: this.entrymode}}, "100y")
if (this.entrymode) {
document.getElementById('shoppinglist').scrollTop = 0
//document.getElementById('shoppinglist').scrollTop = 0
this.$nextTick(function () {
this.setFocus()
})

View File

@ -3,21 +3,18 @@
<div id="app">
<div>
<b-button-group class="w-100 mb-1" >
<b-button variant="dark" block class="btn btn-block btn-sm text-left " >100 g Möhren Test <br/><small class="text-muted">Info</small></b-button>
<b-button variant="success"><i class="fas fa-check"></i></b-button>
</b-button-group>
<br/>
<b-button-group class="w-100">
<b-button variant="dark" block class="btn btn-block text-left" >150 ml Heißwassermöhrenbrühe<br/><small class="text-muted">Info</small></b-button>
<b-button variant="success"><i class="fas fa-check"></i></b-button>
</b-button-group>
<br/>
<br/>
<br/>
<generic-multiselect
:model="Models.SHOPPING_CATEGORY"
:multiple="false"
></generic-multiselect>
<generic-multiselect
:model="Models.SHOPPING_CATEGORY"
:multiple="false"
></generic-multiselect>
<generic-multiselect
:model="Models.SHOPPING_CATEGORY"
:multiple="false"
></generic-multiselect>
----
<markdown-editor-component></markdown-editor-component>
@ -48,7 +45,7 @@ Vue.use(BootstrapVue)
export default {
name: "TestView",
mixins: [ApiMixin],
components: {MarkdownEditorComponent},
components: {GenericMultiselect, MarkdownEditorComponent},
computed: {},
data() {
return {}

View File

@ -2,7 +2,7 @@
<div id="shopping_line_item">
<b-button-group class="w-100" v-if="useShoppingListStore().show_checked_entries || !is_checked">
<b-button :class="{'btn-dark': !is_checked, 'btn-success': is_checked}" block class="btn btn-block text-left" @click="detail_modal_visible = true">
<b-button :class="{'btn-dark': (!is_checked && !is_delayed), 'btn-success': is_checked, 'btn-warning': is_delayed}" block class="btn btn-block text-left" @click="detail_modal_visible = true">
<div class="d-flex ">
<div class="d-flex flex-column pr-2" v-if="Object.keys(amounts).length> 0">
<span v-for="a in amounts" v-bind:key="a.id">{{ a.amount }} {{ a.unit }}<br/></span>
@ -17,7 +17,7 @@
</b-button>
<b-button variant="success" @click="useShoppingListStore().setFoodCheckedState(food, !is_checked)" :class="{'btn-success': !is_checked, 'btn-warning': is_checked}">
<b-button variant="success" @click="useShoppingListStore().setEntriesCheckedState(entries, !is_checked)" :class="{'btn-success': !is_checked, 'btn-warning': is_checked}">
<i class="fas" :class="{'fa-check': !is_checked, 'fa-times': is_checked}"></i>
</b-button>
</b-button-group>
@ -31,7 +31,7 @@
</template>
<template #default>
<h6 class="mt-2">Actions</h6> <!-- TODO localize -->
<h6 class="mt-2">{{ $t('Quick actions')}}</h6>
<b-form-select
class="form-control mb-2"
:options="useShoppingListStore().supermarket_categories"
@ -39,11 +39,11 @@
value-field="id"
v-model="food.supermarket_category"
@change="detail_modal_visible = false; updateFoodCategory(food)"
></b-form-select> <!-- TODO change to lookup input or something else that works with dicts -->
></b-form-select>
<b-button variant="success" block @click="detail_modal_visible = false;"> {{ $t("Edit_Food") }}</b-button> <!-- TODO implement -->
<b-button variant="info" block @click="detail_modal_visible = false;useShoppingListStore().delayFood(food)">{{ $t('Delay') }}</b-button>
<b-button variant="info" block @click="detail_modal_visible = false;useShoppingListStore().delayEntries(entries)">{{ $t('Delay') }}</b-button>
<h6 class="mt-2">{{ $t('Entries') }}</h6>
@ -118,6 +118,14 @@ export default {
}
return true
},
is_delayed: function () {
for (let i in this.entries) {
if ( Date.parse(this.entries[i].delay_until) > new Date(Date.now())) {
return true
}
}
return false
},
food: function () {
return this.entries[Object.keys(this.entries)[0]]['food']
},
@ -210,7 +218,7 @@ export default {
updateFoodCategory: function (food) {
if (typeof food.supermarket_category === "number"){
if (typeof food.supermarket_category === "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]
}

View File

@ -2,15 +2,18 @@ import {ApiApiFactory} from "@/utils/openapi/api"
import {StandardToasts} from "@/utils/utils"
import {defineStore} from "pinia"
import Vue from "vue"
import _ from 'lodash';
const _STORE_ID = "shopping_list_store"
const _LOCAL_STORAGE_KEY = "SHOPPING_LIST_CLIENT_SETTINGS"
/*
* test store to play around with pinia and see if it can work for my usecases
* dont trust that all shopping list entries are in store as there is no cache validation logic, its just a shared data holder
* test store to play around with pinia and see if it can work for my use cases
* don't trust that all shopping list entries are in store as there is no cache validation logic, its just a shared data holder
* */
export const useShoppingListStore = defineStore(_STORE_ID, {
state: () => ({
entries: {},
category_food_entries: {},
supermarket_categories: [],
@ -18,9 +21,25 @@ export const useShoppingListStore = defineStore(_STORE_ID, {
currently_updating: false,
settings: null,
GROUP_CATEGORY: 'food.supermarket_category.name',
GROUP_CREATED_BY: 'created_by.display_name',
GROUP_RECIPE: 'recipe_mealplan.recipe_name',
GROUP_MEALPLAN: 'recipe_mealplan.mealplan', //TODO give this some name from the API
selected_group: 'food.supermarket_category.name',
}),
getters: {
get_entries_by_group: function () {
let structure = {}
for (let i in this.entries) {
structure = this.updateEntryInStructure(structure, this.entries[i], this.selected_group)
}
return structure
},
client_settings: function () {
if (this.settings === null) {
this.settings = this.loadClientSettings()
@ -41,7 +60,7 @@ export const useShoppingListStore = defineStore(_STORE_ID, {
let apiClient = new ApiApiFactory()
apiClient.listShoppingListEntrys().then((r) => {
r.data.forEach((e) => {
this.updateEntryInStructure(e)
Vue.set(this.entries, e.id, e)
})
this.currently_updating = false
}).catch((err) => {
@ -61,7 +80,7 @@ export const useShoppingListStore = defineStore(_STORE_ID, {
// TODO shared handled in backend?
return apiClient.createShoppingListEntry(object).then((r) => {
this.updateEntryInStructure(r.data)
Vue.set(this.entries, r.data.id, r.data)
}).catch((err) => {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
})
@ -69,7 +88,7 @@ export const useShoppingListStore = defineStore(_STORE_ID, {
updateObject(object) {
let apiClient = new ApiApiFactory()
return apiClient.updateShoppingListEntry(object.id, object).then((r) => {
this.updateEntryInStructure(r.data)
Vue.set(this.entries, r.data.id, r.data)
}).catch((err) => {
StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
})
@ -114,28 +133,41 @@ export const useShoppingListStore = defineStore(_STORE_ID, {
}
return -1
},
updateEntryInStructure(entry) {
let category = this.getFoodCategory(entry.food)
if (!(category in this.category_food_entries)) {
Vue.set(this.category_food_entries, category, {'id': category, 'name': entry.food.supermarket_category.name, 'foods': {}})
/**
* function to set entry to its proper place in the data structure to perform grouping
* @param {{}} structure datastructure
* @param {*} entry entry to place
* @param {*} group group to place entry into (must be of ShoppingListStore.GROUP_XXX/dot notation of entry property)
* @returns updated datastructure including entry
*/
updateEntryInStructure(structure, entry, group) {
let grouping_key = _.get(entry, group, -1)
// todo handele parent
if (grouping_key === undefined || grouping_key === null) {
grouping_key = -1
}
if (!(entry.food.id in this.category_food_entries[category]['foods'])) {
Vue.set(this.category_food_entries[category]['foods'], entry.food.id, {'id': entry.food.id, 'name': entry.food.name, 'entries': {}})
}
Vue.set(this.category_food_entries[category]['foods'][entry.food.id]['entries'], entry.id, entry)
},
setFoodCheckedState(food, checked) {
/**
* function to handle user checking or unchecking a food
*/
let entries = this.category_food_entries[this.getFoodCategory(food)]['foods'][food.id]['entries']
if (!(grouping_key in structure)) {
Vue.set(structure, grouping_key, {'name': grouping_key, 'foods': {}})
}
if (!(entry.food.id in structure[grouping_key]['foods'])) {
Vue.set(structure[grouping_key]['foods'], entry.food.id, {'id': entry.food.id, 'name': entry.food.name, 'entries': {}})
}
Vue.set(structure[grouping_key]['foods'][entry.food.id]['entries'], entry.id, entry)
return structure
},
/**
* function to handle user checking or unchecking a set of entries
* @param {{}} entries set of entries
* @param checked boolean to set checked state of entry to
*/
setEntriesCheckedState(entries, checked) {
for (let i in entries) {
entries[i].checked = checked
this.updateObject(entries[i])
this.entries[i].checked = checked
this.updateObject(this.entries[i])
}
},
delayFood(food) {
delayEntries(entries) {
/**
* function to handle user "delaying" shopping entry
* takes a food object as an argument and delays all entries associated with the food
@ -143,12 +175,11 @@ export const useShoppingListStore = defineStore(_STORE_ID, {
let delay = 4 //TODO get delay from settings
let delay_date = new Date(Date.now() + delay * (60 * 60 * 1000))
let entries = this.category_food_entries[this.getFoodCategory(food)]['foods'][food.id]['entries']
for (let i in entries) {
entries[i].delayed_until = delay_date
this.updateObject(entries[i])
console.log('DELAYING ', i, ' until ', delay_date)
this.entries[i].delay_until = delay_date
this.updateObject(this.entries[i])
}
},
deleteFood(food) {
/**