mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-05 13:35:23 +02:00
feat(backend): ✨ add rename tag, tool, category support (#875)
This commit is contained in:
parent
8d77f4b31e
commit
e109ac0f47
25 changed files with 573 additions and 163 deletions
|
@ -1,4 +1,5 @@
|
|||
import { BaseCRUDAPI } from "../_base";
|
||||
import { Recipe } from "~/types/api-types/recipe";
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
|
@ -9,14 +10,24 @@ export interface CreateTool {
|
|||
|
||||
export interface Tool extends CreateTool {
|
||||
id: number;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export interface RecipeToolResponse extends Tool {
|
||||
recipes: Recipe[];
|
||||
}
|
||||
|
||||
const routes = {
|
||||
tools: `${prefix}/tools`,
|
||||
toolsId: (id: string) => `${prefix}/tools/${id}`,
|
||||
toolsSlug: (id: string) => `${prefix}/tools/slug/${id}`,
|
||||
};
|
||||
|
||||
export class ToolsApi extends BaseCRUDAPI<Tool, CreateTool> {
|
||||
baseRoute: string = routes.tools;
|
||||
itemRoute = routes.toolsId;
|
||||
|
||||
async byslug(slug: string) {
|
||||
return await this.requests.get<Tool>(routes.toolsSlug(slug));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-app-bar v-if="!disableToolbar" color="transparent" flat class="mt-n1 flex-sm-wrap rounded">
|
||||
<v-icon v-if="title" large left>
|
||||
{{ displayTitleIcon }}
|
||||
</v-icon>
|
||||
<v-toolbar-title class="headline"> {{ title }} </v-toolbar-title>
|
||||
<slot name="title">
|
||||
<v-icon v-if="title" large left>
|
||||
{{ displayTitleIcon }}
|
||||
</v-icon>
|
||||
<v-toolbar-title class="headline"> {{ title }} </v-toolbar-title>
|
||||
</slot>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn :icon="$vuetify.breakpoint.xsOnly" text :disabled="recipes.length === 0" @click="navigateRandom">
|
||||
<v-icon :left="!$vuetify.breakpoint.xsOnly">
|
||||
|
|
106
frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue
Normal file
106
frontend/components/Domain/Recipe/RecipeCategoryTagToolPage.vue
Normal file
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<div v-if="items">
|
||||
<v-app-bar color="transparent" flat class="mt-n1 rounded">
|
||||
<v-icon large left>
|
||||
{{ icon }}
|
||||
</v-icon>
|
||||
<v-toolbar-title class="headline"> {{ headline }} </v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
</v-app-bar>
|
||||
<section v-for="(itms, key, idx) in itemsSorted" :key="'header' + idx" :class="idx === 1 ? null : 'my-4'">
|
||||
<BaseCardSectionTitle :title="key"> </BaseCardSectionTitle>
|
||||
<v-row>
|
||||
<v-col v-for="(item, index) in itms" :key="'cat' + index" cols="12" :sm="12" :md="6" :lg="4" :xl="3">
|
||||
<v-card hover :to="`/recipes/${itemType}/${item.slug}`">
|
||||
<v-card-actions>
|
||||
<v-icon>
|
||||
{{ icon }}
|
||||
</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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs, useContext, computed } from "@nuxtjs/composition-api";
|
||||
|
||||
type ItemType = "tags" | "categories" | "tools";
|
||||
|
||||
const ItemTypes = {
|
||||
tag: "tags",
|
||||
category: "categories",
|
||||
tool: "tools",
|
||||
};
|
||||
|
||||
interface GenericItem {
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
itemType: {
|
||||
type: String as () => ItemType,
|
||||
required: true,
|
||||
},
|
||||
items: {
|
||||
type: Array as () => GenericItem[],
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
// @ts-ignore
|
||||
const { i18n, $globals } = useContext();
|
||||
|
||||
const state = reactive({
|
||||
headline: "tags",
|
||||
icon: $globals.icons.tags,
|
||||
});
|
||||
|
||||
switch (props.itemType) {
|
||||
case ItemTypes.tag:
|
||||
state.headline = i18n.t("tag.tags") as string;
|
||||
break;
|
||||
case ItemTypes.category:
|
||||
state.headline = i18n.t("recipe.categories") as string;
|
||||
break;
|
||||
case ItemTypes.tool:
|
||||
state.headline = "Tools";
|
||||
state.icon = $globals.icons.potSteam;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const itemsSorted = computed(() => {
|
||||
const byLetter: { [key: string]: Array<any> } = {};
|
||||
|
||||
if (!props.items) return byLetter;
|
||||
|
||||
props.items.forEach((item) => {
|
||||
const letter = item.name[0].toUpperCase();
|
||||
if (!byLetter[letter]) {
|
||||
byLetter[letter] = [];
|
||||
}
|
||||
|
||||
byLetter[letter].push(item);
|
||||
});
|
||||
|
||||
return byLetter;
|
||||
});
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
itemsSorted,
|
||||
};
|
||||
},
|
||||
head: {
|
||||
title: "vbase-nuxt",
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -181,6 +181,11 @@ export default defineComponent({
|
|||
to: "/recipes/tags",
|
||||
title: this.$t("sidebar.tags"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.potSteam,
|
||||
to: "/recipes/tools",
|
||||
title: "Tools",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
|
|
@ -317,7 +317,10 @@ export default {
|
|||
// https://nuxtjs.org/docs/2.x/configuration-glossary/configuration-build
|
||||
analyze: process.env.NODE_ENV !== "production",
|
||||
babel: {
|
||||
plugins: [["@babel/plugin-proposal-private-property-in-object", { loose: true }]],
|
||||
plugins: [
|
||||
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
|
||||
["@nuxtjs/composition-api/dist/babel-plugin"],
|
||||
],
|
||||
},
|
||||
transpile: process.env.NODE_ENV !== "production" ? [/@vue[\\/]composition-api/] : null,
|
||||
},
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</section>
|
||||
<section>
|
||||
<BaseCardSectionTitle class="pt-2" :icon="$globals.icons.email" title="Email Configuration" />
|
||||
<v-alert :key="idx" border="left" colored-border :type="getColor(appConfig.emailReady)" elevation="2">
|
||||
<v-alert border="left" colored-border :type="getColor(appConfig.emailReady)" elevation="2">
|
||||
<div class="font-weight-medium">Email Configuration Status</div>
|
||||
<div>
|
||||
{{ appConfig.emailReady ? "Ready" : "Not Ready - Check Environmental Variables" }}
|
||||
|
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
@ -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>
|
119
frontend/pages/recipes/tools/_slug.vue
Normal file
119
frontend/pages/recipes/tools/_slug.vue
Normal 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>
|
||||
|
33
frontend/pages/recipes/tools/index.vue
Normal file
33
frontend/pages/recipes/tools/index.vue
Normal 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>
|
|
@ -2,7 +2,6 @@ import { useDark } from "@vueuse/core";
|
|||
|
||||
export default ({ $vuetify }: any) => {
|
||||
const isDark = useDark();
|
||||
console.log("isDark Plugin", isDark);
|
||||
|
||||
if (isDark.value) {
|
||||
$vuetify.theme.dark = true;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue