mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
meal plan store and start page widget
This commit is contained in:
@@ -1,6 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container>
|
<v-container>
|
||||||
|
|
||||||
|
<v-row justify="space-between">
|
||||||
|
<v-col>
|
||||||
|
<h2><i class="fas fa-calendar-week fa-fw"></i> Meal Plan</h2>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col v-for="day in meal_plan_grid">
|
||||||
|
<v-list density="compact">
|
||||||
|
<v-list-item>
|
||||||
|
{{ day.date_label }}
|
||||||
|
</v-list-item>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-list-item v-for="p in day.plan_entries">
|
||||||
|
<template #prepend>
|
||||||
|
<v-avatar :image="p.recipe.image" v-if="p.recipe?.image"></v-avatar>
|
||||||
|
<v-avatar image="../../assets/recipe_no_image.svg" v-else></v-avatar>
|
||||||
|
</template>
|
||||||
|
<v-list-item-title>
|
||||||
|
<span v-if="p.recipe">{{ p.recipe.name }}</span>
|
||||||
|
<span v-else>{{ p.title }}</span>
|
||||||
|
</v-list-item-title>
|
||||||
|
<v-list-item-subtitle>
|
||||||
|
{{ p.mealType.name }}
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
</v-list>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-window show-arrows>
|
||||||
|
<v-window-item>
|
||||||
|
<v-row>
|
||||||
|
|
||||||
|
</v-row>
|
||||||
|
</v-window-item>
|
||||||
|
</v-window>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
|
||||||
<!--TODO ideas for "start page": new recipes, meal plan, "last year/month/cooked long ago", high rated, random keyword -->
|
<!--TODO ideas for "start page": new recipes, meal plan, "last year/month/cooked long ago", high rated, random keyword -->
|
||||||
<horizontal-recipe-scroller title="New Recipes" :skeletons="4" :recipes="new_recipes" icon="fas fa-calendar-alt"></horizontal-recipe-scroller>
|
<horizontal-recipe-scroller title="New Recipes" :skeletons="4" :recipes="new_recipes" icon="fas fa-calendar-alt"></horizontal-recipe-scroller>
|
||||||
<horizontal-recipe-scroller title="Top Rated" :skeletons="2" :recipes="high_rated_recipes" icon="fas fa-star"></horizontal-recipe-scroller>
|
<horizontal-recipe-scroller title="Top Rated" :skeletons="2" :recipes="high_rated_recipes" icon="fas fa-star"></horizontal-recipe-scroller>
|
||||||
@@ -18,12 +63,34 @@ import RecipeCardComponent from "@/components/display/RecipeCard.vue";
|
|||||||
import GlobalSearchDialog from "@/components/inputs/GlobalSearchDialog.vue";
|
import GlobalSearchDialog from "@/components/inputs/GlobalSearchDialog.vue";
|
||||||
import RecipeCard from "@/components/display/RecipeCard.vue";
|
import RecipeCard from "@/components/display/RecipeCard.vue";
|
||||||
import HorizontalRecipeScroller from "@/components/display/HorizontalRecipeScroller.vue";
|
import HorizontalRecipeScroller from "@/components/display/HorizontalRecipeScroller.vue";
|
||||||
|
import {DateTime} from "luxon";
|
||||||
|
import {useMealPlanStore} from "@/stores/MealPlanStore";
|
||||||
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "RecipeSearchPage",
|
name: "RecipeSearchPage",
|
||||||
components: {HorizontalRecipeScroller, RecipeCard, GlobalSearchDialog, RecipeCardComponent, KeywordsComponent},
|
components: {HorizontalRecipeScroller, RecipeCard, GlobalSearchDialog, RecipeCardComponent, KeywordsComponent},
|
||||||
computed: {},
|
computed: {
|
||||||
|
|
||||||
|
meal_plan_grid: function () {
|
||||||
|
let grid = []
|
||||||
|
|
||||||
|
if (useMealPlanStore().plan_list.length > 0) {
|
||||||
|
console.log('found plans')
|
||||||
|
for (const x of Array(4).keys()) {
|
||||||
|
let grid_day_date = DateTime.now().plus({days: x})
|
||||||
|
console.log('going trough days ', x, grid_day_date)
|
||||||
|
grid.push({
|
||||||
|
date: grid_day_date,
|
||||||
|
create_default_date: grid_day_date.toISODate(), // improve meal plan edit modal to do formatting itself and accept dates
|
||||||
|
date_label: grid_day_date.toLocaleString(DateTime.DATE_MED),
|
||||||
|
plan_entries: useMealPlanStore().plan_list.filter(m => (DateTime.fromJSDate(m.fromDate) <= grid_day_date && DateTime.fromJSDate((m.toDate != undefined) ? m.toDate : m.fromDate) >= grid_day_date)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grid
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
recipes: [] as Recipe[],
|
recipes: [] as Recipe[],
|
||||||
@@ -37,6 +104,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
const api = new ApiApi()
|
const api = new ApiApi()
|
||||||
|
|
||||||
|
useMealPlanStore().refreshFromAPI(DateTime.now().toJSDate(), DateTime.now().plus({days: 7}).toJSDate())
|
||||||
|
|
||||||
api.apiRecipeList({_new: 'true', pageSize: 16}).then(r => {
|
api.apiRecipeList({_new: 'true', pageSize: 16}).then(r => {
|
||||||
if (r.results != undefined) { // TODO openapi generator makes arrays nullable for some reason
|
if (r.results != undefined) { // TODO openapi generator makes arrays nullable for some reason
|
||||||
this.new_recipes = r.results
|
this.new_recipes = r.results
|
||||||
|
|||||||
117
vue3/src/stores/MealPlanStore.ts
Normal file
117
vue3/src/stores/MealPlanStore.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import {defineStore} from "pinia"
|
||||||
|
import {ApiApi, MealPlan} from "@/openapi";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {DateTime} from "luxon";
|
||||||
|
|
||||||
|
|
||||||
|
const _STORE_ID = "meal_plan_store"
|
||||||
|
const _LOCAL_STORAGE_KEY = "MEAL_PLAN_CLIENT_SETTINGS"
|
||||||
|
/*
|
||||||
|
* test store to play around with pinia and see if it can work for my usecases
|
||||||
|
* dont trust that all mealplans are in store as there is no cache validation logic, its just a shared data holder
|
||||||
|
* */
|
||||||
|
export const useMealPlanStore = defineStore(_STORE_ID, () => {
|
||||||
|
|
||||||
|
let plans = ref(new Map<number, MealPlan>)
|
||||||
|
let currently_updating = ref([new Date(0), new Date(0)])
|
||||||
|
let settings = ref({})
|
||||||
|
|
||||||
|
const plan_list = computed(() => {
|
||||||
|
let plan_list = [] as MealPlan[]
|
||||||
|
|
||||||
|
plans.value.forEach((plan: MealPlan, key: number) => {
|
||||||
|
plan_list.push(plan)
|
||||||
|
})
|
||||||
|
|
||||||
|
return plan_list
|
||||||
|
})
|
||||||
|
|
||||||
|
const empty_meal_plan = computed(() => {
|
||||||
|
return {
|
||||||
|
from_date: null,
|
||||||
|
to_date: null,
|
||||||
|
id: -1,
|
||||||
|
meal_type: null,
|
||||||
|
note: "",
|
||||||
|
note_markdown: "",
|
||||||
|
recipe: null,
|
||||||
|
servings: 1,
|
||||||
|
shared: [],
|
||||||
|
title: "",
|
||||||
|
title_placeholder: "Title", // meal plan edit modal should be improved to not need this
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// const client_settings = computed(() => {
|
||||||
|
// if (this.settings === null) {
|
||||||
|
// this.settings = this.loadClientSettings()
|
||||||
|
// }
|
||||||
|
// return this.settings
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
function refreshFromAPI(from_date: Date, to_date: Date) {
|
||||||
|
if (currently_updating.value[0] !== from_date || currently_updating.value[1] !== to_date) {
|
||||||
|
currently_updating.value = [from_date, to_date] // certainly no perfect check but better than nothing
|
||||||
|
|
||||||
|
const api = new ApiApi()
|
||||||
|
api.apiMealPlanList({fromDate: DateTime.fromJSDate(from_date).toISODate() as string, toDate: DateTime.fromJSDate(to_date).toISODate() as string}).then(r => {
|
||||||
|
r.forEach((p) => {
|
||||||
|
plans.value.set(p.id, p)
|
||||||
|
})
|
||||||
|
currently_updating.value = [new Date(0), new Date(0)]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createObject(object: MealPlan) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiMealPlanCreate({mealPlan: object}).then((r) => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_CREATE)
|
||||||
|
plans.value.set(r.id, r)
|
||||||
|
return r
|
||||||
|
}).catch((err) => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.FAIL_CREATE, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateObject(object: MealPlan) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiMealPlanUpdate({id: object.id, mealPlan: object}).then((r) => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_UPDATE)
|
||||||
|
plans.value.set(r.id, r)
|
||||||
|
}).catch((err) => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.FAIL_UPDATE, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteObject(object: MealPlan) {
|
||||||
|
const api = new ApiApi()
|
||||||
|
return api.apiMealPlanDestroy({id: object.id}).then((r) => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.SUCCESS_DELETE)
|
||||||
|
plans.value.delete(object.id)
|
||||||
|
}).catch((err) => {
|
||||||
|
//StandardToasts.makeStandardToast(this, StandardToasts.FAIL_DELETE, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// function updateClientSettings(settings) {
|
||||||
|
// this.settings = settings
|
||||||
|
// localStorage.setItem(_LOCAL_STORAGE_KEY, JSON.stringify(this.settings))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function loadClientSettings() {
|
||||||
|
// let s = localStorage.getItem(_LOCAL_STORAGE_KEY)
|
||||||
|
// if (s === null) {
|
||||||
|
// return {
|
||||||
|
// displayPeriodUom: "week",
|
||||||
|
// displayPeriodCount: 3,
|
||||||
|
// startingDayOfWeek: 1,
|
||||||
|
// displayWeekNumbers: true,
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// return JSON.parse(s)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return {plans, currently_updating, plan_list, refreshFromAPI, createObject, updateObject, deleteObject}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user