loads of work...

This commit is contained in:
2019-04-28 15:41:00 +02:00
parent 87d683d4af
commit d31b2dd348
21 changed files with 447 additions and 318 deletions

View File

@@ -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,//
}
},

View File

@@ -151,4 +151,5 @@ export default {
ingredients: mountNestedEndPoint(
`${baseUrl}/recips/*/amounts/`
),
meals: mountEndPoint(`${baseUrl}/planning/`),
}

View File

@@ -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>

View File

@@ -1,7 +1,6 @@
<template>
<v-select v-if="type == 'choice'"
:label="label"
placeholder="----"
:items="choices"
item-text="display_name"
:value="value"

View 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>

View 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>

View 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>

View 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>

View File

@@ -72,6 +72,11 @@
},
markUpdated () {
this.updated = true
},
resetFields () {
this.item.ingredient = {}
this.item.amount = null
this.item.unit = null
}
},
watch: {

View File

@@ -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)))
},

View File

@@ -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 => {

View File

@@ -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>

View File

@@ -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 }
]

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"}]}}