mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-05 09:55:17 -05:00
feat: plural foods and units, and aliases (#2674)
* added plural names and alias tables to foods/units
* updated models to include plural names and aliases
* updated parser to include plural and aliases
* fixed migrations
* fixed recursive models
* added plural abbreviation to migration
* updated parser and display prop
* update displays to use plurals
* fix display edgecase and remove print
* added/updated display tests
* fixed model bug and added parser tests
* added tests for aliases
* added new plural options to data management page
* removed unique constraint
* made base dialog more customizable
* added alias management to food and unit data pages
* removed unused awaits
* 🧹
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div>
|
||||
<BaseDialog
|
||||
v-model="dialog"
|
||||
:title="$t('data-pages.manage-aliases')"
|
||||
:icon="$globals.icons.edit"
|
||||
:submit-icon="$globals.icons.check"
|
||||
:submit-text="$tc('general.confirm')"
|
||||
@submit="saveAliases"
|
||||
@cancel="$emit('cancel')"
|
||||
>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-row v-for="alias, i in aliases" :key="i">
|
||||
<v-col cols="10">
|
||||
<v-text-field
|
||||
v-model="alias.name"
|
||||
:label="$t('general.name')"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="2">
|
||||
<BaseButtonGroup
|
||||
:buttons="[
|
||||
{
|
||||
icon: $globals.icons.delete,
|
||||
text: $tc('general.delete'),
|
||||
event: 'delete'
|
||||
}
|
||||
]"
|
||||
@delete="deleteAlias(i)"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
<template #custom-card-action>
|
||||
<BaseButton edit @click="createAlias">{{ $t('data-pages.create-alias') }}
|
||||
<template #icon>
|
||||
{{ $globals.icons.create }}
|
||||
</template>
|
||||
</BaseButton>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from "@nuxtjs/composition-api";
|
||||
import { whenever } from "@vueuse/core";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { IngredientFood, IngredientUnit } from "~/lib/api/types/recipe";
|
||||
|
||||
export interface GenericAlias {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
data: {
|
||||
type: Object as () => IngredientFood | IngredientUnit,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
// V-Model Support
|
||||
const dialog = computed({
|
||||
get: () => {
|
||||
return props.value;
|
||||
},
|
||||
set: (val) => {
|
||||
context.emit("input", val);
|
||||
},
|
||||
});
|
||||
|
||||
function createAlias() {
|
||||
aliases.value.push({
|
||||
"name": "",
|
||||
})
|
||||
}
|
||||
|
||||
function deleteAlias(index: number) {
|
||||
aliases.value.splice(index, 1);
|
||||
}
|
||||
|
||||
const aliases = ref<GenericAlias[]>(props.data.aliases || []);
|
||||
function initAliases() {
|
||||
aliases.value = [...props.data.aliases || []];
|
||||
if (!aliases.value.length) {
|
||||
createAlias();
|
||||
}
|
||||
}
|
||||
|
||||
initAliases();
|
||||
whenever(
|
||||
() => props.value,
|
||||
() => {
|
||||
initAliases();
|
||||
},
|
||||
)
|
||||
|
||||
function saveAliases() {
|
||||
const seenAliasNames: string[] = [];
|
||||
const keepAliases: GenericAlias[] = [];
|
||||
aliases.value.forEach((alias) => {
|
||||
if (
|
||||
!alias.name
|
||||
|| alias.name === props.data.name
|
||||
|| alias.name === props.data.pluralName
|
||||
// @ts-ignore only applies to units
|
||||
|| alias.name === props.data.abbreviation
|
||||
// @ts-ignore only applies to units
|
||||
|| alias.name === props.data.pluralAbbreviation
|
||||
|| seenAliasNames.includes(alias.name)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
keepAliases.push(alias);
|
||||
seenAliasNames.push(alias.name);
|
||||
})
|
||||
|
||||
aliases.value = keepAliases;
|
||||
context.emit("submit", keepAliases);
|
||||
}
|
||||
|
||||
return {
|
||||
aliases,
|
||||
createAlias,
|
||||
dialog,
|
||||
deleteAlias,
|
||||
saveAliases,
|
||||
validators,
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -58,8 +58,12 @@
|
||||
</template>
|
||||
{{ $t("general.confirm") }}
|
||||
</BaseButton>
|
||||
<slot name="custom-card-action"></slot>
|
||||
<BaseButton v-if="$listeners.submit" type="submit" @click="submitEvent">
|
||||
{{ submitText }}
|
||||
<template v-if="submitIcon" #icon>
|
||||
{{ submitIcon }}
|
||||
</template>
|
||||
</BaseButton>
|
||||
</slot>
|
||||
</v-card-actions>
|
||||
@@ -109,6 +113,10 @@ export default defineComponent({
|
||||
default: null,
|
||||
type: Boolean,
|
||||
},
|
||||
submitIcon: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
submitText: {
|
||||
type: String,
|
||||
default: function () {
|
||||
|
||||
@@ -48,4 +48,74 @@ describe(parseIngredientText.name, () => {
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).not.toContain("<script>");
|
||||
});
|
||||
|
||||
test("plural test : plural qty : use abbreviation", () => {
|
||||
const ingredient = createRecipeIngredient({
|
||||
quantity: 2,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: true },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("2 tbsps diced onions");
|
||||
});
|
||||
|
||||
test("plural test : plural qty : not abbreviation", () => {
|
||||
const ingredient = createRecipeIngredient({
|
||||
quantity: 2,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("2 tablespoons diced onions");
|
||||
});
|
||||
|
||||
test("plural test : single qty : use abbreviation", () => {
|
||||
const ingredient = createRecipeIngredient({
|
||||
quantity: 1,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: true },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("1 tbsp diced onion");
|
||||
});
|
||||
|
||||
test("plural test : single qty : not abbreviation", () => {
|
||||
const ingredient = createRecipeIngredient({
|
||||
quantity: 1,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("1 tablespoon diced onion");
|
||||
});
|
||||
|
||||
test("plural test : small qty : use abbreviation", () => {
|
||||
const ingredient = createRecipeIngredient({
|
||||
quantity: 0.5,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: true },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("0.5 tbsp diced onion");
|
||||
});
|
||||
|
||||
test("plural test : small qty : not abbreviation", () => {
|
||||
const ingredient = createRecipeIngredient({
|
||||
quantity: 0.5,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("0.5 tablespoon diced onion");
|
||||
});
|
||||
|
||||
test("plural test : zero qty", () => {
|
||||
const ingredient = createRecipeIngredient({
|
||||
quantity: 0,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("diced onions");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
import { useFraction } from "./use-fraction";
|
||||
import { RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
import { CreateIngredientFood, CreateIngredientUnit, IngredientFood, IngredientUnit, RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
const { frac } = useFraction();
|
||||
|
||||
export function sanitizeIngredientHTML(rawHtml: string) {
|
||||
@@ -10,6 +10,31 @@ export function sanitizeIngredientHTML(rawHtml: string) {
|
||||
});
|
||||
}
|
||||
|
||||
function useFoodName(food: CreateIngredientFood | IngredientFood | undefined, usePlural: boolean) {
|
||||
if (!food) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return (usePlural ? food.pluralName || food.name : food.name) || "";
|
||||
}
|
||||
|
||||
function useUnitName(unit: CreateIngredientUnit | IngredientUnit | undefined, usePlural: boolean) {
|
||||
if (!unit) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let returnVal = "";
|
||||
if (unit.useAbbreviation) {
|
||||
returnVal = (usePlural ? unit.pluralAbbreviation || unit.abbreviation : unit.abbreviation) || "";
|
||||
}
|
||||
|
||||
if (!returnVal) {
|
||||
returnVal = (usePlural ? unit.pluralName || unit.name : unit.name) || "";
|
||||
}
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmount: boolean, scale = 1, includeFormating = true) {
|
||||
if (disableAmount) {
|
||||
return {
|
||||
@@ -21,11 +46,11 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
||||
}
|
||||
|
||||
const { quantity, food, unit, note } = ingredient;
|
||||
const usePluralUnit = quantity !== undefined && quantity > 1;
|
||||
const usePluralFood = (!quantity) || quantity > 1
|
||||
|
||||
let returnQty = "";
|
||||
|
||||
let unitDisplay = unit?.name;
|
||||
|
||||
// casting to number is required as sometimes quantity is a string
|
||||
if (quantity && Number(quantity) !== 0) {
|
||||
if (unit?.fraction) {
|
||||
@@ -42,16 +67,15 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
||||
} else {
|
||||
returnQty = (quantity * scale).toString();
|
||||
}
|
||||
|
||||
if (unit?.useAbbreviation && unit.abbreviation) {
|
||||
unitDisplay = unit.abbreviation;
|
||||
}
|
||||
}
|
||||
|
||||
const unitName = useUnitName(unit, usePluralUnit);
|
||||
const foodName = useFoodName(food, usePluralFood);
|
||||
|
||||
return {
|
||||
quantity: returnQty ? sanitizeIngredientHTML(returnQty) : undefined,
|
||||
unit: unitDisplay ? sanitizeIngredientHTML(unitDisplay) : undefined,
|
||||
name: food?.name ? sanitizeIngredientHTML(food.name) : undefined,
|
||||
unit: unitName && quantity ? sanitizeIngredientHTML(unitName) : undefined,
|
||||
name: foodName ? sanitizeIngredientHTML(foodName) : undefined,
|
||||
note: note ? sanitizeIngredientHTML(note) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@
|
||||
"no-recipe-found": "No Recipe Found",
|
||||
"ok": "OK",
|
||||
"options": "Options:",
|
||||
"plural-name": "Plural Name",
|
||||
"print": "Print",
|
||||
"print-preferences": "Print Preferences",
|
||||
"random": "Random",
|
||||
@@ -888,7 +889,9 @@
|
||||
"create-food": "Create Food",
|
||||
"food-label": "Food Label",
|
||||
"edit-food": "Edit Food",
|
||||
"food-data": "Food Data"
|
||||
"food-data": "Food Data",
|
||||
"example-food-singular": "ex: Onion",
|
||||
"example-food-plural": "ex: Onions"
|
||||
},
|
||||
"units": {
|
||||
"seed-dialog-text": "Seed the database with common units based on your local language.",
|
||||
@@ -899,13 +902,18 @@
|
||||
"merging-unit-into-unit": "Merging {0} into {1}",
|
||||
"create-unit": "Create Unit",
|
||||
"abbreviation": "Abbreviation",
|
||||
"plural-abbreviation": "Plural Abbreviation",
|
||||
"description": "Description",
|
||||
"display-as-fraction": "Display as Fraction",
|
||||
"use-abbreviation": "Use Abbreviation",
|
||||
"edit-unit": "Edit Unit",
|
||||
"unit-data": "Unit Data",
|
||||
"use-abbv": "Use Abbv.",
|
||||
"fraction": "Fraction"
|
||||
"fraction": "Fraction",
|
||||
"example-unit-singular": "ex: Tablespoon",
|
||||
"example-unit-plural": "ex: Tablespoons",
|
||||
"example-unit-abbreviation-singular": "ex: Tbsp",
|
||||
"example-unit-abbreviation-plural": "ex: Tbsps"
|
||||
},
|
||||
"labels": {
|
||||
"seed-dialog-text": "Seed the database with common labels based on your local language.",
|
||||
@@ -934,6 +942,8 @@
|
||||
"delete-recipes": "Delete Recipes",
|
||||
"source-unit-will-be-deleted": "Source Unit will be deleted"
|
||||
},
|
||||
"create-alias": "Create Alias",
|
||||
"manage-aliases": "Manage Aliases",
|
||||
"seed-data": "Seed Data",
|
||||
"seed": "Seed",
|
||||
"data-management": "Data Management",
|
||||
|
||||
@@ -56,21 +56,32 @@ export interface CategorySave {
|
||||
}
|
||||
export interface CreateIngredientFood {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
}
|
||||
export interface CreateIngredientFoodAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: CreateIngredientUnitAlias[];
|
||||
}
|
||||
export interface CreateIngredientUnitAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface CreateRecipe {
|
||||
name: string;
|
||||
@@ -113,16 +124,21 @@ export interface IngredientConfidence {
|
||||
}
|
||||
export interface IngredientFood {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: IngredientFoodAlias[];
|
||||
id: string;
|
||||
label?: MultiPurposeLabelSummary;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface IngredientFoodAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface MultiPurposeLabelSummary {
|
||||
name: string;
|
||||
color?: string;
|
||||
@@ -141,17 +157,23 @@ export interface IngredientRequest {
|
||||
}
|
||||
export interface IngredientUnit {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: IngredientUnitAlias[];
|
||||
id: string;
|
||||
createdAt?: string;
|
||||
updateAt?: string;
|
||||
}
|
||||
export interface IngredientUnitAlias {
|
||||
name: string;
|
||||
}
|
||||
export interface IngredientsRequest {
|
||||
parser?: RegisteredParser & string;
|
||||
ingredients: string[];
|
||||
@@ -206,7 +228,7 @@ export interface Recipe {
|
||||
recipeCategory?: RecipeCategory[];
|
||||
tags?: RecipeTag[];
|
||||
tools?: RecipeTool[];
|
||||
rating?: number | null;
|
||||
rating?: number;
|
||||
orgURL?: string;
|
||||
dateAdded?: string;
|
||||
dateUpdated?: string;
|
||||
@@ -413,22 +435,27 @@ export interface RecipeZipTokenResponse {
|
||||
}
|
||||
export interface SaveIngredientFood {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
labelId?: string;
|
||||
aliases?: CreateIngredientFoodAlias[];
|
||||
groupId: string;
|
||||
}
|
||||
export interface SaveIngredientUnit {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
pluralAbbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
aliases?: CreateIngredientUnitAlias[];
|
||||
groupId: string;
|
||||
}
|
||||
export interface ScrapeRecipe {
|
||||
@@ -438,7 +465,7 @@ export interface ScrapeRecipe {
|
||||
export interface ScrapeRecipeTest {
|
||||
url: string;
|
||||
}
|
||||
export interface SlugResponse {}
|
||||
export interface SlugResponse { }
|
||||
export interface TagIn {
|
||||
name: string;
|
||||
}
|
||||
@@ -454,6 +481,7 @@ export interface TagSave {
|
||||
}
|
||||
export interface UnitFoodBase {
|
||||
name: string;
|
||||
pluralName?: string;
|
||||
description?: string;
|
||||
extras?: {
|
||||
[k: string]: unknown;
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
v-model="createDialog"
|
||||
:icon="$globals.icons.foods"
|
||||
:title="$t('data-pages.foods.create-food')"
|
||||
:submit-icon="$globals.icons.save"
|
||||
:submit-text="$tc('general.save')"
|
||||
@submit="createFood"
|
||||
>
|
||||
@@ -68,8 +69,14 @@
|
||||
v-model="createTarget.name"
|
||||
autofocus
|
||||
:label="$t('general.name')"
|
||||
:hint="$t('data-pages.foods.example-food-singular')"
|
||||
:rules="[validators.required]"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="createTarget.pluralName"
|
||||
:label="$t('general.plural-name')"
|
||||
:hint="$t('data-pages.foods.example-food-plural')"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="createTarget.description" :label="$t('recipe.description')"></v-text-field>
|
||||
<v-autocomplete
|
||||
v-model="createTarget.labelId"
|
||||
@@ -83,18 +90,41 @@
|
||||
</v-form> </v-card-text
|
||||
></BaseDialog>
|
||||
|
||||
<!-- Alias Sub-Dialog -->
|
||||
<RecipeDataAliasManagerDialog
|
||||
v-if="editTarget"
|
||||
:value="aliasManagerDialog"
|
||||
:data="editTarget"
|
||||
@submit="updateFoodAlias"
|
||||
@cancel="aliasManagerDialog = false"
|
||||
/>
|
||||
|
||||
<!-- Edit Dialog -->
|
||||
<BaseDialog
|
||||
v-model="editDialog"
|
||||
:icon="$globals.icons.foods"
|
||||
:title="$t('data-pages.foods.edit-food')"
|
||||
:submit-icon="$globals.icons.save"
|
||||
:submit-text="$tc('general.save')"
|
||||
@submit="editSaveFood"
|
||||
>
|
||||
<v-card-text v-if="editTarget">
|
||||
<v-form ref="domEditFoodForm">
|
||||
<v-text-field v-model="editTarget.name" :label="$t('general.name')" :rules="[validators.required]"></v-text-field>
|
||||
<v-text-field v-model="editTarget.description" :label="$t('recipe.description')"></v-text-field>
|
||||
<v-text-field
|
||||
v-model="editTarget.name"
|
||||
:label="$t('general.name')"
|
||||
:hint="$t('data-pages.foods.example-food-singular')"
|
||||
:rules="[validators.required]"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="editTarget.pluralName"
|
||||
:label="$t('general.plural-name')"
|
||||
:hint="$t('data-pages.foods.example-food-plural')"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="editTarget.description"
|
||||
:label="$t('recipe.description')"
|
||||
></v-text-field>
|
||||
<v-autocomplete
|
||||
v-model="editTarget.labelId"
|
||||
clearable
|
||||
@@ -104,8 +134,12 @@
|
||||
:label="$t('data-pages.foods.food-label')"
|
||||
>
|
||||
</v-autocomplete>
|
||||
</v-form> </v-card-text
|
||||
></BaseDialog>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<template #custom-card-action>
|
||||
<BaseButton edit @click="aliasManagerEventHandler">{{ $t('data-pages.manage-aliases') }}</BaseButton>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Delete Dialog -->
|
||||
<BaseDialog
|
||||
@@ -156,16 +190,18 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, computed, useContext } from "@nuxtjs/composition-api";
|
||||
import type { LocaleObject } from "@nuxtjs/i18n";
|
||||
import RecipeDataAliasManagerDialog from "~/components/Domain/Recipe/RecipeDataAliasManagerDialog.vue";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { CreateIngredientFood, IngredientFood } from "~/lib/api/types/recipe";
|
||||
import { CreateIngredientFood, IngredientFood, IngredientFoodAlias } from "~/lib/api/types/recipe";
|
||||
import MultiPurposeLabel from "~/components/Domain/ShoppingList/MultiPurposeLabel.vue";
|
||||
import { useLocales } from "~/composables/use-locales";
|
||||
import { useFoodStore, useLabelStore } from "~/composables/store";
|
||||
import { VForm } from "~/types/vuetify";
|
||||
|
||||
export default defineComponent({
|
||||
components: { MultiPurposeLabel },
|
||||
|
||||
components: { MultiPurposeLabel, RecipeDataAliasManagerDialog },
|
||||
setup() {
|
||||
const userApi = useUserApi();
|
||||
const { i18n } = useContext();
|
||||
@@ -184,6 +220,11 @@ export default defineComponent({
|
||||
value: "name",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.tc("general.plural-name"),
|
||||
value: "pluralName",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.tc("recipe.description"),
|
||||
value: "description",
|
||||
@@ -264,6 +305,22 @@ export default defineComponent({
|
||||
deleteDialog.value = false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Alias Manager
|
||||
|
||||
const aliasManagerDialog = ref(false);
|
||||
function aliasManagerEventHandler() {
|
||||
aliasManagerDialog.value = true;
|
||||
}
|
||||
|
||||
function updateFoodAlias(newAliases: IngredientFoodAlias[]) {
|
||||
if (!editTarget.value) {
|
||||
return;
|
||||
}
|
||||
editTarget.value.aliases = newAliases;
|
||||
aliasManagerDialog.value = false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Merge Foods
|
||||
|
||||
@@ -337,6 +394,10 @@ export default defineComponent({
|
||||
deleteEventHandler,
|
||||
deleteDialog,
|
||||
deleteFood,
|
||||
// Alias Manager
|
||||
aliasManagerDialog,
|
||||
aliasManagerEventHandler,
|
||||
updateFoodAlias,
|
||||
// Merge
|
||||
canMerge,
|
||||
mergeFoods,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
v-model="createDialog"
|
||||
:icon="$globals.icons.units"
|
||||
:title="$t('data-pages.units.create-unit')"
|
||||
:submit-icon="$globals.icons.save"
|
||||
:submit-text="$tc('general.save')"
|
||||
@submit="createUnit"
|
||||
>
|
||||
@@ -38,9 +39,24 @@
|
||||
v-model="createTarget.name"
|
||||
autofocus
|
||||
:label="$t('general.name')"
|
||||
:hint="$t('data-pages.units.example-unit-singular')"
|
||||
:rules="[validators.required]"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="createTarget.abbreviation" :label="$t('data-pages.units.abbreviation')"></v-text-field>
|
||||
<v-text-field
|
||||
v-model="createTarget.pluralName"
|
||||
:label="$t('general.plural-name')"
|
||||
:hint="$t('data-pages.units.example-unit-plural')"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="createTarget.abbreviation"
|
||||
:label="$t('data-pages.units.abbreviation')"
|
||||
:hint="$t('data-pages.units.example-unit-abbreviation-singular')"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="createTarget.pluralAbbreviation"
|
||||
:label="$t('data-pages.units.plural-abbreviation')"
|
||||
:hint="$t('data-pages.units.example-unit-abbreviation-plural')"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="createTarget.description" :label="$t('data-pages.units.description')"></v-text-field>
|
||||
<v-checkbox v-model="createTarget.fraction" hide-details :label="$t('data-pages.units.display-as-fraction')"></v-checkbox>
|
||||
<v-checkbox v-model="createTarget.useAbbreviation" hide-details :label="$t('data-pages.units.use-abbreviation')"></v-checkbox>
|
||||
@@ -48,23 +64,55 @@
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Alias Sub-Dialog -->
|
||||
<RecipeDataAliasManagerDialog
|
||||
v-if="editTarget"
|
||||
:value="aliasManagerDialog"
|
||||
:data="editTarget"
|
||||
@submit="updateUnitAlias"
|
||||
@cancel="aliasManagerDialog = false"
|
||||
/>
|
||||
|
||||
<!-- Edit Dialog -->
|
||||
<BaseDialog
|
||||
v-model="editDialog"
|
||||
:icon="$globals.icons.units"
|
||||
:title="$t('data-pages.units.edit-unit')"
|
||||
:submit-icon="$globals.icons.save"
|
||||
:submit-text="$tc('general.save')"
|
||||
@submit="editSaveUnit"
|
||||
>
|
||||
<v-card-text v-if="editTarget">
|
||||
<v-form ref="domEditUnitForm">
|
||||
<v-text-field v-model="editTarget.name" :label="$t('general.name')" :rules="[validators.required]"></v-text-field>
|
||||
<v-text-field v-model="editTarget.abbreviation" :label="$t('data-pages.units.abbreviation')"></v-text-field>
|
||||
<v-text-field
|
||||
v-model="editTarget.name"
|
||||
:label="$t('general.name')"
|
||||
:hint="$t('data-pages.units.example-unit-singular')"
|
||||
:rules="[validators.required]"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="editTarget.pluralName"
|
||||
:label="$t('general.plural-name')"
|
||||
:hint="$t('data-pages.units.example-unit-plural')"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="editTarget.abbreviation"
|
||||
:label="$t('data-pages.units.abbreviation')"
|
||||
:hint="$t('data-pages.units.example-unit-abbreviation-singular')"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="editTarget.pluralAbbreviation"
|
||||
:label="$t('data-pages.units.plural-abbreviation')"
|
||||
:hint="$t('data-pages.units.example-unit-abbreviation-plural')"
|
||||
></v-text-field>
|
||||
<v-text-field v-model="editTarget.description" :label="$t('data-pages.units.description')"></v-text-field>
|
||||
<v-checkbox v-model="editTarget.fraction" hide-details :label="$t('data-pages.units.display-as-fraction')"></v-checkbox>
|
||||
<v-checkbox v-model="editTarget.useAbbreviation" hide-details :label="$t('data-pages.units.use-abbreviation')"></v-checkbox>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<template #custom-card-action>
|
||||
<BaseButton edit @click="aliasManagerEventHandler">{{ $t('data-pages.manage-aliases') }}</BaseButton>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Delete Dialog -->
|
||||
@@ -159,14 +207,16 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import type { LocaleObject } from "@nuxtjs/i18n";
|
||||
import RecipeDataAliasManagerDialog from "~/components/Domain/Recipe/RecipeDataAliasManagerDialog.vue";
|
||||
import { validators } from "~/composables/use-validators";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { CreateIngredientUnit, IngredientUnit } from "~/lib/api/types/recipe";
|
||||
import { CreateIngredientUnit, IngredientUnit, IngredientUnitAlias } from "~/lib/api/types/recipe";
|
||||
import { useLocales } from "~/composables/use-locales";
|
||||
import { useUnitStore } from "~/composables/store";
|
||||
import { VForm } from "~/types/vuetify";
|
||||
|
||||
export default defineComponent({
|
||||
components: { RecipeDataAliasManagerDialog },
|
||||
setup() {
|
||||
const userApi = useUserApi();
|
||||
const { i18n } = useContext();
|
||||
@@ -185,11 +235,21 @@ export default defineComponent({
|
||||
value: "name",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.t("general.plural-name"),
|
||||
value: "pluralName",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.t("data-pages.units.abbreviation"),
|
||||
value: "abbreviation",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.t("data-pages.units.plural-abbreviation"),
|
||||
value: "pluralAbbreviation",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: i18n.t("data-pages.units.use-abbv"),
|
||||
value: "useAbbreviation",
|
||||
@@ -278,6 +338,22 @@ export default defineComponent({
|
||||
deleteDialog.value = false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Alias Manager
|
||||
|
||||
const aliasManagerDialog = ref(false);
|
||||
function aliasManagerEventHandler() {
|
||||
aliasManagerDialog.value = true;
|
||||
}
|
||||
|
||||
function updateUnitAlias(newAliases: IngredientUnitAlias[]) {
|
||||
if (!editTarget.value) {
|
||||
return;
|
||||
}
|
||||
editTarget.value.aliases = newAliases;
|
||||
aliasManagerDialog.value = false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Merge Units
|
||||
|
||||
@@ -345,13 +421,16 @@ export default defineComponent({
|
||||
deleteEventHandler,
|
||||
deleteDialog,
|
||||
deleteUnit,
|
||||
// Alias Manager
|
||||
aliasManagerDialog,
|
||||
aliasManagerEventHandler,
|
||||
updateUnitAlias,
|
||||
// Merge
|
||||
canMerge,
|
||||
mergeUnits,
|
||||
mergeDialog,
|
||||
fromUnit,
|
||||
toUnit,
|
||||
|
||||
// Seed
|
||||
seedDatabase,
|
||||
locales,
|
||||
|
||||
Reference in New Issue
Block a user