115 lines
3.3 KiB
Vue
115 lines
3.3 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onMounted, ref, watch } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { useI18n } from 'vue-i18n'
|
|
import DayMealCard from '@/components/today/DayMealCard.vue'
|
|
import DayTotalsCard from '@/components/today/DayTotalsCard.vue'
|
|
import { useDiaryStore } from '@/stores/diary'
|
|
import { useMealsStore } from '@/stores/meals'
|
|
import { useUIStore } from '@/stores/ui'
|
|
import { MEAL_TYPES, type MealType } from '@/types/domain'
|
|
import { todayISO } from '@/utils/date'
|
|
import { toErrorMessage } from '@/utils/error'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const diaryStore = useDiaryStore()
|
|
const mealsStore = useMealsStore()
|
|
const ui = useUIStore()
|
|
const { t } = useI18n()
|
|
|
|
const isWorking = ref(false)
|
|
const selectedDate = ref(todayISO())
|
|
const errorMessage = ref('')
|
|
|
|
const resolveDate = (): string => {
|
|
const dateFromRoute = typeof route.params.date === 'string' ? route.params.date : ''
|
|
return dateFromRoute.length > 0 ? dateFromRoute : todayISO()
|
|
}
|
|
|
|
const mealTypeLabel = (mealType: MealType): string => t(`mealTypes.${mealType}`)
|
|
|
|
const reloadDay = async (date: string) => {
|
|
selectedDate.value = date
|
|
diaryStore.ensureCurrentDay(date)
|
|
errorMessage.value = ''
|
|
isWorking.value = true
|
|
try {
|
|
if (mealsStore.list.length <= 0) {
|
|
await mealsStore.loadMeals()
|
|
}
|
|
await diaryStore.loadDay(date)
|
|
} catch (error) {
|
|
errorMessage.value = toErrorMessage(error, t('ux.errors.loadDay'))
|
|
ui.error(errorMessage.value)
|
|
} finally {
|
|
isWorking.value = false
|
|
}
|
|
}
|
|
|
|
watch(
|
|
() => route.params.date,
|
|
async () => {
|
|
await reloadDay(resolveDate())
|
|
},
|
|
)
|
|
|
|
onMounted(async () => {
|
|
await reloadDay(resolveDate())
|
|
})
|
|
|
|
const onDateChange = async () => {
|
|
await router.replace({ name: 'today', params: { date: selectedDate.value } })
|
|
}
|
|
|
|
const onSelectMeal = async (mealType: MealType, mealId: number | null) => {
|
|
try {
|
|
errorMessage.value = ''
|
|
if (mealId === null) {
|
|
await diaryStore.unsetMealForType(selectedDate.value, mealType)
|
|
ui.success(t('ux.toast.updated'))
|
|
return
|
|
}
|
|
await diaryStore.setMealForType(selectedDate.value, mealType, mealId)
|
|
ui.success(t('ux.toast.updated'))
|
|
} catch (error) {
|
|
errorMessage.value = toErrorMessage(error, t('ux.errors.updateDay'))
|
|
ui.error(errorMessage.value)
|
|
}
|
|
}
|
|
|
|
const dayMeals = computed(() => diaryStore.mealsByType)
|
|
const dayTotals = computed(() => diaryStore.computedTotals)
|
|
</script>
|
|
|
|
<template>
|
|
<section class="page">
|
|
<div class="page-header">
|
|
<label class="filter-control">
|
|
<span>{{ t('common.date') }}</span>
|
|
<input v-model="selectedDate" class="input-date" type="date" @change="onDateChange" />
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="errorMessage" class="card card-state card-state--error">{{ errorMessage }}</div>
|
|
<div v-else-if="isWorking || diaryStore.loading" class="card card-state">{{ t('today.loadingDay') }}</div>
|
|
|
|
<template v-else>
|
|
<div class="grid-three">
|
|
<DayMealCard
|
|
v-for="mealType in MEAL_TYPES"
|
|
:key="mealType"
|
|
:meal-type="mealType"
|
|
:label="mealTypeLabel(mealType)"
|
|
:meal-options="diaryStore.selectedMealOptions[mealType]"
|
|
:selected-meal-id="diaryStore.selectedMealId(mealType)"
|
|
:meal="dayMeals[mealType]"
|
|
@select-meal="onSelectMeal"
|
|
/>
|
|
</div>
|
|
|
|
<DayTotalsCard :totals="dayTotals" />
|
|
</template>
|
|
</section>
|
|
</template>
|