mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-03 13:19:16 -05:00
meal plan and recipe editor improvements
This commit is contained in:
59
vue3/src/components/display/ClosableHelpAlert.vue
Normal file
59
vue3/src/components/display/ClosableHelpAlert.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<v-alert :title="props.title" closable @click:close="closeAlert()" v-if="showAlert">
|
||||
<template #prepend>
|
||||
<v-icon icon="$help"></v-icon>
|
||||
</template>
|
||||
<p>
|
||||
{{ props.text}}
|
||||
<v-btn color="success" class="float-right" v-if="props.actionText != ''" @click="emit('click')">{{ actionText}}</v-btn>
|
||||
</p>
|
||||
</v-alert>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import {MessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
import {computed} from "vue";
|
||||
|
||||
// emit click if action is clicked, actual effect must come from parent component
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const props = defineProps({
|
||||
title: {type: String, required: false,},
|
||||
text: {type: String, required: true},
|
||||
|
||||
// show an action button if any text is given and emit click event if button is pressed
|
||||
actionText: {type: String, required: false,},
|
||||
})
|
||||
|
||||
/**
|
||||
* somewhat unique hash of the given text to save which alerts have already been closed
|
||||
*/
|
||||
const alertHash = computed(() => {
|
||||
return props.text.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0).toString()
|
||||
})
|
||||
|
||||
/**
|
||||
* only show the alert if it hasn't been closed on that device before
|
||||
*/
|
||||
const showAlert = computed(() => {
|
||||
return !useUserPreferenceStore().deviceSettings.general_closedHelpAlerts.includes(alertHash.value)
|
||||
})
|
||||
|
||||
/**
|
||||
* called when alert is closed to save this alert into the list of closed alerts
|
||||
*/
|
||||
function closeAlert() {
|
||||
if (!useUserPreferenceStore().deviceSettings.general_closedHelpAlerts.includes(alertHash.value)) {
|
||||
useUserPreferenceStore().deviceSettings.general_closedHelpAlerts.push(alertHash.value)
|
||||
} else {
|
||||
useMessageStore().addMessage(MessageType.ERROR, 'Trying to close already closed alert', 0, props.text)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
22
vue3/src/components/display/IngredientString.vue
Normal file
22
vue3/src/components/display/IngredientString.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<span v-if="ingredient.amount && !Number.isNaN(ingredient.amount)">{{$n(ingredient.amount)}}</span>
|
||||
<span class="ms-1" v-if="ingredient.unit">{{ ingredient.unit.name}}</span>
|
||||
<span class="ms-1" v-if="ingredient.food">{{ ingredient.food.name}}</span>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Ingredient} from "@/openapi";
|
||||
import {PropType} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
ingredient: {type: {} as PropType<Ingredient>, required: true}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,34 +1,38 @@
|
||||
<template>
|
||||
<v-row class="h-100">
|
||||
<v-col>
|
||||
<!-- TODO add hint about CTRL key while drag/drop -->
|
||||
<calendar-view
|
||||
:show-date="calendarDate"
|
||||
:items="planItems"
|
||||
class="theme-default"
|
||||
:item-content-height="calendarItemHeight"
|
||||
:enable-drag-drop="true"
|
||||
@dropOnDate="dropCalendarItemOnDate"
|
||||
:display-period-uom="useUserPreferenceStore().deviceSettings.mealplan_displayPeriod"
|
||||
:display-period-count="useUserPreferenceStore().deviceSettings.mealplan_displayPeriodCount"
|
||||
:starting-day-of-week="useUserPreferenceStore().deviceSettings.mealplan_startingDayOfWeek"
|
||||
:display-week-numbers="useUserPreferenceStore().deviceSettings.mealplan_displayWeekNumbers"
|
||||
:current-period-label="$t('Today')"
|
||||
@click-date="(date : Date, calendarItems: [], windowEvent: any) => { newPlanDialogDefaultItem.fromDate = date; newPlanDialogDefaultItem.toDate = date; newPlanDialog = true }">
|
||||
<template #header="{ headerProps }">
|
||||
<calendar-view-header :header-props="headerProps" @input="(d:Date) => calendarDate = d"></calendar-view-header>
|
||||
</template>
|
||||
<template #item="{ value, weekStartDate, top }">
|
||||
<meal-plan-calendar-item
|
||||
:item-height="calendarItemHeight"
|
||||
:value="value"
|
||||
:item-top="top"
|
||||
@onDragStart="currentlyDraggedMealplan = value"
|
||||
@delete="(arg: MealPlan) => {useMealPlanStore().plans.delete(arg.id)}"
|
||||
:detailed-items="lgAndUp"
|
||||
></meal-plan-calendar-item>
|
||||
</template>
|
||||
</calendar-view>
|
||||
<v-col class="pb-0">
|
||||
<v-card class="h-100" :loading="useMealPlanStore().loading">
|
||||
<!-- TODO add hint about CTRL key while drag/drop -->
|
||||
<!-- TODO multi selection? date range selection ? -->
|
||||
<calendar-view
|
||||
:show-date="calendarDate"
|
||||
:items="planItems"
|
||||
class="theme-default"
|
||||
:item-content-height="calendarItemHeight"
|
||||
:enable-drag-drop="true"
|
||||
@dropOnDate="dropCalendarItemOnDate"
|
||||
:display-period-uom="useUserPreferenceStore().deviceSettings.mealplan_displayPeriod"
|
||||
:display-period-count="useUserPreferenceStore().deviceSettings.mealplan_displayPeriodCount"
|
||||
:starting-day-of-week="useUserPreferenceStore().deviceSettings.mealplan_startingDayOfWeek"
|
||||
:display-week-numbers="useUserPreferenceStore().deviceSettings.mealplan_displayWeekNumbers"
|
||||
:current-period-label="$t('Today')"
|
||||
@click-date="(date : Date, calendarItems: [], windowEvent: any) => { newPlanDialogDefaultItem.fromDate = date; newPlanDialogDefaultItem.toDate = date; newPlanDialog = true }">
|
||||
<template #header="{ headerProps }">
|
||||
<calendar-view-header :header-props="headerProps" @input="(d:Date) => calendarDate = d"></calendar-view-header>
|
||||
</template>
|
||||
<template #item="{ value, weekStartDate, top }">
|
||||
<meal-plan-calendar-item
|
||||
:item-height="calendarItemHeight"
|
||||
:value="value"
|
||||
:item-top="top"
|
||||
@onDragStart="currentlyDraggedMealplan = value"
|
||||
@delete="(arg: MealPlan) => {useMealPlanStore().plans.delete(arg.id)}"
|
||||
:detailed-items="lgAndUp"
|
||||
></meal-plan-calendar-item>
|
||||
</template>
|
||||
</calendar-view>
|
||||
</v-card>
|
||||
|
||||
|
||||
<model-edit-dialog model="MealPlan" v-model="newPlanDialog" :itemDefaults="newPlanDialogDefaultItem"
|
||||
@create="(arg: any) => useMealPlanStore().plans.set(arg.id, arg)"></model-edit-dialog>
|
||||
@@ -44,8 +48,8 @@ import "vue-simple-calendar/dist/css/default.css"
|
||||
|
||||
import MealPlanCalendarItem from "@/components/display/MealPlanCalendarItem.vue";
|
||||
import {IMealPlanCalendarItem, IMealPlanNormalizedCalendarItem} from "@/types/MealPlan";
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {DateTime} from "luxon";
|
||||
import {computed, onMounted, ref, watch} from "vue";
|
||||
import {DateTime, Duration} from "luxon";
|
||||
import {useDisplay} from "vuetify";
|
||||
import {useMealPlanStore} from "@/stores/MealPlanStore";
|
||||
import ModelEditDialog from "@/components/dialogs/ModelEditDialog.vue";
|
||||
@@ -89,22 +93,47 @@ const calendarItemHeight = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
useMealPlanStore().refreshFromAPI() //TODO filter to visible date
|
||||
/**
|
||||
* watch calendar date and load entries accordingly
|
||||
*/
|
||||
watch(calendarDate, () => {
|
||||
let daysInPeriod = 7
|
||||
if (useUserPreferenceStore().deviceSettings.mealplan_displayPeriod == 'month') {
|
||||
daysInPeriod = 31
|
||||
} else if (useUserPreferenceStore().deviceSettings.mealplan_displayPeriod == 'year') {
|
||||
daysInPeriod = 365
|
||||
}
|
||||
|
||||
let days = useUserPreferenceStore().deviceSettings.mealplan_displayPeriodCount * daysInPeriod
|
||||
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.now().plus({days: days}).toJSDate())
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// initial load for next 30 days
|
||||
useMealPlanStore().refreshFromAPI(calendarDate.value, DateTime.now().plus({days: 30}).toJSDate())
|
||||
})
|
||||
|
||||
/**
|
||||
* handle drop event for calendar items on fields
|
||||
* @param undefinedItem
|
||||
* @param targetDate
|
||||
* @param event
|
||||
*/
|
||||
function dropCalendarItemOnDate(undefinedItem: IMealPlanNormalizedCalendarItem, targetDate: Date, event: DragEvent) {
|
||||
//The item argument (first) is undefined because our custom calendar item cannot manipulate the calendar state so the item is unknown to the calendar (probably fixable by somehow binding state to the item)
|
||||
if (currentlyDraggedMealplan.value.originalItem.mealPlan.id != undefined) {
|
||||
let mealPlan = useMealPlanStore().plans.get(currentlyDraggedMealplan.value.originalItem.mealPlan.id)
|
||||
if (mealPlan != undefined) {
|
||||
let fromToDiff = DateTime.fromJSDate(mealPlan.toDate).diff(DateTime.fromJSDate(mealPlan.fromDate), 'days')
|
||||
let fromToDiff = {days: 1}
|
||||
if (mealPlan.toDate) {
|
||||
fromToDiff = DateTime.fromJSDate(mealPlan.toDate).diff(DateTime.fromJSDate(mealPlan.fromDate), 'days')
|
||||
}
|
||||
// create copy of item if control is pressed
|
||||
if (event.ctrlKey) {
|
||||
let new_entry = Object.assign({}, mealPlan)
|
||||
new_entry.fromDate = targetDate
|
||||
new_entry.toDate = DateTime.fromJSDate(targetDate).plus(fromToDiff).toJSDate()
|
||||
useMealPlanStore().createObject(new_entry)
|
||||
|
||||
} else {
|
||||
mealPlan.fromDate = targetDate
|
||||
mealPlan.toDate = DateTime.fromJSDate(targetDate).plus(fromToDiff).toJSDate()
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<v-input :hint="props.hint" persistent-hint :label="props.label" class="" >
|
||||
|
||||
<!-- TODO resolve-on-load false for now, race condition with model class, make prop once better solution is found -->
|
||||
|
||||
<Multiselect
|
||||
|
||||
:ref="`ref_${props.id}`"
|
||||
|
||||
@@ -14,11 +14,15 @@
|
||||
<v-icon icon="fas fa-sliders-h"></v-icon>
|
||||
<v-menu activator="parent">
|
||||
<v-list>
|
||||
<v-list-item prepend-icon="fas fa-plus-circle" @click="showName = true" v-if="!showName && (step.name == null || step.name == '')">{{
|
||||
$t('Name')
|
||||
}}
|
||||
</v-list-item>
|
||||
<v-list-item prepend-icon="fas fa-plus-circle" @click="showTime = true" v-if="!showTime && step.time == 0">{{ $t('Time') }}</v-list-item>
|
||||
<v-list-item prepend-icon="fas fa-plus-circle" @click="showFile = true" v-if="!showFile && step.file == null">{{ $t('File') }}</v-list-item>
|
||||
<v-list-item prepend-icon="fas fa-plus-circle" @click="showRecipe = true" v-if="!showRecipe && step.stepRecipe == null">{{ $t('Recipe') }}</v-list-item>
|
||||
|
||||
<v-list-item prepend-icon="$delete">{{ $t('Delete') }}</v-list-item>
|
||||
<v-list-item prepend-icon="$delete" @click="emit('delete')">{{ $t('Delete') }}</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
@@ -27,7 +31,8 @@
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="step.name"
|
||||
label="Step Name"
|
||||
:label="$t('Name')"
|
||||
v-if="showName || (step.name != null && step.name != '')"
|
||||
></v-text-field>
|
||||
|
||||
<v-row>
|
||||
@@ -46,20 +51,21 @@
|
||||
<v-col cols="12">
|
||||
<v-label>{{ $t('Ingredients') }}</v-label>
|
||||
|
||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients">
|
||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients" v-if="!mobile">
|
||||
<v-row v-for="(ingredient, index) in step.ingredients" dense>
|
||||
<v-col cols="2">
|
||||
<v-number-input :id="`id_input_amount_${step.id}_${index}`" :label="$t('Amount')" v-model="ingredient.amount" inset control-variant="stacked"
|
||||
hide-details
|
||||
:min="0"></v-number-input>
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<model-select model="Unit" v-model="ingredient.unit"></model-select>
|
||||
<model-select model="Unit" v-model="ingredient.unit" hide-details></model-select>
|
||||
</v-col>
|
||||
<v-col cols="3">
|
||||
<model-select model="Food" v-model="ingredient.food"></model-select>
|
||||
<model-select model="Food" v-model="ingredient.food" hide-details></model-select>
|
||||
</v-col>
|
||||
<v-col cols="3" @keydown.tab="event => handleIngredientNoteTab(event, index)">
|
||||
<v-text-field :label="$t('Note')" v-model="ingredient.note"></v-text-field>
|
||||
<v-text-field :label="$t('Note')" v-model="ingredient.note" hide-details></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="1">
|
||||
<v-btn variant="plain" icon>
|
||||
@@ -74,15 +80,36 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</vue-draggable>
|
||||
<v-btn-group density="compact">
|
||||
|
||||
<v-list v-if="mobile">
|
||||
<vue-draggable v-model="step.ingredients" handle=".drag-handle" :on-sort="sortIngredients">
|
||||
<v-list-item v-for="(ingredient, index) in step.ingredients" border @click="editingIngredientIndex = index; dialogIngredientEditor = true">
|
||||
<ingredient-string :ingredient="ingredient"></ingredient-string>
|
||||
<template #append>
|
||||
<v-icon icon="$dragHandle" class="drag-handle"></v-icon>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
</vue-draggable>
|
||||
</v-list>
|
||||
|
||||
<v-btn-group density="compact" class="mt-1">
|
||||
<v-btn color="success" @click="insertAndFocusIngredient()" prepend-icon="$add">{{ $t('Add') }}</v-btn>
|
||||
<v-btn color="warning" @click="dialogIngredientParser = true"><v-icon icon="$add"></v-icon> <v-icon icon="$add"></v-icon></v-btn>
|
||||
<v-btn color="warning" @click="dialogIngredientParser = true">
|
||||
<v-icon icon="$add"></v-icon>
|
||||
<v-icon icon="$add"></v-icon>
|
||||
</v-btn>
|
||||
</v-btn-group>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-label>{{ $t('Instructions') }}</v-label>
|
||||
<v-alert @click="dialogMarkdownEdit = true" class="mt-2 cursor-text" min-height="52px">
|
||||
{{ step.instruction }}
|
||||
<v-alert @click="dialogMarkdownEditor = true" class="mt-2 cursor-pointer" min-height="52px">
|
||||
<template v-if="step.instruction != '' && step.instruction != null">
|
||||
{{ step.instruction }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<i> {{ $t('InstructionsEditHelp') }} </i>
|
||||
</template>
|
||||
</v-alert>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -92,12 +119,15 @@
|
||||
</v-card>
|
||||
|
||||
<v-dialog
|
||||
v-model="dialogMarkdownEdit"
|
||||
v-model="dialogMarkdownEditor"
|
||||
:max-width="(mobile) ? '100vw': '75vw'"
|
||||
:fullscreen="mobile">
|
||||
<v-card>
|
||||
<v-closable-card-title :title="$t('Instructions')" v-model="dialogMarkdownEdit"></v-closable-card-title>
|
||||
<v-closable-card-title :title="$t('Instructions')" v-model="dialogMarkdownEditor"></v-closable-card-title>
|
||||
<step-markdown-editor class="h-100" v-model="step"></step-markdown-editor>
|
||||
<v-card-actions v-if="!mobile">
|
||||
<v-btn @click="dialogMarkdownEditor = false">{{ $t('Close') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
@@ -115,34 +145,59 @@
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog
|
||||
v-model="dialogIngredientEditor"
|
||||
:max-width="(mobile) ? '100vw': '75vw'"
|
||||
:fullscreen="mobile">
|
||||
<v-card>
|
||||
<v-closable-card-title :title="$t('Ingredient Editor')" v-model="dialogIngredientEditor"></v-closable-card-title>
|
||||
<v-card-text>
|
||||
<v-form>
|
||||
<v-number-input v-model="step.ingredients[editingIngredientIndex].amount" inset control-variant="stacked" :label="$t('Amount')"
|
||||
:min="0"></v-number-input>
|
||||
<model-select model="Unit" v-model="step.ingredients[editingIngredientIndex].unit" :label="$t('Unit')"></model-select>
|
||||
<model-select model="Food" v-model="step.ingredients[editingIngredientIndex].food" :label="$t('Food')"></model-select>
|
||||
<v-text-field :label="$t('Note')" v-model="step.ingredients[editingIngredientIndex].note" ></v-text-field>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn @click="dialogIngredientEditor = false">{{ $t('Close') }}</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {nextTick, ref} from 'vue'
|
||||
import {ApiApi, Food, Ingredient, ParsedIngredient, Step} from "@/openapi";
|
||||
import {ApiApi, Ingredient, ParsedIngredient, Step} from "@/openapi";
|
||||
import StepMarkdownEditor from "@/components/inputs/StepMarkdownEditor.vue";
|
||||
import {VNumberInput} from 'vuetify/labs/VNumberInput' //TODO remove once component is out of labs
|
||||
import IngredientsTableRow from "@/components/display/IngredientsTableRow.vue";
|
||||
import {VNumberInput} from 'vuetify/labs/VNumberInput'
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import {useDisplay} from "vuetify";
|
||||
import {VueDraggable} from "vue-draggable-plus";
|
||||
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
|
||||
import IngredientString from "@/components/display/IngredientString.vue";
|
||||
|
||||
const emit = defineEmits(['delete'])
|
||||
|
||||
const step = defineModel<Step>({required: true})
|
||||
|
||||
const props = defineProps({
|
||||
stepIndex: {type: Number, required: true},
|
||||
})
|
||||
|
||||
const {mobile} = useDisplay()
|
||||
|
||||
const showName = ref(false)
|
||||
const showTime = ref(false)
|
||||
const showRecipe = ref(false)
|
||||
const showFile = ref(false)
|
||||
|
||||
const dialogMarkdownEdit = ref(false)
|
||||
const dialogMarkdownEditor = ref(false)
|
||||
const dialogIngredientEditor = ref(false)
|
||||
const dialogIngredientParser = ref(false)
|
||||
|
||||
const editingIngredientIndex = ref({} as Ingredient)
|
||||
const ingredientTextInput = ref("")
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<closable-help-alert :text="$t('RecipeStepsHelp')" :action-text="$t('Steps')" @click="tab='steps'"></closable-help-alert>
|
||||
</v-form>
|
||||
|
||||
</v-tabs-window-item>
|
||||
@@ -64,13 +65,13 @@
|
||||
<v-form :disabled="loading || fileApiLoading">
|
||||
<v-row v-for="(s,i ) in editingObj.steps" :key="s.id">
|
||||
<v-col>
|
||||
<step-editor v-model="editingObj.steps[i]" :step-index="i"></step-editor>
|
||||
<step-editor v-model="editingObj.steps[i]" :step-index="i" @delete="deleteStepAtIndex(i)"></step-editor>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col class="text-center">
|
||||
<v-btn-group density="compact">
|
||||
<v-btn color="success" prepend-icon="fa-solid fa-plus">{{ $t('Add_Step') }}</v-btn>
|
||||
<v-btn color="success" prepend-icon="fa-solid fa-plus" @click="addStep()">{{ $t('Add_Step') }}</v-btn>
|
||||
<v-btn color="warning" @click="dialogStepManager = true">
|
||||
<v-icon icon="fa-solid fa-arrow-down-1-9"></v-icon>
|
||||
</v-btn>
|
||||
@@ -124,7 +125,7 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {onMounted, PropType, ref, shallowRef} from "vue";
|
||||
import {Recipe, Step} from "@/openapi";
|
||||
import {Ingredient, Recipe, Step} from "@/openapi";
|
||||
import ModelEditorBase from "@/components/model_editors/ModelEditorBase.vue";
|
||||
import {useModelEditorFunctions} from "@/composables/useModelEditorFunctions";
|
||||
import {useI18n} from "vue-i18n";
|
||||
@@ -134,6 +135,7 @@ import {VueDraggable} from "vue-draggable-plus";
|
||||
import PropertiesEditor from "@/components/inputs/PropertiesEditor.vue";
|
||||
import {useFileApi} from "@/composables/useFileApi";
|
||||
import {VFileUpload} from 'vuetify/labs/VFileUpload'
|
||||
import ClosableHelpAlert from "@/components/display/ClosableHelpAlert.vue";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@@ -183,6 +185,16 @@ function deleteImage() {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* add a new step to the recipe
|
||||
*/
|
||||
function addStep(){
|
||||
editingObj.value.steps.push({
|
||||
ingredients: [] as Ingredient[],
|
||||
time: 0,
|
||||
} as Step)
|
||||
}
|
||||
|
||||
/**
|
||||
* called by draggable in step manager dialog when steps are sorted
|
||||
*/
|
||||
@@ -192,6 +204,14 @@ function sortSteps() {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* delete a step at the given index of the steps array of the editingObject
|
||||
* @param index index to delete at
|
||||
*/
|
||||
function deleteStepAtIndex(index: number){
|
||||
editingObj.value.steps.splice(index, 1)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -21,9 +21,10 @@
|
||||
<br/>
|
||||
|
||||
<p class="text-h6 mt-3">{{ $t('DeviceSettings') }}</p>
|
||||
<p class="text-disabled">{{$t('DeviceSettingsHelp')}}</p>
|
||||
<p class="text-disabled">{{ $t('DeviceSettingsHelp') }}</p>
|
||||
|
||||
<v-btn @click="useUserPreferenceStore().resetDeviceSettings()">{{$t('Reset')}}</v-btn>
|
||||
<v-btn @click="useUserPreferenceStore().resetDeviceSettings()" color="warning">{{ $t('Reset') }}</v-btn> <br/>
|
||||
<v-btn @click="useUserPreferenceStore().deviceSettings.general_closedHelpAlerts = []" color="warning" class="mt-1">{{ $t('ResetHelp') }}</v-btn>
|
||||
|
||||
</v-form>
|
||||
</template>
|
||||
@@ -51,9 +52,9 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
|
||||
function save(){
|
||||
function save() {
|
||||
let api = new ApiApi()
|
||||
api.apiUserPartialUpdate({id: user.value.id!, patchedUser: user.value}).then(r => {
|
||||
api.apiUserPartialUpdate({id: user.value.id!, patchedUser: user.value}).then(r => {
|
||||
user.value = r
|
||||
useMessageStore().addPreparedMessage(PreparedMessage.UPDATE_SUCCESS)
|
||||
}).catch(err => {
|
||||
|
||||
Reference in New Issue
Block a user