MealPlan editor in new editor system

This commit is contained in:
vabene1111
2024-10-08 19:23:20 +02:00
parent 9395a456f0
commit 4f425fb99a
38 changed files with 203 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
<template>
<v-dialog max-width="600" activator="parent" v-model="dialog">
<v-dialog max-width="1400" activator="parent" v-model="dialog">
<component :is="editorComponent" :item="item" @create="createEvent" @save="saveEvent" @delete="deleteEvent" dialog @close="dialog = false"></component>
</v-dialog>
</template>
@@ -8,7 +8,10 @@
import {defineAsyncComponent, PropType, ref, shallowRef} from "vue";
import {EditorSupportedModels} from "@/types/Models";
import {EditorSupportedModels, getGenericModelFromString} from "@/types/Models";
import {useI18n} from "vue-i18n";
const {t} = useI18n()
const emit = defineEmits(['create', 'save', 'delete'])
@@ -21,7 +24,7 @@ const props = defineProps({
closeAfterDelete: {default: true},
})
const editorComponent = shallowRef(defineAsyncComponent(() => import(`@/components/model_editors/${props.model}Editor.vue`)))
const editorComponent = shallowRef(defineAsyncComponent(() => import(`@/components/model_editors/${getGenericModelFromString(props.model, t).model.name}Editor.vue`)))
const dialog = ref(false)

View File

@@ -2,6 +2,11 @@
<v-row class="h-100">
<v-col>
<!-- TODO add hint about CTRL key while drag/drop -->
<v-btn>
<model-edit-dialog model="MealPlan"></model-edit-dialog>
</v-btn>
<CalendarView
:items="planItems"
class="theme-default"
@@ -32,6 +37,8 @@ import {computed, onMounted, ref} from "vue";
import {DateTime} from "luxon";
import {useDisplay} from "vuetify";
import {useMealPlanStore} from "@/stores/MealPlanStore";
import MealPlanEditor from "@/components/model_editors/MealPlanEditor.vue";
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
const {lgAndUp} = useDisplay()

View File

@@ -0,0 +1,131 @@
<template>
<model-editor-base
:loading="loading"
:dialog="dialog"
@save="saveObject"
@delete="deleteObject"
@close="emit('close')"
:is-update="isUpdate()"
:model-class="modelClass"
:object-name="editingObjName()">
<v-card-text>
<v-form :disabled="loading">
<v-row>
<v-col cols="12" md="6">
<v-text-field label="Title" v-model="editingObj.title"></v-text-field>
<v-date-input
v-model="dateRangeValue"
label="Plan Date"
multiple="range"
prepend-icon=""
prepend-inner-icon="$calendar"
></v-date-input>
<v-input>
<v-btn-group elevation="1" class="w-100" divided border>
<v-btn class="w-25" @click="adjustDateRangeLength(dateRangeValue,-1); updateDate()"><i class="fa-solid fa-minus"></i></v-btn>
<v-btn class="w-25" @click="dateRangeValue = shiftDateRange(dateRangeValue, -1); updateDate()"><i class="fa-solid fa-angles-left"></i></v-btn>
<v-btn class="w-25" @click="dateRangeValue = shiftDateRange(dateRangeValue, +1); updateDate()"><i class="fa-solid fa-angles-right"></i></v-btn>
<v-btn class="w-25" @click="adjustDateRangeLength(dateRangeValue,+1); updateDate()"><i class="fa-solid fa-plus"></i></v-btn>
</v-btn-group>
</v-input>
<ModelSelect model="MealType" :allow-create="true" v-model="editingObj.mealType"></ModelSelect>
<v-number-input control-variant="split" :min="0" v-model="editingObj.servings" label="Servings"></v-number-input>
<ModelSelect model="User" :allow-create="false" v-model="editingObj.shared" item-label="displayName" mode="tags"></ModelSelect>
</v-col>
<v-col cols="12" md="6">
<ModelSelect model="Recipe" v-model="editingObj.recipe"
@update:modelValue="editingObj.servings = editingObj.recipe?.servings ? editingObj.recipe?.servings : 1"></ModelSelect>
<!-- <v-number-input label="Days" control-variant="split" :min="1"></v-number-input>-->
<!--TODO create days input with +/- synced to date -->
<recipe-card :recipe="editingObj.recipe" v-if="editingObj && editingObj.recipe"></recipe-card>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea label="Note" v-model="editingObj.note"></v-textarea>
</v-col>
</v-row>
</v-form>
</v-card-text>
</model-editor-base>
</template>
<script setup lang="ts">
import {onMounted, PropType, ref} from "vue";
import {ApiApi, MealPlan, MealType} from "@/openapi";
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
import {DateTime} from "luxon";
import {adjustDateRangeLength, shiftDateRange} from "@/utils/date_utils";
import {VNumberInput} from "vuetify/labs/VNumberInput";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
import RecipeCard from "@/components/display/RecipeCard.vue";
import {VDateInput} from "vuetify/labs/VDateInput";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {MessageType, useMessageStore} from "@/stores/MessageStore";
const props = defineProps({
item: {type: {} as PropType<MealPlan>, required: false, default: null},
itemId: {type: [Number, String], required: false, default: undefined},
dialog: {type: Boolean, default: false}
})
const emit = defineEmits(['create', 'save', 'delete', 'close'])
const {setupState, deleteObject, saveObject, isUpdate, editingObjName, loading, editingObj, modelClass} = useModelEditorFunctions<MealPlan>('MealPlan', emit)
// object specific data (for selects/display)
const dateRangeValue = ref([] as Date[])
onMounted(() => {
const api = new ApiApi()
api.apiMealTypeList().then(r => {
let defaultMealType = {} as MealType
r.results.forEach(r => {
if (r._default) {
defaultMealType = r
}
})
setupState(props.item, props.itemId, () => {
editingObj.value.fromDate = DateTime.now().toJSDate()
editingObj.value.toDate = DateTime.now().toJSDate()
editingObj.value.shared = useUserPreferenceStore().userSettings.planShare
editingObj.value.servings = 1
editingObj.value.mealType = defaultMealType
// initialize date range slider
dateRangeValue.value.push(editingObj.value.fromDate)
}, () => {
// TODO add all dates between start and end to date range
})
})
})
/**
* update the editing object with data from the date range selector whenever its changed (could probably be a watcher)
*/
// TODO properly hook into beforeSave hook if i ever implement one for model editors
function updateDate() {
if (dateRangeValue.value != null) {
editingObj.value.fromDate = dateRangeValue.value[0]
editingObj.value.toDate = dateRangeValue.value[dateRangeValue.value.length - 1]
} else {
useMessageStore().addMessage(MessageType.WARNING, 'Food', 7000)
return
}
}
</script>
<style scoped>
</style>

View File

@@ -29,6 +29,8 @@
</v-menu>
</v-text-field>
<v-checkbox v-model="editingObj._default" :label="$t('Default')"></v-checkbox>
<v-color-picker v-model="editingObj.color" mode="hex" :modes="['hex']" show-swatches
:swatches="[['#ddbf86'],['#b98766'],['#b55e4f'],['#82aa8b'],['#385f84']]"></v-color-picker>
</v-form>

View File

