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:
Michael Genson
2023-11-14 09:39:07 -06:00
committed by GitHub
parent 4b55b838ed
commit d440d51ffe
17 changed files with 1181 additions and 104 deletions

View File

@@ -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>

View File

@@ -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 () {

View File

@@ -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");
});
});

View File

@@ -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,
};
}

View File

@@ -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",

View File

@@ -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;

View File

@@ -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,

View File

@@ -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,