Implement vuex, New mobile view design

This commit is contained in:
Michael Götz 2018-04-14 16:25:37 +02:00
parent 586ae29549
commit ced89650f6
24 changed files with 651 additions and 309 deletions

View File

@ -7,7 +7,9 @@
</b-col>
<b-col cols="8" class="text-center">
<!--{% block headline %}{% endblock %}-->
<h2>BaStA</h2>
<h2>
<router-link :to="{name: 'home'}">BaStA</router-link>
</h2>
</b-col>
<b-col cols="2" class="text-right">
<top-menu></top-menu>
@ -15,7 +17,7 @@
</b-row>
<b-row id="content">
<b-col>
<b-col cols="12">
<router-view/>
</b-col>
</b-row>
@ -37,8 +39,11 @@
</b-col>
</b-row>
<b-row class="text-center pb-2 bg-dark font-white p-2">
<b-col>
© Copyright 2018, Michael Götz
<b-col cols="4" class="text-left">
<small>Version 0.1.1</small>
</b-col>
<b-col cols="8" class="text-right">
<small>© Copyright 2018, Michael Götz</small>
</b-col>
</b-row>
</footer>
@ -99,4 +104,9 @@
.font-white {
color: #ffffff;
}
#content {
background-color: #343a40;
color: white;
}
</style>

View File

@ -12,11 +12,13 @@ export default {
},
// Send a request to the login URL and save the returned JWT
login(creds) {
login(creds, store) {
console.log(store);
console.log('LOGIN');
window.axios.post(API_ROOT.concat('/token-auth/token/create/'), creds)
.then((response) => {
this.user.authenticated = true;
store.dispatch('login').then();
console.log(response);
localStorage.setItem('user', JSON.stringify(response));
// Redirect to a specified route
@ -32,21 +34,18 @@ export default {
},
// To log out
logout: function (redirect) {
logout: function (store) {
window.axios.post(API_ROOT.concat('/token-auth/token/destroy/'),)
.then((response) => {
this.user.authenticated = false;
store.dispatch('logout').then();
localStorage.removeItem('user');
console.log(response);
// Redirect to a specified route
window.axios.defaults.headers.common = {
'Authorization': '',
};
location.reload();
if (redirect) {
router.push({name: 'home'});
// router.go(redirect)
}
})
.catch(function (error) {
console.log(error);

View File

@ -1,5 +1,5 @@
<template>
<div id="home">
<b-col id="home">
<b-row id="home">
<b-col cols="12" sm="6" lg="6" xl="6">
<icon-link :config="foodConfig"></icon-link>
@ -16,7 +16,7 @@
<icon-link :config="linksConfig"></icon-link>
</b-col>
</b-row>
</div>
</b-col>
</template>
<script>

View File

@ -1,5 +1,5 @@
<template>
<div>
<b-col cols="12">
<b-tabs>
<b-tab title="Account Details" active>
<account-details :user="user"></account-details>
@ -8,7 +8,7 @@
<food></food>
</b-tab>
</b-tabs>
</div>
</b-col>
</template>
<script>

View File

@ -1,5 +1,5 @@
<template>
<div v-if="!isAuthenticated()">
<div v-if="!isAuthenticated()" class="col col-12">
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
<h2>Login</h2>
@ -38,7 +38,7 @@
</b-col>
</b-row>
</div>
<div v-else>
<div v-else class="col col-12">
<b-row class="justify-content-center text-center ml-md-auto mt-2">
<b-col cols="11" sm="10" md="7" lg="5" xl="4" class="border pb-2 pt-2">
Du wurdest erfolgreich eingeloggt.
@ -65,7 +65,7 @@
methods: {
onSubmit(evt) {
evt.preventDefault();
authentication.login({"username": this.form.username, "password": this.form.password}, -1);
authentication.login({"username": this.form.username, "password": this.form.password}, this.$store);
},
isAuthenticated: function () {
if (authentication.authenticated()) {

View File

@ -14,7 +14,7 @@
name: "Logout",
created() {
if (Auth.authenticated()) {
Auth.logout();
Auth.logout(this.$store);
}
setTimeout(function () {
router.go(-1);

View File

@ -1,24 +1,22 @@
<template>
<div>
<div v-if="!showFormular">
<b-row class="p-1">
<b-col cols="12" sm="12" md="12" lg="8" xl="8" class="p-3 bg-light text-dark">
<h3>{{user.username}}</h3>
<p><strong>Vorname:</strong> {{ user.first_name }}</p>
<p><strong>Nachname:</strong> {{ user.last_name }}</p>
<p><strong>E-Mail:</strong> {{ user.email }}</p>
<!--<a href="{{ url('change-account') }}">Bearbeiten</a>-->
<b-button :pressed.sync="showFormular" variant="primary">{{this.formularButtonLabel}}</b-button>
</b-col>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-4 text-center p-3">
<div class="p-3 bg-light text-dark">
<p>Date joined: {{ user.date_joined | formatDate}}</p>
<p>Last Login: {{ user.last_login | formatDate}}</p>
</div>
<div v-if="!showFormular" class="row p-1">
<b-col cols="12" sm="12" md="12" lg="8" xl="8" class="p-3 bg-light text-dark">
<h3>{{user.username}}</h3>
<p><strong>Vorname:</strong> {{ user.first_name }}</p>
<p><strong>Nachname:</strong> {{ user.last_name }}</p>
<p><strong>E-Mail:</strong> {{ user.email }}</p>
<!--<a href="{{ url('change-account') }}">Bearbeiten</a>-->
<b-button :pressed.sync="showFormular" variant="primary">{{this.formularButtonLabel}}</b-button>
</b-col>
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-4 text-center p-3">
<div class="p-3 bg-light text-dark">
<p>Date joined: {{ user.date_joined | formatDate}}</p>
<p>Last Login: {{ user.last_login | formatDate}}</p>
</div>
</b-row>
</div>
</div>
<div v-if="showFormular">
<div v-if="showFormular" class="row">
<account-formular :user="user"></account-formular>
<b-button :pressed.sync="showFormular" variant="primary">{{this.formularButtonLabel}}</b-button>
</div>

View File

@ -2,10 +2,10 @@
<div>
<h4>Essen</h4>
<div class="row p-1">
<div v-for="food in foods" class="col col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 p-2">
<b-row class="food border p-2">
<div v-for="food in foods" class="col col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 p-2 bg-light text-dark">
<b-row class="food p-2">
<b-col cols="4" class="image">
<a v-if="food.image" :href="food.image.thumb">
<a v-if="food.image" :href="food.image.image">
<img :src="food.image.thumb" class="media-object" alt="Bild" width="100%">
</a>
<a v-else :href="defaultImageUrl">

View File

@ -1,11 +1,11 @@
<template>
<div class="menu">
<div class="p-3 border border-dark rounded bg-light text-dark">
<div v-if="menu">
<div class="p-1 bg-light text-dark">
<div v-if="menu.date">
<p>{{ menu.date | formatDateWithWeekday}}</p>
<ul class="border">
<ul class="">
<li v-for="food in menu.menu" :data-food="food.id" :data-rating="food.rating" class="food-item media mb-2">
<single-menu :food="food" :defaultImageUrl="defaultImageUrl"></single-menu>
<single-menu :food="food"></single-menu>
</li>
</ul>
</div>
@ -21,14 +21,15 @@
export default {
name: "DayMenu",
props: ['menu', 'failMessage', 'defaultImageUrl'],
props: ['menu', 'failMessage'],
components: {SingleMenu},
}
</script>
<style scoped>
ul {
.menu, ul {
padding: 0;
}
</style>

View File

@ -1,44 +1,56 @@
<template>
<b-row>
<b-col cols="12" class="text-center border pt-2">
<b-row align-v="center">
<b-col cols="1"></b-col>
<b-col cols="10">
<h4>Detailansicht</h4>
<b-col cols="12">
<b-row align-v="center" class="bg-light text-dark">
<b-col cols="12" class="mb-0 mt-2 text-center">
<h5>{{food.name}}</h5>
</b-col>
<b-col cols="1">
<router-link :to="{name: 'food'}">Back</router-link>
</b-col>
</b-row>
</b-col>
<b-col cols="7" class="border">
<b-row>
<b-col class="border pt-2">
<allergens-overview :allergens="food.allergens"></allergens-overview>
<div v-if="!mobile" class="row">
<b-col cols="8">
<b-row>
<b-col cols="12">
<food-picture></food-picture>
</b-col>
</b-row>
<b-row>
<b-col cols="12">
<comments-overview></comments-overview>
</b-col>
</b-row>
</b-col>
</b-row>
<b-row>
<b-col class="border pt-2">
<price-overview :student="food.price_student" :employee="food.price_employee"
:guest="food.price_guest"></price-overview>
<b-col cols="4">
<b-row class="m-0">
<b-col cols="12" class="bg-light text-dark mb-2 mt-2 pt-2 pb-2">
<rating-combined></rating-combined>
</b-col>
<b-col cols="12" class="bg-light text-dark mb-2 pt-2 pb-2">
<price-overview></price-overview>
</b-col>
<b-col cols="12" class="bg-light text-dark pt-2 pb-2">
<allergens-overview></allergens-overview>
</b-col>
</b-row>
</b-col>
</b-row>
<b-row>
<b-col class="border pt-2">
<rating-combined v-on:updateFood="loadFood" :globalRating="food.rating" :foodId="food_id"></rating-combined>
</div>
<div v-else class="row">
<b-col cols="12">
<food-picture></food-picture>
</b-col>
</b-row>
<b-row>
<b-col class="border pt-2">
<comments-overview :comments="comments"></comments-overview>
<b-col cols="12" class="bg-light text-dark mb-2 mt-2 pt-2 pb-2">
<rating-combined></rating-combined>
</b-col>
</b-row>
</b-col>
<b-col cols="5" class="border">
<food-picture :image="food.image" :userFoodImage="userFoodImage"></food-picture>
<food-picture-upload v-on:updateImage="loadUserImage"></food-picture-upload>
<comment-box v-on:updateFood="loadFood" :foodId="food_id"></comment-box>
<b-col cols="12" class="bg-light text-dark mb-2 pt-2 pb-2">
<price-overview></price-overview>
</b-col>
<b-col cols="12" class="bg-light text-dark mb-2 pt-2 pb-2">
<allergens-overview></allergens-overview>
</b-col>
<b-col cols="12" class="bg-light text-dark pt-2 pb-2">
<comments-overview></comments-overview>
</b-col>
</div>
</b-col>
</b-row>
</template>
@ -50,8 +62,6 @@
import PriceOverview from "@/components/food/utils/PriceOverview";
import RatingCombined from "@/components/food/utils/RatingCombined";
import FoodPicture from "@/components/food/utils/FoodPicture";
import FoodPictureUpload from "@/components/food/utils/PictureUpload";
import CommentBox from "@/components/food/utils/CommentBox";
import CommentsOverview from "@/components/food/utils/CommentsOverview";
@ -62,65 +72,34 @@
PriceOverview,
RatingCombined,
FoodPicture,
FoodPictureUpload,
CommentBox,
CommentsOverview
CommentsOverview,
},
data() {
return {
food_id: '',
food: {},
comments: '',
show: true,
images: '',
form: {
file: '',
},
userFoodImage: '',
mobile: false,
};
}, created() {
this.loadFood();
this.loadUserImage();
this.addMobileChecker();
this.$store
.dispatch('loadDetailedFood', {foodId: this.$route.params.id})
.then();
this.$store.dispatch('loadDefaultImageLocation').then();
},
computed: {
food() {
return this.$store.getters.getDetailedFood;
},
},
methods: {
loadFood() {
window.axios.get(CONFIG.API_ROOT_FOOD
.concat('/meals/').concat(this.$route.params.id))
.then(response => {
// JSON responses are automatically parsed.
console.log(JSON.parse(JSON.stringify(response.data)));
this.food = response.data;
this.food_id = this.food.id;
})
.catch(e => {
});
window.axios.get(CONFIG.API_ROOT_FOOD
.concat('/meals/').concat(this.$route.params.id).concat('/comments'))
.then(response => {
console.log(JSON.parse(JSON.stringify(response.data)));
this.comments = response.data;
})
.catch(e => {
});
},
loadUserImage() {
if (authentication.authenticated()) {
let url = CONFIG.API_ROOT_ACCOUNT + '/food/images/?food_id=' + this.$route.params.id;
window.axios
.get(url)
.then(response => {
console.log('LOG IMAGE');
if (response.data.length > 0) {
this.userFoodImage = response.data[0].image.image;
}
})
.catch()
}
},
isAuthenticated: function () {
return authentication.authenticated();
},
addMobileChecker() {
this.mobile = window.innerWidth < 768;
window.addEventListener('resize', () => {
this.mobile = window.innerWidth < 768;
}, true);
},
}
}
</script>

View File

@ -1,12 +1,31 @@
<template>
<b-col>
<b-row>
<div class="col col-6" v-for="location in locations">
<tab-menu :title="location.short" :location="location.id" :defaultImageUrl="defaultImageUrl"></tab-menu>
<b-row>
<b-col class="food-overview">
<div v-if="!mobile" class="row">
<div v-for="location in locations" class="tab-menu-wrapper col col-6">
<h3>{{ location.short }} </h3>
<tab-menu :title="location.short" :location="location.id"></tab-menu>
</div>
</div>
</b-row>
</b-col>
<div v-else class="row">
<div v-for="location in locations" class="col col-12">
<b-card no-body class="mt-3 rounded-0">
<b-card-header header-tag="header" class="p-0" role="tab">
<b-btn block class="rounded-0" href="#" v-b-toggle="location.id" variant="light">
{{ location.short }}
</b-btn>
</b-card-header>
<b-collapse :id="location.id" accordion="my-accordion" role="tabpanel">
<b-card-body class="p-0 m-0">
<tab-menu :title="location.short" :location="location.id"></tab-menu>
</b-card-body>
</b-collapse>
</b-card>
</div>
<div class="clearfix m-3"></div>
</div>
</b-col>
</b-row>
</template>
<script>
@ -19,34 +38,32 @@
components: {TabMenu},
data() {
return {
defaultImageUrl: '',
locations: [],
mobile: false,
}
},
computed: {
locations() {
return this.$store.getters.getStudWuerzburgLocations;
},
},
created() {
axios.get(CONFIG.API_ROOT_FOOD.concat('/menus/locations'))
.then(response => {
// JSON responses are automatically parsed.
console.log(JSON.parse(JSON.stringify(response.data)));
this.locations = response.data;
})
.catch(e => {
console.error(e)
});
axios.get(CONFIG.API_ROOT_FOOD.concat('/meals/images/default'))
.then(response => {
// JSON responses are automatically parsed.
console.log(JSON.parse(JSON.stringify(response.data)));
this.defaultImageUrl = response.data.image;
})
.catch(e => {
console.error(e)
});
this.addMobileChecker();
this.$store.dispatch('loadLocations').then();
this.$store.dispatch('loadDefaultImageLocation').then();
},
methods: {
addMobileChecker() {
this.mobile = window.innerWidth < 768;
window.addEventListener('resize', () => {
this.mobile = window.innerWidth < 768;
}, true);
},
}
}
</script>
<style scoped>
.tab-menu-wrapper {
padding: 0;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<b-col>
<b-row>
<b-col cols="3">
<b-col cols="3" class="p-0">
<a v-if="food.image" :href="food.image.thumb" :data-lightbox="title"
:data-title="food.name">
<img :src="food.image.thumb" class="media-object" alt="Bild" width="80px">
@ -13,23 +13,34 @@
alt="Bild" width="80px">
</a>
</b-col>
<b-col cols="7" class="media-body">
<div class="food-name">
<router-link :to="{name: 'food-detail', params:{id: food.id}}">{{ food.name }}</router-link>
</div>
<fa-rating :glyph="star"
:spacing="-1"
:inactive-color="'#cfcfcf'"
:active-color="'#0074D9'"
:increment="0.5"
:fixed-points="2"
:show-rating="false"
:item-size="15"
:inline="true"
v-model="userRating">
</fa-rating>
<b-col cols="9" class="media-body">
<b-row>
<b-col cols="12" sm="12" md="12" lg="12" xl="12">
<div class="food-name">
<router-link :to="{name: 'food-detail', params:{id: food.id}}">{{ food.name }}</router-link>
</div>
</b-col>
</b-row>
<b-row>
<b-col cols="8" sm="8" md="8" lg="8" xl="8">
<fa-rating :glyph="star"
:spacing="-1"
:inactive-color="'#cfcfcf'"
:active-color="'#0074D9'"
:increment="0.5"
:fixed-points="2"
:show-rating="false"
:item-size="15"
:inline="true"
v-model="userRating">
</fa-rating>
</b-col>
<div v-if="food.price_student" class="col col-4">
<span class="float-right">{{ food.price_student }} </span>
</div>
</b-row>
</b-col>
<div v-if="food.price_student" class="col-2"><span class="float-right">{{ food.price_student }}</span></div>
</b-row>
</b-col>
</template>
@ -43,21 +54,27 @@
export default {
name: "SingleMenu",
props: ['food', 'title', 'defaultImageUrl'],
props: ['food', 'title'],
components: {FaRating},
data() {
return {
userRating: this.food.rating,
star: '',
};
}, created() {
},
computed: {
defaultImageUrl() {
return this.$store.getters.getDefaultImageLocation
},
},
created() {
// register the icon
this.star = Star;
// console.log(this.food.rating);
// this.userRating = this.food.rating;
},
watch: {
userRating: function (newRating) {
userRating: function (newRating, oldRating) {
if (authentication.authenticated()) {
let url = CONFIG.API_ROOT_FOOD.concat('/meals/').concat(this.food.id).concat('/rating');
window.axios
@ -68,6 +85,7 @@
}
)
.catch();
console.log(oldRating);
} else {
router.push({name: 'login'})
}

View File

@ -1,12 +1,11 @@
<template>
<div>
<h3>{{ title }} </h3>
<div class="tab-menu m-0 p-1">
<b-tabs>
<b-tab title="Daily" active>
<day-menu :menu="dayMenu" :defaultImageUrl="defaultImageUrl"></day-menu>
<b-tab title="Heute" active title-link-class="rounded-0">
<day-menu :menu="dayMenu"></day-menu>
</b-tab>
<b-tab title="Weekly">
<week-menu :menus="weekMenus" :defaultImageUrl="defaultImageUrl"></week-menu>
<b-tab title="Woche" title-link-class="rounded-0">
<week-menu :menus="weekMenus"></week-menu>
</b-tab>
</b-tabs>
</div>
@ -20,7 +19,7 @@
export default {
name: "TabMenu",
props: ['title', 'location', 'defaultImageUrl'],
props: ['title', 'location'],
components: {DayMenu, WeekMenu},
data() {
return {
@ -38,8 +37,6 @@
.concat('&startdate=').concat(today_yyyymmdd)
.concat('&enddate=').concat(today_yyyymmdd))
.then(response => {
// JSON responses are automatically parsed.
console.log(JSON.parse(JSON.stringify(response.data)));
if (response.data.length == 1) {
this.dayMenu = response.data[0];
@ -79,5 +76,8 @@
</script>
<style scoped>
.tab-menu {
margin: 5px;
padding: 10px;
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<div class="p-3 border border-dark rounded bg-light text-dark">
<div v-if="menus">
<div v-for="menu in menus" class="menu">
<day-menu :menu="menu" :defaultImageUrl="defaultImageUrl"></day-menu>
<div class="p-0 bg-light text-dark">
<div v-if="menus.length > 0">
<div v-for="menu in menus" class="menu-wrapper">
<day-menu :menu="menu"></day-menu>
</div>
</div>
<p v-else>Keine Daten gefunden.</p>
@ -14,11 +14,18 @@
export default {
name: "WeekMenu",
props: ['menus', 'failMessage', 'defaultImageUrl'],
props: ['menus', 'failMessage'],
components: {DayMenu: DayMenu},
}
</script>
<style scoped>
.menu-wrapper:first-child {
border-top: none;
}
.menu-wrapper {
border: 1px solid rgba(0, 0, 0, 0.125);
margin-bottom: 5px;
}
</style>

View File

@ -1,23 +1,58 @@
<template>
<div>
<div v-if="!mobile">
<h5>Allergene</h5>
<div v-if="allergens">
<div v-if="allergens.length == 0">
Keine Angaben gefunden.
</div>
<div v-else>
<ul>
<li v-for="allergen in allergens">{{ allergen.name }}</li>
</ul>
</div>
<div v-else>
Keine Angaben gefunden.
</div>
</div>
<div v-else>
<b-card no-body class="mb-1 rounded-0">
<b-card-header header-tag="header" class="p-0" role="tab">
<b-btn block href="#" v-b-toggle.allergensBox variant="light">Allergene</b-btn>
</b-card-header>
<b-collapse id="allergensBox" accordion="allergensBox-accordion" role="tabpanel">
<b-card-body>
<div v-if="allergens.length == 0">
Keine Angaben gefunden.
</div>
<div v-else>
<ul>
<li v-for="allergen in allergens">{{ allergen.name }}</li>
</ul>
</div>
</b-card-body>
</b-collapse>
</b-card>
</div>
</template>
<script>
export default {
name: "AllergensOverview",
props: ['allergens'],
data() {
return {mobile: false};
},
computed: {
allergens() {
return this.$store.getters.getDetailedFoodUserAllergens
},
},
created() {
this.addMobileChecker();
},
methods: {
addMobileChecker() {
this.mobile = window.innerWidth < 768;
window.addEventListener('resize', () => {
this.mobile = window.innerWidth < 768;
}, true);
},
}
}
</script>

View File

@ -1,29 +1,37 @@
<template>
<div v-if="isAuthenticated()">
<h5>Kommentare</h5>
<b-form @submit="onSubmit">
<b-form-group id="commentShort"
label="Überschrift, Zusammenfassung"
label-for="commentShort">
<b-form-input id="commentShort"
type="text"
v-model="form.commentShort"
required
placeholder="Enter Title">
</b-form-input>
</b-form-group>
<b-form-group id="commentText"
label="Kommentar"
label-for="commentText">
<b-form-textarea id="commentText"
v-model="form.commentText"
placeholder="Enter something"
:rows="3"
:max-rows="6">
</b-form-textarea>
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
<b-card no-body class="mb-1 rounded-0">
<b-card-header header-tag="header" class="p-0" role="tab">
<b-btn block href="#" v-b-toggle.commentBox variant="light">Kommentieren</b-btn>
</b-card-header>
<b-collapse id="commentBox" accordion="commentBox-accordion" role="tabpanel">
<b-card-body>
<b-form @submit="onSubmit">
<b-form-group id="commentShort"
label="Überschrift, Zusammenfassung"
label-for="commentShort">
<b-form-input id="commentShort"
type="text"
v-model="form.commentShort"
required
placeholder="Enter Title">
</b-form-input>
</b-form-group>
<b-form-group id="commentText"
label="Kommentar"
label-for="commentText">
<b-form-textarea id="commentText"
v-model="form.commentText"
placeholder="Enter something"
:rows="3"
:max-rows="6">
</b-form-textarea>
</b-form-group>
<b-button type="submit" variant="primary">Speichern</b-button>
</b-form>
</b-card-body>
</b-collapse>
</b-card>
</div>
</template>
@ -33,7 +41,6 @@
export default {
name: "CommentBox",
props: ['foodId'],
data() {
return {
form: {
@ -42,19 +49,24 @@
}
};
},
computed: {
foodID() {
return this.$store.getters.getDetailedFoodID
},
},
created() {
this.getUserComment();
},
watch: {
foodId: function () {
foodID: function () {
this.getUserComment();
},
},
methods: {
onSubmit(evt) {
evt.preventDefault();
if (authentication.authenticated() && this.foodId) {
let url = CONFIG.API_ROOT_FOOD + '/meals/' + this.foodId + '/comment';
if (authentication.authenticated() && this.foodID) {
let url = CONFIG.API_ROOT_FOOD + '/meals/' + this.$route.params.id + '/comment';
let jsonData = {
"title": this.form.commentShort,
"description": this.form.commentText
@ -62,7 +74,7 @@
window.axios
.post(url, jsonData)
.then(response => {
this.$emit('updateFood');
this.$store.dispatch('loadDetailedFood', {foodId: this.$route.params.id}).then();
})
.catch()
}
@ -71,12 +83,11 @@
return authentication.authenticated();
},
getUserComment: function () {
if (authentication.authenticated() && this.foodId) {
let url = CONFIG.API_ROOT_ACCOUNT + '/food/comments/?food_id=' + this.foodId;
if (authentication.authenticated() && this.foodID) {
let url = CONFIG.API_ROOT_ACCOUNT + '/food/comments/?food_id=' + this.$route.params.id;
window.axios
.get(url)
.then(response => {
console.log(response.data);
if (response.data.length > 0) {
this.form.commentText = response.data[0].description;
this.form.commentShort = response.data[0].title;

View File

@ -1,19 +1,33 @@
<template>
<b-row>
<b-col>
<b-row class="bg-light text-dark m-0">
<b-col class="pt-2">
<h5>Kommentare</h5>
<comment-box></comment-box>
</b-col>
<div v-for="comment in comments" class="col col-12 border">
<h5>{{comment.title}}</h5>
<p>{{comment.description}}</p>
<div v-for="comment in comments" class="col col-12">
<b-card :title="comment.title">
<p class="card-text">
{{comment.description}}
</p>
</b-card>
<p></p>
</div>
</b-row>
</template>
<script>
import CommentBox from "@/components/food/utils/CommentBox";
export default {
name: "CommentsOverview",
props: ['comments'],
components: {
CommentBox
},
computed: {
comments() {
return this.$store.getters.getDetailedFoodComments
}
},
}
</script>

View File

@ -1,39 +1,43 @@
<template>
<div v-if="userFoodImage">
<b-img :src="userFoodImage" fluid-grow alt="Fluid-Grow image"/>
</div>
<div v-else-if="image">
<b-img :src="image" fluid-grow alt="Fluid-Grow image"/>
</div>
<div v-else>
<b-img img :src="defaultImage" fluid-grow alt="Fluid-Grow image"/>
<div>
<food-picture-upload id="pic-upload"></food-picture-upload>
<div v-if="userFoodImage">
<b-img :src="userFoodImage" fluid-grow alt="Fluid-Grow image"/>
</div>
<div v-else-if="foodImage">
<b-img :src="foodImage" fluid-grow alt="Fluid-Grow image"/>
</div>
<div v-else>
<b-img img :src="defaultImage" fluid-grow alt="Fluid-Grow image"/>
</div>
</div>
</template>
<script>
import * as CONFIG from '../../../config'
import FoodPictureUpload from "@/components/food/utils/PictureUpload";
export default {
name: "FoodPicture",
props: ['image', 'userFoodImage'],
data() {
return {
defaultImage: '',
};
},
created() {
window.axios
.get(CONFIG.API_ROOT_FOOD.concat('/meals/images/default'))
.then(response => {
console.log(JSON.parse(JSON.stringify(response.data)));
this.defaultImage = response.data.image;
})
.catch(e => {
});
components: {FoodPictureUpload},
computed: {
defaultImage() {
return this.$store.getters.getDefaultImageLocation
},
foodImage() {
return this.$store.getters.getDetailedFoodImage
},
userFoodImage() {
return this.$store.getters.getDetailedFoodUserImage
},
},
}
</script>
<style scoped>
#pic-upload {
position: absolute;
right: 10px;
top: 10px;
margin-right: 10px;
}
</style>

View File

@ -1,47 +1,86 @@
<template>
<div v-if="isAuthenticated()">
<h4>Bilder</h4>
<p>Bild senden</p>
<b-form @submit="onSubmit">
<b-form-file v-model="form.file" :state="Boolean(form.file)" placeholder="Choose a file..."></b-form-file>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
<div v-if="showFileUpload">
<div class="fileUpload">
<label class="inputLabel" for="file">
<icon :name="'upload'" :scale="1" class="inputLabel-icon"></icon>
</label>
<input v-on:change="onSubmit" type="file" name="file" id="file" class="inputfile" ref="userFile"/>
</div>
</div>
<div v-else-if="showProgressBar" class="progress-bar-wrapper">
<b-progress :value="uploadProgress" :max="100" variant="success" height="10px"
class="w-100"></b-progress>
</div>
<div v-else class="success-response">
<icon :name="'check-circle'" :scale="2" class="success"></icon>
</div>
</div>
</template>
<script>
import * as CONFIG from '../../../config'
import authentication from '../../../authentication'
import Icon from 'vue-awesome/components/Icon'
import 'vue-awesome/icons/check-circle';
import 'vue-awesome/icons/upload';
export default {
name: "PictureUpload",
props: ['image'],
components: {Icon},
data() {
return {
form: {
file: '',
}
showFileUpload: true,
showProgressBar: false,
uploadProgress: 0,
};
},
watch: {
file: function (newForm) {
console.log('Pic Changed');
this.onSubmit();
},
},
watch: {
file: (newFile) => {
this.onSubmit()
}
},
methods: {
isAuthenticated: function () {
return authentication.authenticated();
},
onSubmit(evt) {
evt.preventDefault();
var formData = new FormData();
console.log(this.form.file);
// append Blob/File object
formData.append('image', this.form.file, this.form.file.name);
onSubmit() {
this.showFileUpload = false;
this.showProgressBar = true;
let url = CONFIG.API_ROOT_FOOD + '/meals/' + this.$route.params.id + '/image';
let config = {
onUploadProgress: (progressEvent) => {
console.log(Math.round(progressEvent.loaded / progressEvent.total * 100));
this.uploadProgress = Math.round(progressEvent.loaded / progressEvent.total * 100);
}
};
let formData = new FormData();
console.log(this.$refs.userFile.files);
formData.append('image', this.$refs.userFile.files[0], this.$refs.userFile.files[0].name);
window.axios.post(CONFIG.API_ROOT_FOOD
.concat('/meals/').concat(this.$route.params.id).concat('/image'), formData)
window.axios
.post(url, formData, config)
.then(response => {
// JSON responses are automatically parsed.
this.$emit('updateImage');
console.log(response.data.image);
this.$store
.dispatch('setUserImage', {image: response.data.image})
.then();
this.showProgressBar = false;
this.showFileUpload = false;
this.uploadProgress = 0;
setTimeout(() => {
this.showFileUpload = true;
}, 5000);
})
.catch(e => {
console.log(JSON.parse(JSON.stringify(e.response)));
console.log(e.response);
});
},
}
@ -49,5 +88,56 @@
</script>
<style scoped>
.success {
color: #57d25f;
}
.inputfile {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.inputfile + label, .inputLabel {
cursor: pointer; /* "hand" cursor */
}
.success-response {
background-color: #ffffff;
padding: 5px;
height: 42px;
}
.progress-bar-wrapper {
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
padding-bottom: 5px;
background-color: #ffffff;
width: 60px;
}
.inputLabel, .progress-bar-wrapper {
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
background-color: #ffffff;
}
.inputLabel:hover {
outline: 1px dotted #000;
outline: -webkit-focus-ring-color auto 5px;
}
.inputLabel-icon {
color: #303030;
}
.inputfile:focus + label, .inputLabel:focus {
outline: 1px dotted #000;
outline: -webkit-focus-ring-color auto 5px;
}
</style>

View File

@ -3,18 +3,18 @@
<h5>Preise</h5>
<div class="student">
Student:
<span v-if="student">{{ student }}</span>
<span v-else>Keine Angaben gefunden</span>
<span v-if="prices.student">{{ prices.student }} </span>
<span v-else>n/a</span>
</div>
<div class="price-employee">
Employee:
<span v-if="employee">{{ employee }}</span>
<span v-else>Keine Angaben gefunden</span>
Mitarbeiter:
<span v-if="prices.employee">{{ prices.employee }} </span>
<span v-else>n/a</span>
</div>
<div class="price-guest">
Guest:
<span v-if="guest">{{ guest }}</span>
<span v-else>Keine Angaben gefunden</span>
Gast:
<span v-if="prices.guest">{{ prices.guest }} </span>
<span v-else>n/a</span>
</div>
</div>
</template>
@ -22,7 +22,11 @@
<script>
export default {
name: "PriceOverview",
props: ['student', 'employee', 'guest'],
computed: {
prices() {
return this.$store.getters.getDetailedFoodPrices
}
}
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<div>
<div id="rating" class="mt-3"><strong>Rating:</strong>
<div id="rating" class=""><strong>Rating:</strong>
<fa-rating :glyph="star"
:spacing="-1"
:inactive-color="'#cfcfcf'"
@ -14,7 +14,7 @@
v-model="globalRating">
</fa-rating>
</div>
<div v-if="isAuthenticated()" id="user-rating" class="mt-3"><strong>Dein Rating:</strong>
<div v-if="isAuthenticated()" id="user-rating" class=""><strong>Dein Rating:</strong>
<fa-rating :glyph="star"
:spacing="-1"
:inactive-color="'#cfcfcf'"
@ -40,7 +40,6 @@
export default {
name: "RatingCombined",
components: {FaRating},
props: ['globalRating', 'foodId'],
data() {
return {
star: Star,
@ -48,24 +47,32 @@
loaded: false,
};
},
computed: {
foodID() {
return this.$store.getters.getDetailedFoodID
},
globalRating() {
return this.$store.getters.getDetailedFoodRating
},
},
created() {
if (window.authentication.authenticated() && this.foodId) {
if (window.authentication.authenticated() && this.foodID) {
this.loadUserRating();
this.loaded = true;
}
},
watch: {
foodId: function () {
foodID: function () {
this.loadUserRating();
this.loaded = true;
},
userRating: function (newRating) {
if (authentication.authenticated() && this.loaded) {
let url = CONFIG.API_ROOT_FOOD.concat('/meals/').concat(this.foodId).concat('/rating');
let url = CONFIG.API_ROOT_FOOD + '/meals/' + this.$route.params.id + '/rating';
window.axios
.post(url, {rating: newRating})
.then(response => {
this.$emit('updateFood');
this.$store.dispatch('loadDetailedFood', {foodId: this.$route.params.id}).then()
}
)
.catch();
@ -79,16 +86,12 @@
return authentication.authenticated();
},
loadUserRating: function () {
console.log('LOAD USER RATING');
if (authentication.authenticated()) {
console.log(this.foodId);
let url = CONFIG.API_ROOT_ACCOUNT.concat('/food/ratings/?food_id=').concat(this.foodId);
let url = CONFIG.API_ROOT_ACCOUNT + '/food/ratings/?food_id=' + this.$route.params.id;
window.axios
.get(url)
.then(response => {
if (response.data.length > 0) {
console.log('RATING');
console.log(response.data);
this.userRating = response.data[0].rating;
}
}

View File

@ -11,7 +11,7 @@
</strong>
</div>
<div class="mobile-nav-item">
<router-link to="food">Food</router-link>
<router-link :to="{name: 'food'}">Food</router-link>
</div>
<div class="mobile-nav-item">
<a class="menu-item" href="#">Events</a>

View File

@ -1,6 +1,8 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import Vuex from 'vuex';
import {store} from './store.js';
import App from './App'
import router from './router'
import BootstrapVue from 'bootstrap-vue'
@ -11,6 +13,7 @@ import axios from 'axios'
import authentication from "./authentication";
import Vuelidate from 'vuelidate'
Vue.use(Vuex);
Vue.use(Vuelidate);
Vue.use(BootstrapVue);
Vue.use(authentication.authenticated());
@ -25,7 +28,8 @@ Vue.filter('formatDate', function (value) {
Vue.filter('formatDateWithWeekday', function (value) {
if (value) {
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
// var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var days = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
let date = new Date(value);
return days[date.getDay()] + ', ' + date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear()
}
@ -61,6 +65,7 @@ axios.defaults.xsrfHeaderName = 'HTTP_X_CSRFTOKEN';
new Vue({
el: '#app',
router,
store,
components: {App},
template: '<App/>'
});

View File

@ -1,10 +1,157 @@
import Vue from 'vue'
import Vuex from 'vuex'
import * as CONFIG from './config'
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
authenticated: false,
}
user: {
authenticated: true,
},
foodAppStudWue: {
locations: [],
defaultImage: '',
currentFood: {},
currentFoodComments: [],
currentFoodUserImage: '',
},
},
getters: {
isAuthenticated: state => {
return state.user.authenticated
},
getStudWuerzburgLocations: state => {
return state.foodAppStudWue.locations
},
getDefaultImageLocation: state => {
return state.foodAppStudWue.defaultImage
},
getDetailedFood: state => {
return state.foodAppStudWue.currentFood
},
getDetailedFoodID: state => {
return state.foodAppStudWue.currentFood.id
},
getDetailedFoodRating: state => {
return state.foodAppStudWue.currentFood.rating
},
getDetailedFoodImage: state => {
return state.foodAppStudWue.currentFood.image
},
getDetailedFoodUserImage: state => {
return state.foodAppStudWue.currentFoodUserImage
},
getDetailedFoodUserAllergens: state => {
return state.foodAppStudWue.currentFood.allergens
},
getDetailedFoodPrices: state => {
return {
employee: state.foodAppStudWue.currentFood.price_employee,
guest: state.foodAppStudWue.currentFood.price_guest,
student: state.foodAppStudWue.currentFood.price_student,
}
},
getDetailedFoodComments: state => {
return state.foodAppStudWue.currentFoodComments
},
},
mutations: {
logout(state) {
state.user.authenticated = false;
},
login(state) {
state.user.authenticated = true;
},
loadLocations(state) {
window.axios.get(CONFIG.API_ROOT_FOOD.concat('/menus/locations'))
.then(response => {
state.foodAppStudWue.locations = response.data;
})
.catch(e => {
console.error(e)
});
},
loadDefaultImageLocation(state) {
window.axios.get(CONFIG.API_ROOT_FOOD.concat('/meals/images/default'))
.then(response => {
state.foodAppStudWue.defaultImage = response.data.image;
})
.catch(e => {
console.error(e)
});
},
loadDetailedFood(state, {foodId}) {
window.axios.get(CONFIG.API_ROOT_FOOD
.concat('/meals/').concat(foodId))
.then(response => {
state.foodAppStudWue.currentFood = response.data;
})
.catch(e => {
});
window.axios.get(CONFIG.API_ROOT_FOOD
.concat('/meals/').concat(foodId).concat('/comments'))
.then(response => {
state.foodAppStudWue.currentFoodComments = response.data;
})
.catch(e => {
});
if (state.user.authenticated) {
state.foodAppStudWue.currentFoodUserImage = '';
let url = CONFIG.API_ROOT_ACCOUNT + '/food/images/?food_id=' + foodId;
window.axios
.get(url)
.then(response => {
if (response.data.length > 0) {
state.foodAppStudWue.currentFoodUserImage = response.data[0].image.image;
}
})
.catch(e => {
console.error(e)
})
}
},
loadUserImage(state, {foodId}) {
if (state.user.authenticated) {
let url = CONFIG.API_ROOT_ACCOUNT + '/food/images/?food_id=' + foodId;
window.axios
.get(url)
.then(response => {
if (response.data.length > 0) {
state.foodAppStudWue.currentFoodUserImage = response.data[0].image.image;
}
})
.catch(e => {
console.error(e)
})
}
},
setUserImage(state, {image}) {
state.foodAppStudWue.currentFoodUserImage = image;
}
},
actions: {
login(context) {
context.commit('login')
},
logout(context) {
context.commit('logout')
},
loadLocations(context) {
context.commit('loadLocations')
},
loadDefaultImageLocation(context) {
context.commit('loadDefaultImageLocation')
},
loadDetailedFood(context, foodId) {
context.commit('loadDetailedFood', foodId)
},
loadUserImage(context, foodId) {
context.commit('loadUserImage', foodId)
},
setUserImage(context, image) {
context.commit('setUserImage', image)
},
},
});