loads of work...
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
__pycache__
|
||||
db.sqlite3
|
||||
run_dev.sh
|
||||
**/migrations/
|
||||
|
||||
@@ -1,46 +1,49 @@
|
||||
"""cookAssistant URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/2.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from recipe_book import views
|
||||
from rest_framework_nested import routers
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'recips', views.RecipesViewSet)
|
||||
router.register(r'ingdts', views.IngredientViewSet)
|
||||
|
||||
amounts_router = routers.NestedDefaultRouter(
|
||||
router, r'recips', lookup="recipe"
|
||||
)
|
||||
amounts_router.register(
|
||||
r'amounts', views.IngredientWAmountViewSet,
|
||||
base_name="recipe-ingdt-amounts"
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', include(router.urls)),
|
||||
path('api/', include(amounts_router.urls)),
|
||||
path('',
|
||||
TemplateView.as_view(
|
||||
template_name="application.html"
|
||||
),
|
||||
name="app"
|
||||
),
|
||||
]
|
||||
"""cookAssistant URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/2.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from recipe_book import views
|
||||
from planning import views as planning_views
|
||||
from rest_framework_nested import routers
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register(r'recips', views.RecipesViewSet)
|
||||
router.register(r'ingdts', views.IngredientViewSet)
|
||||
|
||||
amounts_router = routers.NestedDefaultRouter(
|
||||
router, r'recips', lookup="recipe"
|
||||
)
|
||||
amounts_router.register(
|
||||
r'amounts', views.IngredientWAmountViewSet,
|
||||
base_name="recipe-ingdt-amounts"
|
||||
)
|
||||
|
||||
router.register(r'planning', planning_views.MealViewSet)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/', include(router.urls)),
|
||||
path('api/', include(amounts_router.urls)),
|
||||
path('',
|
||||
TemplateView.as_view(
|
||||
template_name="application.html"
|
||||
),
|
||||
name="app"
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
<template>
|
||||
<v-app dark>
|
||||
<v-app light>
|
||||
<v-navigation-drawer app clipped mini-variant v-model="showNav">
|
||||
<v-list>
|
||||
<v-list-tile>
|
||||
<v-list-tile-title>
|
||||
CookAssistant
|
||||
</v-list-tile-title>
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
<v-list dense>
|
||||
<v-list-tile
|
||||
v-for="link in links"
|
||||
:key="link.path"
|
||||
@@ -24,7 +17,7 @@
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
<v-toolbar app clipped-left>
|
||||
<v-toolbar app flat color="light-green" clipped-left>
|
||||
<v-toolbar-side-icon
|
||||
@click.stop="showNav = !showNav"
|
||||
></v-toolbar-side-icon>
|
||||
@@ -47,9 +40,7 @@
|
||||
</v-toolbar>
|
||||
|
||||
<v-content>
|
||||
<v-container fluid>
|
||||
<router-view></router-view>
|
||||
</v-container>
|
||||
<router-view></router-view>
|
||||
</v-content>
|
||||
</v-app>
|
||||
</template>
|
||||
@@ -58,12 +49,6 @@
|
||||
|
||||
import NotFound from './routes/NotFound'
|
||||
|
||||
export const RecipeCategories = {
|
||||
0: "Petit-déjeuner",
|
||||
1: "Entrée",
|
||||
2: "Plat",
|
||||
3: "Dessert"
|
||||
}
|
||||
// *** TOOLS ***
|
||||
//
|
||||
// * create_recipe: go to creation page
|
||||
@@ -83,7 +68,6 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
links: [],
|
||||
categories: RecipeCategories,
|
||||
showNav: null,//
|
||||
}
|
||||
},
|
||||
|
||||
@@ -151,4 +151,5 @@ export default {
|
||||
ingredients: mountNestedEndPoint(
|
||||
`${baseUrl}/recips/*/amounts/`
|
||||
),
|
||||
meals: mountEndPoint(`${baseUrl}/planning/`),
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<template>
|
||||
<!-- Categories list -->
|
||||
<v-tabs v-if="showTabs" :mandatory="false">
|
||||
<v-tab v-for="key in Object.keys(categories)"
|
||||
:key="`cat-${key}`"
|
||||
:to="`/recipes/category/${key}`">
|
||||
{{ categories[key] }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-btn v-else
|
||||
icon
|
||||
@click="$router.go(-1)"
|
||||
><v-icon>arrow_back</v-icon></v-btn>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { RecipeCategories } from '../App'
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
categories: RecipeCategories,
|
||||
}),
|
||||
computed: {
|
||||
showTabs: function() {
|
||||
return !(this.$route.path.startsWith("/recipes/id"))
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<v-select v-if="type == 'choice'"
|
||||
:label="label"
|
||||
placeholder="----"
|
||||
:items="choices"
|
||||
item-text="display_name"
|
||||
:value="value"
|
||||
|
||||
27
frontend/src/components/planning/Day.vue
Normal file
27
frontend/src/components/planning/Day.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="headline">{{ day }}</span>
|
||||
</v-card-title>
|
||||
<v-divider light></v-divider>
|
||||
<v-card-text>
|
||||
<span>Recette n°1</span>
|
||||
<span>Recette n°2</span>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn icon><v-icon>delete</v-icon></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['day', 'meals'],
|
||||
data () {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
19
frontend/src/components/planning/Meal.vue
Normal file
19
frontend/src/components/planning/Meal.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<v-card-text v-if="item">
|
||||
<p class="body-2 ma-0 text-xs-center" v-for="(dish, id) in item.dishes" :key="id">
|
||||
{{ dish.name }}
|
||||
</p>
|
||||
</v-card-text>
|
||||
<v-card-text v-else>()</v-card-text>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: ['item'],
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
65
frontend/src/components/planning/Options.vue
Normal file
65
frontend/src/components/planning/Options.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<v-container grid-list-lg>
|
||||
<v-layout wrap>
|
||||
<v-flex>
|
||||
<h6 class="title mb-3">Meal Options</h6>
|
||||
<v-card :flat="!isMealUsed">
|
||||
<v-container>
|
||||
<v-layout>
|
||||
<v-card-title>
|
||||
<h6 class="title">
|
||||
Repas du { date }
|
||||
</h6>
|
||||
</v-card-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-flex shrink>
|
||||
<v-switch
|
||||
v-model="isMealUsed"
|
||||
label="Used ?"
|
||||
color="light-green">
|
||||
</v-switch>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
<v-card-text>
|
||||
Contenu
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<v-flex>
|
||||
<h6 class="title mb-3">Qui mange ?</h6>
|
||||
<v-card flat>
|
||||
<v-card-text>Liste des personnes</v-card-text>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
<v-flex>
|
||||
<h6 class="title mb-3">Règles spéciales</h6>
|
||||
<v-card :flat="!isIngredientListValid" hover class="pa-2" >
|
||||
<v-switch v-model="isIngredientsRuleUsed"
|
||||
label="Ingrédients à utiliser">
|
||||
</v-switch>
|
||||
<v-textarea label="Liste"></v-textarea>
|
||||
</v-card>
|
||||
<v-card :flat="!isNutritionalRuleUsed" hover class="pa-2 mt-2">
|
||||
<v-switch v-model="isNutritionalRuleUsed"
|
||||
label="Valeurs nutrionnelles"></v-switch>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
isMealUsed: false,
|
||||
isNutritionalRuleUsed: false,
|
||||
isIngredientsRuleUsed: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isIngredientListValid () { return true; }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
65
frontend/src/components/planning/Week.vue
Normal file
65
frontend/src/components/planning/Week.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<v-card class="pa-2">
|
||||
<v-calendar
|
||||
start="2019-04-22"
|
||||
type="week"
|
||||
locale="fr-fr"
|
||||
first-interval=2
|
||||
interval-count=4
|
||||
interval-minutes=240
|
||||
interval-height=150
|
||||
:show-interval-label="(i) => false"
|
||||
>
|
||||
<template v-slot:interval="{ date, hour }">
|
||||
<v-card flat>
|
||||
<v-toolbar flat dense>
|
||||
<v-toolbar-item>{{ intervals_heading[indexFromHour(hour)] }}</v-toolbar-item>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon flat color="light-green"><v-icon color="grey">more_horiz</v-icon></v-btn>
|
||||
</v-toolbar>
|
||||
<MealSlot v-if="mealsByDay[date]" :item="mealsByDay[date][indexFromHour(hour)]" />
|
||||
</v-card>
|
||||
</template>
|
||||
</v-calendar>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MealSlot from './Meal'
|
||||
const IntervalsHeading = ['Petit-déjeuner', 'Déjeuner', 'Goûter', 'Diner']
|
||||
|
||||
export default {
|
||||
props: ['meals'],
|
||||
components: { MealSlot },
|
||||
data () {
|
||||
return {
|
||||
intervals_heading: IntervalsHeading,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Maps interval's hour to index of a meal inside 'day' array
|
||||
// /!\ Tighly coupled to interval-* values
|
||||
indexFromHour (hour) {
|
||||
return hour / 4 - 2
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// Arrange meals as a 4-sized array for each day
|
||||
mealsByDay () {
|
||||
var result = {};
|
||||
for (var idx in this.meals) {
|
||||
var meal = this.meals[idx]
|
||||
var day = meal.day
|
||||
var meal_idx = Number(meal.kind)
|
||||
if (!(day in result)) {
|
||||
result[day] = [null, null, null, null]
|
||||
}
|
||||
result[day][meal_idx] = meal
|
||||
}
|
||||
console.log('computed', result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -72,6 +72,11 @@
|
||||
},
|
||||
markUpdated () {
|
||||
this.updated = true
|
||||
},
|
||||
resetFields () {
|
||||
this.item.ingredient = {}
|
||||
this.item.amount = null
|
||||
this.item.unit = null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
<template>
|
||||
<v-card class="pa-1">
|
||||
<v-layout column>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<strong>Ingrédients</strong>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<ingredient-details
|
||||
v-for="ingdt in edited_ingdts"
|
||||
:item="ingdt"
|
||||
:fields="fields"
|
||||
:editing="editing"
|
||||
@delete="deleteItem"
|
||||
@save="saveItem"
|
||||
/>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<!-- New ingredient in editing mode -->
|
||||
<v-card hover v-if="editing">
|
||||
<v-card-title>
|
||||
<strong>Ingrédients</strong>
|
||||
Nouveau
|
||||
</v-card-title>
|
||||
<v-card-text class="py-0">
|
||||
<ingredient-details
|
||||
v-for="ingdt in edited_ingdts"
|
||||
:item="ingdt"
|
||||
:fields="fields"
|
||||
:editing="editing"
|
||||
@delete="deleteItem"
|
||||
@save="saveItem"
|
||||
/>
|
||||
<!-- New ingredient in editing mode -->
|
||||
<p v-if="editing">
|
||||
(+) <ingredient-details
|
||||
:key="addItemKey"
|
||||
:item="newItem()"
|
||||
:fields="fields"
|
||||
:editing="editing"
|
||||
@create="createItem"
|
||||
/>
|
||||
</p>
|
||||
></ingredient-details>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -28,9 +38,9 @@
|
||||
import Ingredient from './Ingredient'
|
||||
function NullIngredient (recipe_id) {
|
||||
this.recipe = recipe_id
|
||||
this.ingredient = { name: "" }
|
||||
this.amount = 0
|
||||
this.unit = ""
|
||||
this.ingredient = { name: null }
|
||||
this.amount = null
|
||||
this.unit = null
|
||||
this.display = ""
|
||||
}
|
||||
|
||||
@@ -43,6 +53,12 @@
|
||||
return {
|
||||
fields: {},
|
||||
edited_ingdts: [],
|
||||
newItemKey: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
addItemKey () {
|
||||
return `add-item-${this.newItemKey}`
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -70,6 +86,7 @@
|
||||
.then(data => {
|
||||
this.edited_ingdts.push(data)
|
||||
//TODO: reset additionnal Ingredient
|
||||
this.newItemKey += 1
|
||||
})
|
||||
.catch(err => comp.errors.push(JSON.stringify(err)))
|
||||
},
|
||||
|
||||
@@ -5,8 +5,36 @@ Has read/edit modes
|
||||
-->
|
||||
<template>
|
||||
<v-container fluid>
|
||||
<v-layout wrap>
|
||||
<v-flex xs10> <!-- Heading -->
|
||||
<v-layout wrap align-items-center>
|
||||
<v-flex shrink> <!-- Buttons -->
|
||||
<v-layout column>
|
||||
<!-- Add to planning -->
|
||||
<v-bottom-sheet v-show="!editing"
|
||||
v-model="sheetSelectSlot"
|
||||
>
|
||||
<template v-slot:activator>
|
||||
<v-btn fab dark small
|
||||
color="success"
|
||||
><v-icon>add</v-icon></v-btn>
|
||||
</template>
|
||||
<!-- Put a meal picker here... -->
|
||||
<v-card elevation="10" style="min-height: 300px;">
|
||||
<v-card-text>
|
||||
Content of bottom sheet
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-bottom-sheet>
|
||||
<!-- Edit mode -->
|
||||
<v-btn fab dark small
|
||||
color="error"
|
||||
@click="switchEdit"
|
||||
>
|
||||
<v-icon v-if="editing">save</v-icon>
|
||||
<v-icon v-else>edit</v-icon>
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
</v-flex>
|
||||
<v-flex> <!-- Heading -->
|
||||
<v-alert v-for="(error,idx) in errors"
|
||||
:key="`alert-${idx}`"
|
||||
:value="true">
|
||||
@@ -31,51 +59,17 @@ Has read/edit modes
|
||||
/>
|
||||
</template>
|
||||
</v-flex>
|
||||
<v-flex xs2> <!-- Buttons -->
|
||||
<!-- Add to planning -->
|
||||
<v-bottom-sheet v-show="!editing"
|
||||
v-model="sheetSelectSlot"
|
||||
>
|
||||
<template v-slot:activator>
|
||||
<v-btn
|
||||
fab dark
|
||||
color="success"
|
||||
><v-icon>add</v-icon></v-btn>
|
||||
</template>
|
||||
<!-- Put a meal picker here... -->
|
||||
<v-card elevation="10" style="min-height: 300px;">
|
||||
<v-card-text>
|
||||
Content of bottom sheet
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-bottom-sheet>
|
||||
<!-- Edit mode -->
|
||||
<v-fab-transition>
|
||||
<v-btn
|
||||
fab dark
|
||||
:small="!editing"
|
||||
color="error"
|
||||
@click="switchEdit"
|
||||
>
|
||||
<v-icon v-if="editing">save</v-icon>
|
||||
<v-icon v-else>edit</v-icon>
|
||||
</v-btn>
|
||||
</v-fab-transition>
|
||||
</v-flex>
|
||||
<v-flex xs12 md4>
|
||||
<IngredientList v-if="!isLoading"
|
||||
:editing="editing"
|
||||
:recipe="item.id"
|
||||
:ingredients="item.ingredients"
|
||||
/>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<IngredientList v-if="!isLoading"
|
||||
:editing="editing"
|
||||
:recipe="item.id"
|
||||
:ingredients="item.ingredients"
|
||||
/>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RField from '../RessourceField'
|
||||
import { RecipeCategories } from '../../App'
|
||||
import api from '../../api.js'
|
||||
import NotFound from '../../routes/NotFound'
|
||||
const IngredientList = import('./IngredientList')
|
||||
@@ -98,22 +92,12 @@ export default {
|
||||
RField,
|
||||
},
|
||||
data() {
|
||||
// Transform categories for v-select component
|
||||
var select_items = []
|
||||
Object.keys(RecipeCategories)
|
||||
.map(function(key) {
|
||||
select_items.push({
|
||||
text: RecipeCategories[key],
|
||||
value: key,
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
isLoading: true,
|
||||
sheetSelectSlot: false,
|
||||
editing: false,
|
||||
select_items,
|
||||
categories: RecipeCategories,
|
||||
select_items: [],
|
||||
categories: [],
|
||||
errors: [],
|
||||
// Will be populated by initRecipeItem method
|
||||
item: new NullRecipe,
|
||||
@@ -165,16 +149,16 @@ export default {
|
||||
.then(() => this.isLoading = false)
|
||||
}
|
||||
},
|
||||
updateRecip: function(form_data) {
|
||||
console.log(form_data);
|
||||
var messages = [];
|
||||
return messages;
|
||||
}
|
||||
},
|
||||
created () {
|
||||
api.recipes.options()
|
||||
.then(data => this.fields = data.actions.POST)
|
||||
.then(() => console.log(this.fields))
|
||||
.then(data => {
|
||||
this.fields = data.actions.POST
|
||||
// Transform categories into selectable
|
||||
var cats = this.fields.category.choices
|
||||
Object.keys(cats)
|
||||
.map( (key) => this.categories[key] = cats[key].display_name )
|
||||
})
|
||||
},
|
||||
beforeRouteEnter (to, from, next) {
|
||||
next(vm => {
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
/* List view of recipes */
|
||||
<template>
|
||||
<v-list>
|
||||
<template v-for="(item, idx) in items">
|
||||
<v-list-tile
|
||||
:key="item.title"
|
||||
:to="`/recipes/id/${item.id}`">
|
||||
<v-list-tile-content>
|
||||
{{item.name}}
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
<v-divider v-if="idx < (items.length - 1)"
|
||||
:key="idx"></v-divider>
|
||||
</template>
|
||||
</v-list>
|
||||
<v-card>
|
||||
<!-- Categories list -->
|
||||
<v-tabs fixed-tabs slider-color="light-green" v-model="active" :mandatory="false">
|
||||
<template v-for="(name, id) in categories">
|
||||
<v-tab
|
||||
:key="id" >
|
||||
{{ name }}
|
||||
</v-tab>
|
||||
<v-tab-item :key="id" transition="slide-x-transition">
|
||||
<v-card flat>
|
||||
<v-list>
|
||||
<template v-for="(item, idx) in content[id]">
|
||||
<v-list-tile
|
||||
:key="item.title"
|
||||
:to="`/recipes/id/${item.id}`">
|
||||
<v-list-tile-content>
|
||||
{{item.name}}
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</v-tab-item>
|
||||
</template>
|
||||
</v-tabs>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -21,10 +34,12 @@ import api from '../../api.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
active: null,
|
||||
categories: {},
|
||||
// Recipes from db grouped by categories id
|
||||
content: {
|
||||
0: [], 1: [], 2: [], 3: []
|
||||
}
|
||||
0: [], 1: [], 2: [], 3: []
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -32,7 +47,7 @@ export default {
|
||||
return this.content[this.$route.params.cat];
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
mounted: function() {
|
||||
api.recipes.list()
|
||||
.then(data => {
|
||||
for (var idx in data) {
|
||||
@@ -40,6 +55,17 @@ export default {
|
||||
this.content[parseInt(obj.category)].push(obj);
|
||||
}
|
||||
})
|
||||
.then(() => console.log('list gathered !'))
|
||||
|
||||
api.recipes.options()
|
||||
.then(opts => {
|
||||
let cats = opts.actions.POST.category.choices
|
||||
this.categories = {}
|
||||
for (var idx in cats) {
|
||||
let cat = cats[idx]
|
||||
this.categories[parseInt(cat.value)] = cat.display_name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,6 @@ import Planning from './routes/Planning'
|
||||
|
||||
import RecipeDetails from './components/recipes/RecipeDetails'
|
||||
import RecipeList from './components/recipes/RecipeList'
|
||||
import CategoryTabs from './components/CategoryTabs'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
@@ -20,17 +19,16 @@ const router = new Router({
|
||||
},
|
||||
|
||||
{ path: '/recipes',
|
||||
components: { default: Recipes,
|
||||
extension: CategoryTabs, },
|
||||
component: Recipes,
|
||||
meta: { title: "Recettes",
|
||||
icon:"book" },
|
||||
children: [
|
||||
{ path: '',
|
||||
component: RecipeList,
|
||||
meta: {}},
|
||||
{ path: 'id/:id',
|
||||
component: RecipeDetails,
|
||||
meta: { subtitle: "Détails", } },
|
||||
{ path: 'category/:cat',
|
||||
component: RecipeList,
|
||||
meta: { subtitle: "Liste", } },
|
||||
{ path: '*',
|
||||
component: NotFound }
|
||||
]
|
||||
|
||||
@@ -1,91 +1,38 @@
|
||||
<template>
|
||||
<v-list two-line overflow-y>
|
||||
<template v-for="(item, index) in items">
|
||||
<v-subheader v-if="item.header"
|
||||
:key="item.header"
|
||||
>{{item.header}}</v-subheader>
|
||||
<v-divider v-else-if="item.divider"></v-divider>
|
||||
<v-list-tile v-else
|
||||
:key="index">
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-sub-title
|
||||
>{{ item.subtitle }}</v-list-tile-sub-title>
|
||||
<v-list-tile-title
|
||||
>{{ item.title }}</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action v-show="item.title">
|
||||
<v-btn icon color="primary" small
|
||||
:to="`/recipes/id/${item.id}`"
|
||||
><v-icon>remove_red_eye</v-icon></v-btn>
|
||||
</v-list-tile-action>
|
||||
<v-list-tile-action>
|
||||
<v-btn icon color="warning" small>
|
||||
<v-icon>
|
||||
{{ item.title ? 'delete' : 'add' }}
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
</template>
|
||||
</v-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
const MockPlanning = {
|
||||
"Lundi": {
|
||||
"Midi": "Raclette",
|
||||
"Soir": "Soupe",
|
||||
},
|
||||
"Mardi": {
|
||||
"Midi": null,
|
||||
"Soir": null,
|
||||
},
|
||||
"Mercredi": {
|
||||
"Midi": null,
|
||||
"Soir": null,
|
||||
},
|
||||
"Jeudi": {
|
||||
"Midi": null,
|
||||
"Soir": null,
|
||||
},
|
||||
"Vendredi": {
|
||||
"Midi": null,
|
||||
"Soir": null,
|
||||
},
|
||||
"Samedi": {
|
||||
"Midi": null,
|
||||
"Soir": null,
|
||||
},
|
||||
"Dimanche": {
|
||||
"Midi": null,
|
||||
"Soir": null,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
data() {
|
||||
var items = [];
|
||||
|
||||
for (var day in MockPlanning) {
|
||||
items.push({ divider: true })
|
||||
items.push({ header: day });
|
||||
for (var meal in MockPlanning[day]) {
|
||||
items.push({ id: 0, subtitle: meal, title: MockPlanning[day][meal] });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
items,
|
||||
toolActions: [
|
||||
{ icon: "find_replace", color: "success" },
|
||||
],
|
||||
planning: MockPlanning,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<template>
|
||||
<v-container>
|
||||
<v-alert :value="!(error == null)">{{ error }}</v-alert>
|
||||
<WeekView :meals="meals" />
|
||||
<OptionsView />
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WeekView from '../components/planning/Week'
|
||||
import OptionsView from '../components/planning/Options'
|
||||
import api from '../api.js'
|
||||
|
||||
export default {
|
||||
components: { WeekView, OptionsView },
|
||||
data () {
|
||||
return {
|
||||
meals: [],
|
||||
error: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchPlanning () {
|
||||
api.meals.list()
|
||||
.then(data => {
|
||||
this.meals = data
|
||||
})
|
||||
.catch(err => this.error = "Error fetching planning" + JSON.stringify(err))
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.fetchPlanning();
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
}),
|
||||
}
|
||||
<template>
|
||||
<keep-alive>
|
||||
<router-view :key="$route.fullPath"></router-view>
|
||||
</keep-alive>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"status":"done","publicPath":"http://localhost:8080/","chunks":{"null":[{"name":"0.js","publicPath":"http://localhost:8080/0.js","path":"/home/artus/Documents/cookAssistant/frontend/dist/0.js"}],"app":[{"name":"app.js","publicPath":"http://localhost:8080/app.js","path":"/home/artus/Documents/cookAssistant/frontend/dist/app.js"},{"name":"app.bb93e89c95f5e29b766f.hot-update.js","publicPath":"http://localhost:8080/app.bb93e89c95f5e29b766f.hot-update.js","path":"/home/artus/Documents/cookAssistant/frontend/dist/app.bb93e89c95f5e29b766f.hot-update.js"}]}}
|
||||
{"status":"done","publicPath":"http://localhost:8080/","error":"ModuleError","message":"Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):\n(Emitted value instead of an instance of Error) \n\n Errors compiling template:\n\n tag <v-card> has no matching end tag.\n\n 12 | >\n 13 | <template v-slot:interval=\"{ date, hour }\">\n 14 | <v-card>\n | ^^^^^^^^\n 15 | <v-toolbar>\n 16 | <v-toolbar-title>{{ intervals_heading[indexFromHour(hour)] }}</v-toolbar-title>\n","chunks":{"null":[{"name":"0.js","publicPath":"http://localhost:8080/0.js","path":"/home/artus/Documents/cookAssistant/frontend/dist/0.js"}],"app":[{"name":"app.js","publicPath":"http://localhost:8080/app.js","path":"/home/artus/Documents/cookAssistant/frontend/dist/app.js"},{"name":"app.5ec28f6394212113d31d.hot-update.js","publicPath":"http://localhost:8080/app.5ec28f6394212113d31d.hot-update.js","path":"/home/artus/Documents/cookAssistant/frontend/dist/app.5ec28f6394212113d31d.hot-update.js"}]}}
|
||||
@@ -1,12 +1,20 @@
|
||||
from django.db import models
|
||||
from recipe_book.models import Recipe
|
||||
# Create your models here.
|
||||
|
||||
class WeekPlanning(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class DayMeals(models.Model):
|
||||
midi = models.ForeignKey(
|
||||
Recipe,
|
||||
on_delete=models.CASCADE )
|
||||
from django.db import models
|
||||
from recipe_book.models import Recipe
|
||||
# Create your models here.
|
||||
|
||||
class Meal(models.Model):
|
||||
day = models.DateField()
|
||||
kind = models.SmallIntegerField(choices=(
|
||||
(0, 'Petit-déjeuner'),
|
||||
(1, 'Déjeuner'),
|
||||
(2, 'Goûter'),
|
||||
(3, 'Diner'),
|
||||
))
|
||||
dishes = models.ManyToManyField(
|
||||
'recipe_book.Recipe',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['day', 'kind'], name="unique_constraint"),
|
||||
]
|
||||
|
||||
8
planning/serializers.py
Normal file
8
planning/serializers.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from rest_framework import serializers as ser
|
||||
from .models import Meal
|
||||
|
||||
class MealSerializer(ser.ModelSerializer):
|
||||
class Meta:
|
||||
model = Meal
|
||||
fields = ('id', 'day', 'kind', 'dishes')
|
||||
depth = 1
|
||||
@@ -1,3 +1,9 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
from django.shortcuts import render
|
||||
from rest_framework import viewsets
|
||||
from .serializers import MealSerializer
|
||||
from .models import Meal
|
||||
|
||||
# Create your views here.
|
||||
class MealViewSet(viewsets.ModelViewSet):
|
||||
queryset = Meal.objects.all()
|
||||
serializer_class = MealSerializer
|
||||
|
||||
Reference in New Issue
Block a user