@@ -1,13 +1,13 @@
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import {onBeforeMount, PropType, ref} from "vue";
import {GenericModel, getGenericModelFromString} from "@/types/Models";
import {onBeforeMount, ref} from "vue";
import {EditorSupportedModels, GenericModel, getGenericModelFromString} from "@/types/Models";
import {useI18n} from "vue-i18n";
import {ResponseError} from "@/openapi";
// TODO type emit parameter (https://mokkapps.de/vue-tips/emit-event-from-composable)
// TODO alternatively there seems to be a getContext method to get the calling context (good practice?)
export function useModelEditorFunctions<T>(modelName: string, emit: any) {
export function useModelEditorFunctions<T>(modelName: EditorSupportedModels, emit: any) {
const loading = ref(true)
const editingObj = ref({} as T)

View File

@@ -61,6 +61,7 @@
"Custom Filter": "",
"Database": "",
"Date": "",
"Default": "",
"DelayFor": "",
"DelayUntil": "",
"Delete": "",

View File

@@ -58,6 +58,7 @@
"Custom Filter": "Персонализиран филтър",
"Database": "",
"Date": "Дата",
"Default": "",
"DelayFor": "Закъснение за {hours} часа",
"DelayUntil": "Забавяне до",
"Delete": "Изтрий",

View File

@@ -88,6 +88,7 @@
"Day": "",
"Days": "",
"Decimals": "",
"Default": "",
"Default_Unit": "",
"DelayFor": "",
"DelayUntil": "",

View File

@@ -87,6 +87,7 @@
"Day": "Den",
"Days": "Dny",
"Decimals": "Desetinná místa",
"Default": "",
"DefaultPage": "Výchozí stránka",
"Default_Unit": "Výchozí jednotka",
"DelayFor": "Odložit na {hours} hodin",

View File

@@ -80,6 +80,7 @@
"Day": "Dag",
"Days": "Dage",
"Decimals": "Decimaler",
"Default": "",
"Default_Unit": "Standardenhed",
"DelayFor": "Udskyd i {hours} hours",
"DelayUntil": "Udskyd indtil",

View File

@@ -89,6 +89,7 @@
"Day": "Tag",
"Days": "Tage",
"Decimals": "Nachkommastellen",
"Default": "Standard",
"DefaultPage": "Standardseite",
"Default_Unit": "Standardeinheit",
"DelayFor": "Um {hours} Stunden verschieben",

View File

@@ -79,6 +79,7 @@
"Day": "Ημέρα",
"Days": "Ημέρες",
"Decimals": "Δεκαδικά",
"Default": "",
"Default_Unit": "Προεπιλεγμένη μονάδα μέτρησης",
"DelayFor": "Καθυστέρηση για {hours} ώρες",
"DelayUntil": "Καθυστέρηση μέχρι",

View File

@@ -88,6 +88,7 @@
"Day": "Day",
"Days": "Days",
"Decimals": "Decimals",
"Default": "Default",
"DefaultPage": "Default Page",
"Default_Unit": "Default Unit",
"DelayFor": "Delay for {hours} hours",

View File

@@ -88,6 +88,7 @@
"Day": "Día",
"Days": "Días",
"Decimals": "Decimales",
"Default": "",
"DefaultPage": "Página por Defecto",
"Default_Unit": "Unidad Predeterminada",
"DelayFor": "Retrasar por {hours} horas",

View File

@@ -41,6 +41,7 @@
"Current_Period": "Nykyinen Jakso",
"Database": "",
"Date": "Päivämäärä",
"Default": "",
"Delete": "Poista",
"DeleteConfirmQuestion": "",
"Delete_Food": "Poista ruoka",

View File

@@ -87,6 +87,7 @@
"Day": "Jour",
"Days": "Jours",
"Decimals": "Décimales",
"Default": "",
"DefaultPage": "Page par défaut",
"Default_Unit": "Unité par défaut",
"DelayFor": "Retard de {hours} heures",

View File

@@ -88,6 +88,7 @@
"Day": "יום",
"Days": "ימים",
"Decimals": "דצימל",
"Default": "",
"DefaultPage": "עמוד ברירת מחדל",
"Default_Unit": "ערך ברירת מחדל",
"DelayFor": "השהה ל {hours} שעות",

View File

@@ -79,6 +79,7 @@
"Day": "Nap",
"Days": "Nap",
"Decimals": "Tizedesek",
"Default": "",
"DelayFor": "Késleltetés {hours} óráig",
"DelayUntil": "",
"Delete": "Törlés",

View File

@@ -28,6 +28,7 @@
"Create_New_Shopping Category": "Ստեղծել գնումների նոր կատեգորիա",
"Database": "",
"Date": "",
"Default": "",
"Delete": "",
"DeleteConfirmQuestion": "",
"Delete_Food": "Ջնջել սննդամթերքը",

View File

@@ -69,6 +69,7 @@
"Day": "",
"Days": "",
"Decimals": "",
"Default": "",
"Default_Unit": "",
"DelayFor": "",
"DelayUntil": "",

View File

@@ -88,6 +88,7 @@
"Day": "",
"Days": "",
"Decimals": "",
"Default": "",
"Default_Unit": "",
"DelayFor": "",
"DelayUntil": "",

View File

@@ -73,6 +73,7 @@
"Day": "Giorno",
"Days": "Giorni",
"Decimals": "Decimali",
"Default": "",
"Default_Unit": "Unità predefinita",
"DelayFor": "Ritarda per {hours} ore",
"DelayUntil": "Ritarda fino a",

View File

@@ -80,6 +80,7 @@
"Day": "",
"Days": "",
"Decimals": "",
"Default": "",
"Default_Unit": "",
"DelayFor": "",
"DelayUntil": "",

View File

@@ -77,6 +77,7 @@
"Day": "Dag",
"Days": "Dager",
"Decimals": "Desimaler",
"Default": "",
"Default_Unit": "Standard Enhet",
"DelayFor": "Utsett i {hours} timer",
"DelayUntil": "Forsink til",

View File

@@ -81,6 +81,7 @@
"Day": "Dag",
"Days": "Dagen",
"Decimals": "Decimalen",
"Default": "",
"Default_Unit": "Standaardeenheid",
"DelayFor": "Stel {hours} uur uit",
"DelayUntil": "Vertraag tot",

View File

@@ -89,6 +89,7 @@
"Day": "Dzień",
"Days": "Dni",
"Decimals": "Ułamki dziesiętne",
"Default": "",
"DefaultPage": "Strona domyślna",
"Default_Unit": "Jednostka domyślna",
"DelayFor": "Opóźnij o {hours} godzin",

View File

@@ -61,6 +61,7 @@
"Database": "",
"Date": "Data",
"Decimals": "Casas decimais",
"Default": "",
"Default_Unit": "Unidade padrão",
"DelayFor": "Atrasar por {hours} horas",
"DelayUntil": "",

View File

@@ -86,6 +86,7 @@
"Day": "Dia",
"Days": "Dias",
"Decimals": "Decimais",
"Default": "",
"Default_Unit": "Unidade Padrão",
"DelayFor": "Demorar por {hours} horas",
"DelayUntil": "Atrasar Até",

View File

@@ -75,6 +75,7 @@
"Day": "Zi",
"Days": "Zile",
"Decimals": "Zecimale",
"Default": "",
"Default_Unit": "Unitate standard",
"DelayFor": "Întârziere pentru {hours} ore",
"DelayUntil": "Amână până la",

View File

@@ -52,6 +52,7 @@
"Custom Filter": "Пользовательский фильтр",
"Database": "",
"Date": "Дата",
"Default": "",
"DelayFor": "Отложить на {hours} часов",
"Delete": "Удалить",
"DeleteConfirmQuestion": "",

View File

@@ -52,6 +52,7 @@
"Data_Import_Info": "Izboljšajte svoj prostor z uvozom seznama živil, enot in drugega, ker je pripravila skupnost, ter s tem izboljšajte svojo zbirko receptov.",
"Database": "",
"Date": "Datum",
"Default": "",
"DelayFor": "Zamakni za {hours} ur",
"DelayUntil": "Zamakni do",
"Delete": "Izbriši",

View File

@@ -89,6 +89,7 @@
"Day": "Dag",
"Days": "Dagar",
"Decimals": "Decimaler",
"Default": "",
"DefaultPage": "Standardsida",
"Default_Unit": "Standardenhet",
"DelayFor": "Fördröjning på {hours} timmar",

View File

@@ -88,6 +88,7 @@
"Day": "Gün",
"Days": "Günler",
"Decimals": "Ondalık Sayılar",
"Default": "",
"DefaultPage": "Varsayılan Sayfa",
"Default_Unit": "Varsayılan Birim",
"DelayFor": "{hours} saat geciktir",

View File

@@ -65,6 +65,7 @@
"Database": "",
"Date": "Дата",
"Decimals": "Десятки",
"Default": "",
"Default_Unit": "Одиниця замовчуванням",
"DelayFor": "Затримка на {hours} годин",
"DelayUntil": "",

View File

@@ -86,6 +86,7 @@
"Day": "天",
"Days": "天",
"Decimals": "小数",
"Default": "",
"Default_Unit": "默认单位",
"DelayFor": "延迟 {hours} 小时",
"DelayUntil": "推迟到",

View File

@@ -23,6 +23,7 @@
"Create": "",
"Database": "",
"Date": "",
"Default": "",
"Delete": "",
"DeleteConfirmQuestion": "",
"Deleted": "",

View File

@@ -20,15 +20,18 @@
<script setup lang="ts">
import {useRouter} from "vue-router";
import {EditorSupportedModels} from "@/types/Models";
import {EditorSupportedModels, getGenericModelFromString} from "@/types/Models";
import {defineAsyncComponent, PropType, shallowRef} from "vue";
import {useI18n} from "vue-i18n";
const {t} = useI18n()
const props = defineProps({
model: {type: String as PropType<EditorSupportedModels>, required: true},
id: {type: String, required: false, default: undefined},
})
const editorComponent = shallowRef(defineAsyncComponent(() => import(`@/components/model_editors/${props.model}Editor.vue`)))
const editorComponent = shallowRef(defineAsyncComponent(() => import(`@/components/model_editors/${getGenericModelFromString(props.model, t).model.name}Editor.vue`)))
const router = useRouter()

View File

@@ -10,7 +10,7 @@ type VDataTableProps = InstanceType<typeof VDataTable>['$props']
* @param t translation function from calling context
* @return instance of GenericModel
*/
export function getGenericModelFromString(modelName: string, t: any) {
export function getGenericModelFromString(modelName: EditorSupportedModels, t: any) {
if (SUPPORTED_MODELS.has(modelName.toLowerCase())) {
return new GenericModel(SUPPORTED_MODELS.get(modelName.toLowerCase()), t)
} else {
@@ -70,7 +70,7 @@ export type Model = {
}
export let SUPPORTED_MODELS = new Map<string, Model>()
export type EditorSupportedModels = 'UnitConversion' | 'AccessToken' | 'InviteLink' | 'UserSpace' | 'MealType' | 'Property' | 'Food' | 'Supermarket' | 'SupermarketCategory' | 'PropertyType' | 'Automation'
export type EditorSupportedModels = 'UnitConversion' | 'AccessToken' | 'InviteLink' | 'UserSpace' | 'MealType' | 'MealPlan' | 'Property' | 'Food' | 'Supermarket' | 'SupermarketCategory' | 'PropertyType' | 'Automation'
export const TFood = {
name: 'Food',
@@ -150,6 +150,22 @@ export const TMealType = {
} as Model
registerModel(TMealType)
export const TMealPlan = {
name: 'MealPlan',
localizationKey: 'Meal_Plan',
icon: 'fa-solid fa-calendar-days',
isPaginated: true,
toStringKeys: ['name'],
tableHeaders: [
{title: 'Title', key: 'title'},
{title: 'StartDate', key: 'startDate'},
{title: 'Actions', key: 'action', align: 'end'},
]
} as Model
registerModel(TMealPlan)
export const TUser = {
name: 'User',
localizationKey: 'User',