Saving recipe update
This commit is contained in:
@@ -61,4 +61,18 @@ public class RecipeController : ControllerBase
|
|||||||
return Ok(new { message = "Recipe saved to your collection!" });
|
return Ok(new { message = "Recipe saved to your collection!" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
|
[HttpGet("my-collection")]
|
||||||
|
public async Task<ActionResult<IEnumerable<Recipe>>> GetMyRecipes()
|
||||||
|
{
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
|
||||||
|
var myRecipes = await _context.Recipes
|
||||||
|
.Where(r => r.UserId == userId)
|
||||||
|
.OrderByDescending(r => r.CreatedAt)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(myRecipes);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,14 @@
|
|||||||
Back to Recipe Upload
|
Back to Recipe Upload
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<v-row v-if="recipes?.length">
|
<v-row v-if="loading" justify="center" class="py-16">
|
||||||
|
<v-col cols="12" class="d-flex flex-column align-center">
|
||||||
|
<v-progress-circular indeterminate color="#556b2f" size="64" width="3"></v-progress-circular>
|
||||||
|
<p class="brand-subtitle mt-4">Opening the Ledger...</p>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-row v-else-if="recipes?.length">
|
||||||
<v-col v-for="recipe in recipes" :key="recipe.id" cols="12" sm="6" md="4">
|
<v-col v-for="recipe in recipes" :key="recipe.id" cols="12" sm="6" md="4">
|
||||||
<v-card class="gallery-item-card pa-4">
|
<v-card class="gallery-item-card pa-4">
|
||||||
<v-img
|
<v-img
|
||||||
@@ -53,6 +60,7 @@
|
|||||||
<v-row v-else justify="center" class="py-10 text-center">
|
<v-row v-else justify="center" class="py-10 text-center">
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<p class="brand-subtitle mb-4">Your collection is empty.</p>
|
<p class="brand-subtitle mb-4">Your collection is empty.</p>
|
||||||
|
<v-btn to="/" variant="text" color="#556b2f">Return to kitchen to add some</v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
@@ -60,28 +68,41 @@
|
|||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import '@/assets/css/gallery.css'
|
import '@/assets/css/gallery.css'
|
||||||
//const config = useRuntimeConfig();
|
const config = useRuntimeConfig()
|
||||||
//const { data: recipes } = await useFetch(`${config.public.apiBase}api/recipe`);
|
const recipes = ref([])
|
||||||
const recipes = ref([
|
const loading = ref(true)
|
||||||
{
|
|
||||||
id: 1,
|
onMounted(async () => {
|
||||||
title: 'Salmon Salad',
|
await fetchRecipes()
|
||||||
createdAt: '2026-03-05T12:00:00Z',
|
})
|
||||||
description: 'A fresh herb-crusted salmon over greens.'
|
|
||||||
},
|
const fetchRecipes = async () => {
|
||||||
{
|
// Get the token we saved during login
|
||||||
id: 2,
|
const token = useCookie('seasoned_token').value
|
||||||
title: 'Power Bowl',
|
|| (import.meta.client ? localStorage.getItem('token') : null)
|
||||||
createdAt: '2026-03-04T10:00:00Z',
|
|
||||||
description: 'Rich and earthy pesto with toasted walnuts.'
|
if (!token) {
|
||||||
},
|
// If no token, kick them back to login
|
||||||
{
|
return navigateTo('/login')
|
||||||
id: 3,
|
|
||||||
title: 'Protein Slam',
|
|
||||||
createdAt: '2026-03-01T08:00:00Z',
|
|
||||||
description: 'Crispy crust with a light, airy center.'
|
|
||||||
}
|
}
|
||||||
]);
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
// You'll need to add this GET endpoint to your RecipeController
|
||||||
|
const data = await $fetch(`${config.public.apiBase}api/recipe/my-collection`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
recipes.value = data
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to load collection:", err)
|
||||||
|
if (err.status === 401) navigateTo('/login')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -186,16 +186,29 @@ const uploadImage = async () => {
|
|||||||
const saveToCollection = async () => {
|
const saveToCollection = async () => {
|
||||||
if (!recipe.value || hasSaved.value) return;
|
if (!recipe.value || hasSaved.value) return;
|
||||||
|
|
||||||
|
// 1. Get the token (same logic as gallery)
|
||||||
|
const token = useCookie('seasoned_token').value
|
||||||
|
|| (import.meta.client ? localStorage.getItem('token') : null)
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
alert("Please sign in to save recipes.")
|
||||||
|
return navigateTo('/login')
|
||||||
|
}
|
||||||
|
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await $fetch(`${config.public.apiBase}api/recipe/save`, {
|
await $fetch(`${config.public.apiBase}api/recipe/save`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
body: recipe.value
|
body: recipe.value
|
||||||
});
|
});
|
||||||
hasSaved.value = true;
|
hasSaved.value = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Save failed:", error);
|
console.error("Save failed:", error);
|
||||||
|
alert("Could not save recipe. Your session might have expired.")
|
||||||
} finally {
|
} finally {
|
||||||
saving.value = false;
|
saving.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user