Meal Planning Start
This commit is contained in:
parent
ca2b05b161
commit
11c616b5fe
7 changed files with 150 additions and 10 deletions
12
src/assets/plan-meal.svg
Normal file
12
src/assets/plan-meal.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg fill="#000000" height="800px" width="800px" version="1.1" id="XMLID_124_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 0 24 24" xml:space="preserve">
|
||||||
|
<g id="plan">
|
||||||
|
<g>
|
||||||
|
<path d="M24,24H0V3h5V0h2v3h10V0h2v3h5V24z M2,22h20V5H2v3h20v2H2V22z M19,19H9v-2h10V19z M7,19H5v-2h2V19z M19,15H9v-2h10V15z
|
||||||
|
M7,15H5v-2h2V15z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 519 B |
|
|
@ -2,6 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<action-item title="Add to next shop" :image="require('@/assets/add-cart.svg')" @click="addToNextShop" />
|
<action-item title="Add to next shop" :image="require('@/assets/add-cart.svg')" @click="addToNextShop" />
|
||||||
<action-item title="Add Recipe" :image="require('@/assets/add-recipe.svg')" @click="addRecipe" />
|
<action-item title="Add Recipe" :image="require('@/assets/add-recipe.svg')" @click="addRecipe" />
|
||||||
|
<action-item title="Plan Meal" :image="require('@/assets/plan-meal.svg')" @click="planMeal" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -19,6 +20,10 @@ export default {
|
||||||
addRecipe() {
|
addRecipe() {
|
||||||
console.log('Add Recipe')
|
console.log('Add Recipe')
|
||||||
this.$router.push('/recipes/add')
|
this.$router.push('/recipes/add')
|
||||||
|
},
|
||||||
|
planMeal() {
|
||||||
|
console.log('Plan Meal')
|
||||||
|
this.$router.push('/meals/add')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,36 @@
|
||||||
<template>
|
<template>
|
||||||
<div></div>
|
<div>
|
||||||
|
<input type="date" v-model="meal.date" />
|
||||||
|
<recipe-search-box @select-recipe="selectRecipe" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import data from '@/data.js'
|
||||||
|
import RecipeSearchBox from './RecipeSearchBox.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['id'],
|
props: ['id'],
|
||||||
|
components: { RecipeSearchBox },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
meal: {
|
meal: {
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
recipe: { name: 'Sar Tay' },
|
recipes: [{ name: 'Sar Tay' }],
|
||||||
chefs: [{ name: 'Ryan' }],
|
chefs: [{ name: 'Ryan' }],
|
||||||
consumers: [{ name: 'Ryan' }, { name: 'Jacob' }],
|
consumers: [{ name: 'Ryan' }, { name: 'Jacob' }],
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
if (this.id) {
|
||||||
|
this.meal = data.getMeal(this.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
selectRecipe(recipe) {
|
||||||
|
this.meal.recipe = recipe;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="actions" v-if="selectedMeal">
|
<ul class="actions" v-if="selectedMeal">
|
||||||
<li><router-link class="nav-link" to="/meal/{{meal.id}}/edit" active-class="active">Edit Meal</router-link></li>
|
<li><router-link class="nav-link" to="/meals/{{meal.id}}" active-class="active">Edit Meal</router-link></li>
|
||||||
<li><router-link class="nav-link" to="/meal/{{meal.id}}/swap" active-class="active">Swap Day</router-link></li>
|
<li><router-link class="nav-link" to="/meals/{{meal.id}}/swap" active-class="active">Swap Day</router-link></li>
|
||||||
<li><a @click="deleteSelectedMeal" class="button">Remove</a></li>
|
<li><a @click="deleteSelectedMeal" class="button">Remove</a></li>
|
||||||
<li><a @click="selectedMeal = null">Cancel</a></li>
|
<li><a @click="selectedMeal = null">Cancel</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -50,6 +50,7 @@ li {
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MealCard from './MealCard.vue'
|
import MealCard from './MealCard.vue'
|
||||||
|
import data from '@/data.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MealPlanPage',
|
name: 'MealPlanPage',
|
||||||
|
|
@ -57,7 +58,11 @@ export default {
|
||||||
MealCard
|
MealCard
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const from = new Date();
|
||||||
|
const to = new Date(from.getDate() + 7)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
from, to,
|
||||||
meals: [{
|
meals: [{
|
||||||
'date': new Date(),
|
'date': new Date(),
|
||||||
'recipe': {'name': 'Sar Tay'},
|
'recipe': {'name': 'Sar Tay'},
|
||||||
|
|
@ -69,6 +74,10 @@ export default {
|
||||||
selectedMeal: null
|
selectedMeal: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
beforeMount() {
|
||||||
|
data.getMeals(this.from, this.to)
|
||||||
|
.then(meals => this.meals = meals);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
editmeal(meal) {
|
editmeal(meal) {
|
||||||
console.log('editmeal', meal)
|
console.log('editmeal', meal)
|
||||||
|
|
|
||||||
96
src/components/RecipeSearchBox.vue
Normal file
96
src/components/RecipeSearchBox.vue
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<input type="text" v-model="searchTerm" @keyup.enter="search" @focusin="search" @focusout="search" placeholder="Add a recipe..." />
|
||||||
|
<ul v-if="recipes?.length">
|
||||||
|
<li class="recipe" v-for="recipe in recipes" :key="recipe.id" @click="selectRecipe(recipe)">
|
||||||
|
<img v-if="recipe.image_urls" :src="recipe.image_urls[0]" />
|
||||||
|
<p class="recipe-name">{{ recipe.name }}</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import data from '@/data.js'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchTerm: '',
|
||||||
|
recipes: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
searchTerm() {
|
||||||
|
const searchTerm = this.searchTerm;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (searchTerm === this.searchTerm){
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
search() {
|
||||||
|
data.searchRecipes(this.searchTerm).then((recipes) => {
|
||||||
|
this.recipes = recipes
|
||||||
|
});
|
||||||
|
},
|
||||||
|
selectRecipe(recipe) {
|
||||||
|
this.$emit('select-recipe', recipe);
|
||||||
|
this.searchTerm = '';
|
||||||
|
this.recipes = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
font-size: large;
|
||||||
|
margin: 1ex 1em;
|
||||||
|
width: 80%;
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show recipes as cards in a flex grid 2-3 items wide */
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
width: 80%;
|
||||||
|
margin: 0 auto;
|
||||||
|
border: solid 1px #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
list-style-type: none;
|
||||||
|
border: solid 1px #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 1em;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: left;
|
||||||
|
max-height: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li img {
|
||||||
|
display: block;
|
||||||
|
width: 8em;
|
||||||
|
height: 8em;
|
||||||
|
margin: 0.5em;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipe-name {
|
||||||
|
font-size: larger;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
const BASE_URL = "http://localhost:8000"
|
const BASE_URL = "http://localhost:8000"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getMeals() {
|
getMeals(from, to) {
|
||||||
return fetch(BASE_URL + "/meals")
|
return fetch(BASE_URL + "/meals?from=" + from.toISOString() + "&to=" + to.toISOString())
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
},
|
},
|
||||||
getMeal(id) {
|
getMeal(id) {
|
||||||
return fetch(BASE_URL + `/meals/${encodeURIComponent(id)}`)
|
return fetch(BASE_URL + `/meals/${encodeURIComponent(id)}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
},
|
},
|
||||||
getRecipes() {
|
searchRecipes(query) {
|
||||||
|
return fetch(BASE_URL + "/recipes?q=" + encodeURIComponent(query))
|
||||||
|
.then(response => response.json())
|
||||||
},
|
},
|
||||||
parseRecipe(url) {
|
parseRecipe(url) {
|
||||||
return fetch(BASE_URL + `/recipes/parse?url=${encodeURIComponent(url)}`)
|
return fetch(BASE_URL + `/recipes/parse?url=${encodeURIComponent(url)}`)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import ActionsPage from './components/ActionsPage.vue'
|
||||||
import MealPlanPage from './components/MealPlanPage.vue'
|
import MealPlanPage from './components/MealPlanPage.vue'
|
||||||
import ShoppingPage from './components/ShoppingPage.vue'
|
import ShoppingPage from './components/ShoppingPage.vue'
|
||||||
import EditMealPage from './components/EditMealPage.vue'
|
import EditMealPage from './components/EditMealPage.vue'
|
||||||
import SwapMealPage from './components/SwapMealPage.vue'
|
|
||||||
import EditRecipePage from './components/EditRecipePage.vue'
|
import EditRecipePage from './components/EditRecipePage.vue'
|
||||||
|
|
||||||
// 2. Define some routes
|
// 2. Define some routes
|
||||||
|
|
@ -17,10 +16,10 @@ const routes = [
|
||||||
{ path: '/', component: ActionsPage },
|
{ path: '/', component: ActionsPage },
|
||||||
{ path: '/mealplan', component: MealPlanPage },
|
{ path: '/mealplan', component: MealPlanPage },
|
||||||
{ path: '/shopping', component: ShoppingPage },
|
{ path: '/shopping', component: ShoppingPage },
|
||||||
{ path: '/meal/:id/edit', component: EditMealPage },
|
|
||||||
{ path: '/meal/:id/swap', component: SwapMealPage },
|
|
||||||
{ path: '/recipes/add', component: EditRecipePage },
|
{ path: '/recipes/add', component: EditRecipePage },
|
||||||
{ path: '/recipes/:id', component: EditRecipePage, props: true },
|
{ path: '/recipes/:id', component: EditRecipePage, props: true },
|
||||||
|
{ path: '/meals/add', component: EditMealPage },
|
||||||
|
{ path: '/meals/:id', component: EditMealPage, props: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
// 3. Create the router instance and pass the `routes` option
|
// 3. Create the router instance and pass the `routes` option
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue