fix: recipe ingredient editor bugs (#1251)

* filter unallowed fields #1140

* fix type and layout

* propery validate none type quantites

* fix rendering error #1237
This commit is contained in:
Hayden 2022-05-22 11:16:23 -08:00 committed by GitHub
parent d06d4d2fd9
commit cd0da36e7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 22 deletions

View File

@ -20,6 +20,7 @@
class="mx-1"
type="number"
placeholder="Quantity"
@keypress="quantityFilter"
>
<v-icon v-if="$listeners && $listeners.delete" slot="prepend" class="mr-n1 handle">
{{ $globals.icons.arrowUpDown }}
@ -166,7 +167,7 @@ export default defineComponent({
if (state.showTitle) {
value.title = "";
}
state.showTitle = !state.showTitle
state.showTitle = !state.showTitle;
}
function toggleOriginalText() {
@ -211,7 +212,15 @@ export default defineComponent({
return options;
});
function quantityFilter(e: KeyboardEvent) {
// if digit is pressed, add to quantity
if (e.key === "-" || e.key === "+" || e.key === "e") {
e.preventDefault();
}
}
return {
quantityFilter,
toggleOriginalText,
contextMenuOptions,
handleUnitEnter,

View File

@ -10,11 +10,8 @@
<v-divider v-if="showTitleEditor[index]"></v-divider>
<v-list-item dense @click="toggleChecked(index)">
<v-checkbox hide-details :value="checked[index]" class="pt-0 my-auto py-auto" color="secondary" />
<v-list-item-content>
<VueMarkdown
class="ma-0 pa-0 text-subtitle-1 dense-markdown"
:source="parseIngredientText(ingredient, disableAmount, scale)"
/>
<v-list-item-content :key="ingredient.quantity">
<VueMarkdown class="ma-0 pa-0 text-subtitle-1 dense-markdown" :source="ingredientDisplay[index]" />
</v-list-item-content>
</v-list-item>
</div>
@ -58,11 +55,7 @@ export default defineComponent({
});
const ingredientCopyText = computed(() => {
return props.value
.map((ingredient) => {
return `- [ ] ${parseIngredientText(ingredient, props.disableAmount, props.scale)}`;
})
.join("\n");
return ingredientDisplay.value.join("\n");
});
function toggleChecked(index: number) {
@ -71,7 +64,14 @@ export default defineComponent({
state.checked.splice(index, 1, !state.checked[index]);
}
const ingredientDisplay = computed(() => {
return props.value.map((ingredient) => {
return `${parseIngredientText(ingredient, props.disableAmount, props.scale)}`;
});
});
return {
ingredientDisplay,
...toRefs(state),
parseIngredientText,
ingredientCopyText,

View File

@ -5,8 +5,8 @@ const { frac } = useFraction();
function sanitizeIngredientHTML(rawHtml: string) {
return DOMPurify.sanitize(rawHtml, {
"USE_PROFILES": {html: true},
"ALLOWED_TAGS": ["b", "q", "i", "strong", "sup"]
USE_PROFILES: { html: true },
ALLOWED_TAGS: ["b", "q", "i", "strong", "sup"],
});
}
@ -18,7 +18,10 @@ export function parseIngredientText(ingredient: RecipeIngredient, disableAmount:
const { quantity, food, unit, note } = ingredient;
let returnQty = "";
if (quantity !== undefined && quantity !== 0) {
// casting to number is required as sometimes quantity is a string
if (quantity && Number(quantity) !== 0) {
console.log("Using Quantity", quantity, typeof quantity);
if (unit?.fraction) {
const fraction = frac(quantity * scale, 10, true);
if (fraction[0] !== undefined && fraction[0] > 0) {

View File

@ -18,7 +18,7 @@
<div class="icon-container">
<v-divider class="icon-divider"></v-divider>
<v-avatar class="pa-2 icon-avatar" color="primary" size="75">
<svg class="icon-white" style="width: 75;" viewBox="0 0 24 24">
<svg class="icon-white" style="width: 75" viewBox="0 0 24 24">
<path
d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z"
/>
@ -225,14 +225,14 @@
<span class="headline">{{ $t("general.confirm") }}</span>
</v-card-title>
<v-list>
<template v-for="(item, idx) in confirmationData.value">
<template v-for="(item, idx) in confirmationData">
<v-list-item v-if="item.display" :key="idx">
<v-list-item-content>
<v-list-item-title> {{ item.text }} </v-list-item-title>
<v-list-item-subtitle> {{ item.value }} </v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider v-if="idx !== confirmationData.value.length - 1" :key="`divider-${idx}`" />
<v-divider v-if="idx !== confirmationData.length - 1" :key="`divider-${idx}`" />
</template>
</v-list>
@ -263,9 +263,8 @@
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, useRouter, Ref, useContext } from "@nuxtjs/composition-api";
import { defineComponent, onMounted, ref, useRouter, Ref, useContext, computed } from "@nuxtjs/composition-api";
import { useDark } from "@vueuse/core";
import { computed } from "@vue/reactivity";
import { States, RegistrationType, useRegistration } from "./states";
import { useRouteQuery } from "~/composables/use-router";
import { validators, useAsyncValidator } from "~/composables/use-validators";
@ -285,7 +284,7 @@ const inputAttrs = {
};
export default defineComponent({
layout: "basic",
layout: "blank",
setup() {
const { i18n } = useContext();

View File

@ -4,7 +4,7 @@ import enum
from typing import Optional, Union
from uuid import UUID, uuid4
from pydantic import UUID4, Field
from pydantic import UUID4, Field, validator
from mealie.schema._mealie import MealieModel
from mealie.schema._mealie.types import NoneFloat
@ -53,7 +53,7 @@ class RecipeIngredient(MealieModel):
unit: Optional[Union[IngredientUnit, CreateIngredientUnit]]
food: Optional[Union[IngredientFood, CreateIngredientFood]]
disable_amount: bool = True
quantity: float = 1
quantity: NoneFloat = 1
original_text: Optional[str]
# Ref is used as a way to distinguish between an individual ingredient on the frontend
@ -64,6 +64,20 @@ class RecipeIngredient(MealieModel):
class Config:
orm_mode = True
@validator("quantity", pre=True)
@classmethod
def validate_quantity(cls, value, values) -> NoneFloat:
"""
Sometimes the frontend UI will provide an emptry string as a "null" value because of the default
bindings in Vue. This validator will ensure that the quantity is set to None if the value is an
empty string.
"""
if isinstance(value, float):
return value
if value is None or value == "":
return None
return value
class IngredientConfidence(MealieModel):
average: NoneFloat = None