1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-03 04:25:24 +02:00

feat(backend): add rename tag, tool, category support (#875)

This commit is contained in:
Hayden 2021-12-10 19:48:06 -09:00 committed by GitHub
parent 8d77f4b31e
commit e109ac0f47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 573 additions and 163 deletions

View file

@ -6,12 +6,54 @@
:title="category.name"
:recipes="category.recipes"
@sort="assignSorted"
></RecipeCardSection>
>
<template #title>
<v-btn icon class="mr-1">
<v-icon dark large @click="reset">
{{ $globals.icons.tags }}
</v-icon>
</v-btn>
<template v-if="edit">
<v-text-field
v-model="category.name"
autofocus
single-line
dense
hide-details
class="headline"
@keyup.enter="updateCategory"
>
</v-text-field>
<v-btn icon @click="updateCategory">
<v-icon size="28">
{{ $globals.icons.save }}
</v-icon>
</v-btn>
<v-btn icon class="mr-1" @click="reset">
<v-icon size="28">
{{ $globals.icons.close }}
</v-icon>
</v-btn>
</template>
<template v-else>
<v-tooltip top>
<template #activator="{ on, attrs }">
<v-toolbar-title v-bind="attrs" style="cursor: pointer" class="headline" v-on="on" @click="edit = true">
{{ category.name }}
</v-toolbar-title>
</template>
<span> Click to Edit </span>
</v-tooltip>
</template>
</template>
</RecipeCardSection>
</v-container>
</template>
<script lang="ts">
import { defineComponent, useAsync, useRoute } from "@nuxtjs/composition-api";
import { defineComponent, useAsync, useRoute, reactive, toRefs, useRouter } from "@nuxtjs/composition-api";
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
import { useUserApi } from "~/composables/api";
import { Recipe } from "~/types/api-types/recipe";
@ -21,13 +63,49 @@ export default defineComponent({
setup() {
const api = useUserApi();
const route = useRoute();
const router = useRouter();
const slug = route.value.params.slug;
const state = reactive({
initialValue: "",
edit: false,
});
const category = useAsync(async () => {
const { data } = await api.categories.getOne(slug);
if (data) {
state.initialValue = data.name;
}
return data;
}, slug);
return { category };
function reset() {
state.edit = false;
if (category.value) {
category.value.name = state.initialValue;
}
}
async function updateCategory() {
state.edit = false;
if (!category.value) {
return;
}
const { data } = await api.categories.updateOne(category.value.slug, category.value);
if (data) {
router.push("/recipes/categories/" + data.slug);
}
}
return {
category,
reset,
...toRefs(state),
updateCategory,
};
},
head() {
return {
@ -44,6 +122,4 @@ export default defineComponent({
},
});
</script>
<style scoped>
</style>

View file

@ -1,71 +1,35 @@
<template>
<v-container v-if="categories">
<v-app-bar color="transparent" flat class="mt-n1 rounded">
<v-icon large left>
{{ $globals.icons.tags }}
</v-icon>
<v-toolbar-title class="headline"> {{ $t("recipe.categories") }} </v-toolbar-title>
<v-spacer></v-spacer>
</v-app-bar>
<section v-for="(items, key, idx) in categoriesByLetter" :key="'header' + idx" :class="idx === 1 ? null : 'my-4'">
<BaseCardSectionTitle :title="key"> </BaseCardSectionTitle>
<v-row>
<v-col v-for="(item, index) in items" :key="'cat' + index" cols="12" :sm="12" :md="6" :lg="4" :xl="3">
<v-card hover :to="`/recipes/categories/${item.slug}`">
<v-card-actions>
<v-icon>
{{ $globals.icons.tags }}
</v-icon>
<v-card-title class="py-1">{{ item.name }}</v-card-title>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</section>
<v-container>
<RecipeCategoryTagToolPage v-if="categories" :items="categories" item-type="categories" />
</v-container>
</template>
<script lang="ts">
import { computed, defineComponent, useAsync } from "@nuxtjs/composition-api";
import { defineComponent, useAsync } from "@nuxtjs/composition-api";
import RecipeCategoryTagToolPage from "~/components/Domain/Recipe/RecipeCategoryTagToolPage.vue";
import { useUserApi } from "~/composables/api";
import { useAsyncKey } from "~/composables/use-utils";
export default defineComponent({
components: {
RecipeCategoryTagToolPage,
},
setup() {
const api = useUserApi();
const userApi = useUserApi();
const categories = useAsync(async () => {
const { data } = await api.categories.getAll();
return data;
}, useAsyncKey());
const { data } = await userApi.categories.getAll();
const categoriesByLetter: any = computed(() => {
const catsByLetter: { [key: string]: Array<any> } = {};
if (!categories.value) return catsByLetter;
categories.value.forEach((item) => {
const letter = item.name[0].toUpperCase();
if (!catsByLetter[letter]) {
catsByLetter[letter] = [];
}
catsByLetter[letter].push(item);
});
return catsByLetter;
if (data) {
return data;
}
});
return { categories, api, categoriesByLetter };
},
head() {
return {
title: this.$t("sidebar.categories") as string,
categories,
};
},
// head: {
// // @ts-ignore
// title: this.$t("sidebar.categories") as string,
// },
});
</script>
<style scoped>
</style>
</script>

View file

@ -1,49 +1,125 @@
<template>
<v-container>
<RecipeCardSection
v-if="tag"
v-if="tags"
:icon="$globals.icons.tags"
:title="tag.name"
:recipes="tag.recipes"
:title="tags.name"
:recipes="tags.recipes"
@sort="assignSorted"
></RecipeCardSection>
>
<template #title>
<v-btn icon class="mr-1">
<v-icon dark large @click="reset">
{{ $globals.icons.tags }}
</v-icon>
</v-btn>
<template v-if="edit">
<v-text-field
v-model="tags.name"
autofocus
single-line
dense
hide-details
class="headline"
@keyup.enter="updateTags"
>
</v-text-field>
<v-btn icon @click="updateTags">
<v-icon size="28">
{{ $globals.icons.save }}
</v-icon>
</v-btn>
<v-btn icon class="mr-1" @click="reset">
<v-icon size="28">
{{ $globals.icons.close }}
</v-icon>
</v-btn>
</template>
<template v-else>
<v-tooltip top>
<template #activator="{ on, attrs }">
<v-toolbar-title v-bind="attrs" style="cursor: pointer" class="headline" v-on="on" @click="edit = true">
{{ tags.name }}
</v-toolbar-title>
</template>
<span> Click to Edit </span>
</v-tooltip>
</template>
</template>
</RecipeCardSection>
</v-container>
</template>
<script lang="ts">
import { defineComponent, useAsync, useRoute } from "@nuxtjs/composition-api";
import { defineComponent, useAsync, useRoute, reactive, toRefs, useRouter } from "@nuxtjs/composition-api";
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
import { useUserApi } from "~/composables/api";
import { Recipe } from "~/types/api-types/admin";
import { Recipe } from "~/types/api-types/recipe";
export default defineComponent({
components: { RecipeCardSection },
setup() {
const api = useUserApi();
const route = useRoute();
const router = useRouter();
const slug = route.value.params.slug;
const tag = useAsync(async () => {
const state = reactive({
initialValue: "",
edit: false,
});
const tags = useAsync(async () => {
const { data } = await api.tags.getOne(slug);
if (data) {
state.initialValue = data.name;
}
return data;
}, slug);
return { tag };
function reset() {
state.edit = false;
if (tags.value) {
tags.value.name = state.initialValue;
}
}
async function updateTags() {
state.edit = false;
if (!tags.value) {
return;
}
const { data } = await api.tags.updateOne(tags.value.slug, tags.value);
if (data) {
router.push("/recipes/tags/" + data.slug);
}
}
return {
tags,
reset,
...toRefs(state),
updateTags,
};
},
head() {
return {
title: this.$t("sidebar.tags") as string,
title: this.$t("sidebar.categories") as string,
};
},
methods: {
assignSorted(val: Array<Recipe>) {
if (this.tag) {
if (this.tags) {
// @ts-ignore
this.tag.recipes = val;
this.tags.recipes = val;
}
},
},
});
</script>
<style scoped>
</style>

View file

@ -1,71 +1,34 @@
<template>
<v-container v-if="tags">
<v-app-bar color="transparent" flat class="mt-n1 rounded">
<v-icon large left>
{{ $globals.icons.tags }}
</v-icon>
<v-toolbar-title class="headline"> {{ $t("tag.tags") }} </v-toolbar-title>
<v-spacer></v-spacer>
</v-app-bar>
<section v-for="(items, key, idx) in tagsByLetter" :key="'header' + idx" :class="idx === 1 ? null : 'my-4'">
<BaseCardSectionTitle :title="key"> </BaseCardSectionTitle>
<v-row>
<v-col v-for="(item, index) in items" :key="'cat' + index" cols="12" :sm="12" :md="6" :lg="4" :xl="3">
<v-card hover :to="`/recipes/tags/${item.slug}`">
<v-card-actions>
<v-icon>
{{ $globals.icons.tags }}
</v-icon>
<v-card-title class="py-1">{{ item.name }}</v-card-title>
<v-spacer></v-spacer>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</section>
<v-container>
<RecipeCategoryTagToolPage v-if="tools" :items="tools" item-type="tags" />
</v-container>
</template>
<script lang="ts">
import { defineComponent, useAsync, computed } from "@nuxtjs/composition-api";
import { defineComponent, useAsync } from "@nuxtjs/composition-api";
import RecipeCategoryTagToolPage from "~/components/Domain/Recipe/RecipeCategoryTagToolPage.vue";
import { useUserApi } from "~/composables/api";
import { useAsyncKey } from "~/composables/use-utils";
export default defineComponent({
setup() {
const api = useUserApi();
const tags = useAsync(async () => {
const { data } = await api.tags.getAll();
return data;
}, useAsyncKey());
const tagsByLetter: any = computed(() => {
const tagsByLetter: { [key: string]: Array<any> } = {};
if (!tags.value) return tagsByLetter;
tags.value.forEach((item) => {
const letter = item.name[0].toUpperCase();
if (!tagsByLetter[letter]) {
tagsByLetter[letter] = [];
}
tagsByLetter[letter].push(item);
});
return tagsByLetter;
});
return { tags, api, tagsByLetter };
components: {
RecipeCategoryTagToolPage,
},
head() {
setup() {
const userApi = useUserApi();
const tools = useAsync(async () => {
const { data } = await userApi.tags.getAll();
if (data) {
return data;
}
});
return {
title: this.$t("sidebar.tags") as string,
tools,
};
},
// head: {
// // @ts-ignore
// title: this.$t("sidebar.tags") as string,
// },
});
</script>
<style scoped>
</style>
</script>

View file

@ -0,0 +1,119 @@
<template>
<v-container>
<RecipeCardSection v-if="tools" :title="tools.name" :recipes="tools.recipes" @sort="assignSorted">
<template #title>
<v-btn icon class="mr-1">
<v-icon dark large @click="reset">
{{ $globals.icons.potSteam }}
</v-icon>
</v-btn>
<template v-if="edit">
<v-text-field
v-model="tools.name"
autofocus
single-line
dense
hide-details
class="headline"
@keyup.enter="updateTools"
>
</v-text-field>
<v-btn icon @click="updateTools">
<v-icon size="28">
{{ $globals.icons.save }}
</v-icon>
</v-btn>
<v-btn icon class="mr-1" @click="reset">
<v-icon size="28">
{{ $globals.icons.close }}
</v-icon>
</v-btn>
</template>
<template v-else>
<v-tooltip top>
<template #activator="{ on, attrs }">
<v-toolbar-title v-bind="attrs" style="cursor: pointer" class="headline" v-on="on" @click="edit = true">
{{ tools.name }}
</v-toolbar-title>
</template>
<span> Click to Edit </span>
</v-tooltip>
</template>
</template>
</RecipeCardSection>
</v-container>
</template>
<script lang="ts">
import { defineComponent, useAsync, useRoute, reactive, toRefs, useRouter } from "@nuxtjs/composition-api";
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
import { useUserApi } from "~/composables/api";
import { Recipe } from "~/types/api-types/recipe";
export default defineComponent({
components: { RecipeCardSection },
setup() {
const api = useUserApi();
const route = useRoute();
const router = useRouter();
const slug = route.value.params.slug;
const state = reactive({
initialValue: "",
edit: false,
});
const tools = useAsync(async () => {
const { data } = await api.tools.byslug(slug);
if (data) {
state.initialValue = data.name;
}
return data;
}, slug);
function reset() {
state.edit = false;
if (tools.value) {
tools.value.name = state.initialValue;
}
}
async function updateTools() {
state.edit = false;
if (!tools.value) {
return;
}
const { data } = await api.tools.updateOne(tools.value.id, tools.value);
if (data) {
router.push("/recipes/tools/" + data.slug);
}
}
return {
tools,
reset,
...toRefs(state),
updateTools,
};
},
head() {
return {
title: this.$t("sidebar.categories") as string,
};
},
methods: {
assignSorted(val: Array<Recipe>) {
if (this.tools) {
// @ts-ignore
this.tools.recipes = val;
}
},
},
});
</script>

View file

@ -0,0 +1,33 @@
<template>
<v-container>
<RecipeCategoryTagToolPage v-if="tools" :items="tools" item-type="tools" />
</v-container>
</template>
<script lang="ts">
import { defineComponent, useAsync } from "@nuxtjs/composition-api";
import RecipeCategoryTagToolPage from "~/components/Domain/Recipe/RecipeCategoryTagToolPage.vue";
import { useUserApi } from "~/composables/api";
export default defineComponent({
components: {
RecipeCategoryTagToolPage,
},
setup() {
const userApi = useUserApi();
const tools = useAsync(async () => {
const { data } = await userApi.tools.getAll();
if (data) {
return data;
}
});
return {
tools,
};
},
head: {
title: "Tools",
},
});
</script>