added implementation frontend by prompt @ 2026-02-14 05:35:18 #CODEX

This commit is contained in:
2026-02-14 07:13:06 +01:00
parent 92086055dc
commit 3010a66d59
32 changed files with 2024 additions and 39 deletions

View File

@ -0,0 +1,127 @@
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,
}
})