128 lines
3.3 KiB
TypeScript
128 lines
3.3 KiB
TypeScript
import { computed, ref } from 'vue'
|
|
import { defineStore } from 'pinia'
|
|
import BackendAPI from '@/BackendAPI.ts'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import { useMealsStore } from '@/stores/meals'
|
|
import type { DayMealsByType, DiaryDay, MealType } from '@/types/domain'
|
|
import { MEAL_TYPES } from '@/types/domain'
|
|
import { computeDayTotals, emptyTotals } from '@/utils/nutrition'
|
|
import { unwrapApiData } from '@/utils/api'
|
|
|
|
const createEmptyDay = (date: string): DiaryDay => ({
|
|
user_id: 0,
|
|
day_date: date,
|
|
diary_day_id: null,
|
|
entries: [],
|
|
totals: emptyTotals(),
|
|
})
|
|
|
|
export const useDiaryStore = defineStore('diary', () => {
|
|
const auth = useAuthStore()
|
|
const mealsStore = useMealsStore()
|
|
|
|
const currentDay = ref<DiaryDay | null>(null)
|
|
const loading = ref(false)
|
|
|
|
const requireToken = (): string => {
|
|
if (!auth.token) {
|
|
throw new Error('User is not authenticated')
|
|
}
|
|
return auth.token
|
|
}
|
|
|
|
const mealsByType = computed<DayMealsByType>(() => {
|
|
const ret: DayMealsByType = {
|
|
breakfast: null,
|
|
lunch: null,
|
|
dinner: null,
|
|
}
|
|
|
|
for (const entry of currentDay.value?.entries ?? []) {
|
|
const cachedMeal = mealsStore.getMealFromCache(entry.meal_id)
|
|
ret[entry.meal_type] = cachedMeal ?? entry.meal ?? null
|
|
}
|
|
|
|
return ret
|
|
})
|
|
|
|
const computedTotals = computed(() => computeDayTotals(mealsByType.value))
|
|
|
|
const hydrateDayMeals = async (day: DiaryDay) => {
|
|
const loadTasks: Promise<unknown>[] = []
|
|
for (const entry of day.entries) {
|
|
loadTasks.push(mealsStore.ensureMeal(entry.meal_id))
|
|
}
|
|
if (loadTasks.length > 0) {
|
|
await Promise.all(loadTasks)
|
|
}
|
|
}
|
|
|
|
const setCurrentDay = async (day: DiaryDay) => {
|
|
currentDay.value = day
|
|
await hydrateDayMeals(day)
|
|
}
|
|
|
|
const loadDay = async (date: string) => {
|
|
loading.value = true
|
|
try {
|
|
const token = requireToken()
|
|
const payload = unwrapApiData<DiaryDay>(await BackendAPI.diaryDayGet(token, date, true))
|
|
await setCurrentDay(payload)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const setMealForType = async (date: string, mealType: MealType, mealId: number) => {
|
|
const token = requireToken()
|
|
const payload = unwrapApiData<DiaryDay>(await BackendAPI.diaryDaySetMeal(token, date, mealType, mealId))
|
|
await setCurrentDay(payload)
|
|
}
|
|
|
|
const unsetMealForType = async (date: string, mealType: MealType) => {
|
|
const token = requireToken()
|
|
const payload = unwrapApiData<DiaryDay>(await BackendAPI.diaryDayUnsetMeal(token, date, mealType))
|
|
await setCurrentDay(payload)
|
|
}
|
|
|
|
const selectedMealId = (mealType: MealType): number | null => {
|
|
const entry = currentDay.value?.entries.find((item) => item.meal_type === mealType)
|
|
return entry?.meal_id ?? null
|
|
}
|
|
|
|
const ensureCurrentDay = (date: string) => {
|
|
if (!currentDay.value || currentDay.value.day_date !== date) {
|
|
currentDay.value = createEmptyDay(date)
|
|
}
|
|
}
|
|
|
|
const selectedMealOptions = computed(() => {
|
|
const map: Record<MealType, { value: number; label: string }[]> = {
|
|
breakfast: [],
|
|
lunch: [],
|
|
dinner: [],
|
|
}
|
|
|
|
for (const type of MEAL_TYPES) {
|
|
map[type] = mealsStore.sortedMeals
|
|
.filter((meal) => meal.meal_type === type)
|
|
.map((meal) => ({ value: meal.meal_id, label: meal.name }))
|
|
}
|
|
|
|
return map
|
|
})
|
|
|
|
return {
|
|
currentDay,
|
|
loading,
|
|
mealsByType,
|
|
computedTotals,
|
|
selectedMealOptions,
|
|
selectedMealId,
|
|
ensureCurrentDay,
|
|
loadDay,
|
|
setMealForType,
|
|
unsetMealForType,
|
|
}
|
|
})
|