mirror of
https://github.com/rjNemo/meal_planner
synced 2026-06-06 02:26:49 +00:00
add cookbook
This commit is contained in:
parent
35af888724
commit
3e34609fcf
5 changed files with 74 additions and 27 deletions
|
|
@ -10,7 +10,7 @@
|
||||||
:autofocus="autofocus"
|
:autofocus="autofocus"
|
||||||
@focus="isFocused = true"
|
@focus="isFocused = true"
|
||||||
@blur="isFocused = false"
|
@blur="isFocused = false"
|
||||||
>
|
/>
|
||||||
<kbd
|
<kbd
|
||||||
class="hidden md:inline-block kbd kbd-sm"
|
class="hidden md:inline-block kbd kbd-sm"
|
||||||
:class="{ 'opacity-50': !isFocused }"
|
:class="{ 'opacity-50': !isFocused }"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,29 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Recipe } from "~/types/recipe";
|
import type { Recipe } from "~/types/recipe";
|
||||||
|
|
||||||
defineProps<{ recipe: Recipe }>();
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
|
const { recipe } = defineProps<{ recipe: Recipe }>();
|
||||||
|
|
||||||
|
const cookbook = useLocalStorage<Recipe[]>("cookbook", []);
|
||||||
|
|
||||||
|
const likedRecipes = ref(new Set<string>());
|
||||||
|
onMounted(() => {
|
||||||
|
likedRecipes.value = new Set(cookbook.value.map((recipe) => recipe.id));
|
||||||
|
console.log("cook", likedRecipes.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const toggleLike = (recipeId: string) => {
|
||||||
|
if (likedRecipes.value.has(recipeId)) {
|
||||||
|
likedRecipes.value.delete(recipeId);
|
||||||
|
cookbook.value = cookbook.value.filter((recipe) => recipe.id !== recipeId);
|
||||||
|
} else {
|
||||||
|
likedRecipes.value.add(recipeId);
|
||||||
|
const recipeToAdd = recipe;
|
||||||
|
if (recipeToAdd) {
|
||||||
|
cookbook.value = [...cookbook.value, recipeToAdd];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const shareRecipe = async (recipe: Recipe) => {
|
const shareRecipe = async (recipe: Recipe) => {
|
||||||
const url =
|
const url =
|
||||||
|
|
@ -49,10 +71,23 @@ const shareRecipe = async (recipe: Recipe) => {
|
||||||
<p class="prose prose-lg max-w-none w-full">
|
<p class="prose prose-lg max-w-none w-full">
|
||||||
{{ recipe.instructions }}
|
{{ recipe.instructions }}
|
||||||
</p>
|
</p>
|
||||||
<button class="btn btn-accent mt-4" @click="shareRecipe(recipe)">
|
<div class="flex gap-4 mt-4">
|
||||||
<icon name="uil:share-alt" class="mr-2 w-6 h-6" />
|
<button class="btn btn-accent" @click="shareRecipe(recipe)">
|
||||||
Share Recipe
|
<icon name="uil:share-alt" class="mr-2 w-6 h-6" />
|
||||||
</button>
|
Share Recipe
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-ghost"
|
||||||
|
@click="toggleLike(recipe.id)"
|
||||||
|
:class="{ 'text-red-500': likedRecipes.has(recipe.id) }"
|
||||||
|
>
|
||||||
|
<icon
|
||||||
|
:name="likedRecipes.has(recipe.id) ? 'uil:heart' : 'uil:heart-alt'"
|
||||||
|
class="w-6 h-6"
|
||||||
|
/>
|
||||||
|
Like
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Recipe } from "~/types/recipe";
|
import type { Recipe } from "~/types/recipe";
|
||||||
|
import { useStorage } from "@vueuse/core";
|
||||||
const cookbook = [] as Recipe[];
|
const cookbook = useStorage<Recipe[]>("cookbook", [], localStorage);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -16,7 +16,7 @@ const cookbook = [] as Recipe[];
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="recipe in cookbook" :key="recipe.id">
|
<li v-for="recipe in cookbook" :key="recipe.id">
|
||||||
<nuxt-link :to="`/recipes/${recipe.id}`">
|
<nuxt-link :to="`/${recipe.id}`">
|
||||||
<recipe-card
|
<recipe-card
|
||||||
:title="recipe.title"
|
:title="recipe.title"
|
||||||
:picture-url="recipe.pictureUrl"
|
:picture-url="recipe.pictureUrl"
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,24 @@ import { publicProcedure, router } from "../trpc";
|
||||||
const { apiUrl } = useRuntimeConfig();
|
const { apiUrl } = useRuntimeConfig();
|
||||||
|
|
||||||
export const recipeRouter = router({
|
export const recipeRouter = router({
|
||||||
recipesByCategory: publicProcedure.input(z.string()).query(async ({ input }) => {
|
recipesByCategory: publicProcedure
|
||||||
const data = await $fetch<{ meals: Meal[] }>(new URL(`filter.php?c=${input}`, apiUrl).href);
|
.input(z.string())
|
||||||
|
.query(async ({ input }) => {
|
||||||
|
const data = await $fetch<{ meals: Meal[] }>(
|
||||||
|
new URL(`filter.php?c=${input}`, apiUrl).href,
|
||||||
|
);
|
||||||
|
|
||||||
const result = categoryRecipesResponseSchema.safeParse(data);
|
const result = categoryRecipesResponseSchema.safeParse(data);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
statusMessage: "Recipes for category not found",
|
statusMessage: "Recipes for category not found",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.data.recipes;
|
return result.data.recipes;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
recipeGet: publicProcedure
|
recipeGet: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
|
|
@ -33,10 +37,12 @@ export const recipeRouter = router({
|
||||||
required_error: "recipe id is required",
|
required_error: "recipe id is required",
|
||||||
invalid_type_error: "recipe id must be a number",
|
invalid_type_error: "recipe id must be a number",
|
||||||
})
|
})
|
||||||
.positive("recipe id must be positive")
|
.positive("recipe id must be positive"),
|
||||||
)
|
)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const data = await $fetch<{ meals: Meal[] }>(new URL(`lookup.php?i=${input}`, apiUrl).href);
|
const data = await $fetch<{ meals: Meal[] }>(
|
||||||
|
new URL(`lookup.php?i=${input}`, apiUrl).href,
|
||||||
|
);
|
||||||
if (!data?.meals) {
|
if (!data?.meals) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
|
|
@ -49,7 +55,9 @@ export const recipeRouter = router({
|
||||||
}),
|
}),
|
||||||
|
|
||||||
recipeRandom: publicProcedure.query(async () => {
|
recipeRandom: publicProcedure.query(async () => {
|
||||||
const data = await $fetch<{ meals: Meal[] }>(new URL("random.php", apiUrl).toString());
|
const data = await $fetch<{ meals: Meal[] }>(
|
||||||
|
new URL("random.php", apiUrl).toString(),
|
||||||
|
);
|
||||||
if (!data?.meals) {
|
if (!data?.meals) {
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 500,
|
statusCode: 500,
|
||||||
|
|
@ -64,10 +72,12 @@ export const recipeRouter = router({
|
||||||
.input(
|
.input(
|
||||||
z.string({
|
z.string({
|
||||||
required_error: "search query is required",
|
required_error: "search query is required",
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const data = await $fetch<{ meals: Meal[] }>(new URL(`search.php?s=${input}`, apiUrl).href);
|
const data = await $fetch<{ meals: Meal[] }>(
|
||||||
|
new URL(`search.php?s=${input}`, apiUrl).href,
|
||||||
|
);
|
||||||
if (!data?.meals) {
|
if (!data?.meals) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +86,9 @@ export const recipeRouter = router({
|
||||||
}),
|
}),
|
||||||
|
|
||||||
listCategories: publicProcedure.query(async () => {
|
listCategories: publicProcedure.query(async () => {
|
||||||
const response = await $fetch<CategoriesResponse>(new URL("categories.php", apiUrl).href);
|
const response = await $fetch<CategoriesResponse>(
|
||||||
|
new URL("categories.php", apiUrl).href,
|
||||||
|
);
|
||||||
|
|
||||||
const result = categoriesResponseSchema.safeParse(response);
|
const result = categoriesResponseSchema.safeParse(response);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { initTRPC , TRPCError } from "@trpc/server";
|
import { initTRPC, TRPCError } from "@trpc/server";
|
||||||
import type { Context } from "~/server/trpc/context";
|
import type { Context } from "~/server/trpc/context";
|
||||||
// import { authMiddleware } from "~/server/trpc/middleware";
|
// import { authMiddleware } from "~/server/trpc/middleware";
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue