You are on page 1of 33

FOOD APP

Submitted by -
DEBASHIS PANDA
ROLL-NO – RKM081A07
Github Link - https://github.com/Elite-Debashis/food-app
App Hosted on Github Pages - https://elite-debashis.github.io/food-
app

Description –

Introduction
This is a simple food delivery web-app created using VueJs and Cloud Firestore. It
consists of a menu and an order page for the user. It has also the admin page to add or
manage items with admins only access. It has convinient state management system
using vuex and vuexfire. I have used dummy-text to fullfill the text area.

Features
• Admin page for the restaurant owner to access for modifying menu items and
delivering orders.
• Used Cloud Firestore for backend database which is super convenient.
• Uses Cloud Firestore Authentication to authenticate restaurant owners using
email and password.
• NoSQL databases helps to handle data easily and more efficiently.
• Proper State Management and Route Setup

Technologies Used

• VueJs
• Vuetify
• Firebase
• VueX
• VueXFire

About.vue
<template>
<v-container style="margin-top: 50px;" class="fill-height" fluid>
<v-row style="align-content: center; justify-content: center">
<v-col cols="12" sm="8" md="4">
<v-card class="elevation-12">
<v-toolbar color="#892cdc" dark flat>
<v-toolbar-title>ABOUT US</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-card-title>FOOD PLANET</v-card-title>
<v-card-subtitle>WE DELIVER BEST QUALITY AND HYGIENE FOOD</v-
card-subtitle>
<v-card-subtitle style="margin-top: 1px">ADDRESS 1</v-card-
subtitle>
<v-card-subtitle style="margin-top: -20px">ADDRESS 2</v-card-
subtitle>
<v-card-subtitle style="margin-top: -20px">EMAIL ID :
foodplanet@gmail.com</v-card-subtitle>
<v-card-subtitle style="margin-top: -20px">PHONE NO:
7894567525</v-card-subtitle>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
</template>

<script>
export default {
name: "about"
};
</script>
<style scoped>
/* div {
flex: 1 0 auto;
} */
h3 {
text-align: center;
}
</style>
Admin.vue
<template>
<div class="admin_wrapper">
<AdminContent v-if="currentUser !== null" />
<Login v-if="currentUser === null" />
</div>
</template>

<script>
import Login from "./Login.vue";
import { mapGetters } from "vuex";
const AdminContent = () => import("./AdminContent");

export default {
name: "admin",
components: {
Login,
AdminContent
},
computed: {
...mapGetters(["currentUser"])
}
};
</script>

<style scoped>
.admin_wrapper {
margin: 10px;
}
</style>

AdminContent.vue
<template>
<section style="margin-top: 70px">
<div class="current_user_wrapper">
<span>Logged in as:</span>
{{currentUser}}
<button type="button" class="btn_red" @click.prevent="signOut">Sign
out</button>
</div>
<NewPizza />
<div class="menu_wrapper">
<h3>Menu:</h3>
<table>
<thead>
<tr>
<th>Item</th>
<th>Remove from menu</th>
</tr>
</thead>
<tbody v-for="item in getMenuItems" :key="item.id">
<tr>
<td>{{ item.name }}</td>
<td>
<button type="button" class="btn_red"
@click="removeMenuItem(item.id)">&times;</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="orders_wrapper">
<h3>Current orders ({{ numberOfOrders }}):</h3>
<table>
<thead>
<tr>
<th>Item</th>
<th>Size</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<tbody v-for="(order, index) in getOrders" :key="order.id">
<tr class="order_number">
<th colspan="4">
<strong>Order Number: {{ index + 1 }}</strong>
<button type="button" class="btn_red"
@click="removeOrder(order.id)">x</button>
</th>
</tr>
<tr v-for="orderItem in order.pizzas" :key="orderItem.id">
<td>{{ orderItem.name }}</td>
<td>{{ orderItem.size }} "</td>
<td>{{ orderItem.quantity }}</td>
<td>{{ orderItem.price | currency }}</td>
</tr>
</tbody>
</table>
</div>
</section>
</template>

<script>
import NewPizza from "./NewPizza.vue";
import { store } from "../store/store.js";
import { mapGetters } from "vuex";

export default {
name: "adminContent",
components: {
NewPizza
},
computed: {
...mapGetters([
"getOrders",
"getMenuItems",
"numberOfOrders",
"currentUser"
])
},
methods: {
removeMenuItem(id) {
store.dispatch("removeMenuItem", id);
},
removeOrder(id) {
store.dispatch("removeOrder", id);
},
signOut() {
store.dispatch("signOut");
}
},
beforeRouteLeave: (to, from, next) => {
if (confirm("You will be logged out when leaving admin?") == true) {
store.dispatch("signOut");
next();
} else {
next(false);
}
}
};
</script>
<style>
.current_user_wrapper,
.orders_wrapper,
.menu_wrapper {
margin: 10px 0;
padding: 10px;
border: solid 1px #f79e38;
background: rgb(254, 254, 252);
}

table {
text-align: left;
width: 70vw;
}

.order_number th {
background: #ddd;
}

.order_number button {
margin: 0 10px;
}
</style>

Contact.vue
<template>
<v-container style="margin-top: 50px;" class="fill-height" fluid>
<v-row style="align-content: center; justify-content: center">
<v-col cols="12" sm="8" md="4">
<v-card class="elevation-12">
<v-toolbar color="#892cdc" dark flat>
<v-toolbar-title>CONTACT US</v-toolbar-title>
</v-toolbar>
<v-card-text>
<v-card-title>FOOD PLANET</v-card-title>
<h3 style="margin-left: 12px">CALL US</h3>
<v-card-subtitle style="margin-top: -18px">EMAIL ID :
foodplanet@gmail.com</v-card-subtitle>
<v-card-subtitle style="margin-top: -20px">PHONE NO:
7894567525</v-card-subtitle>
<h3 style="margin-left: 10px">DROP A MESSAGE</h3>
<v-form>
<v-text-field
style="margin-left: 10px"
label="Name"
>
</v-text-field>
<v-text-field
style="margin-left: 10px"
label="Phone No"
type="number"
>
</v-text-field>
<v-text-field
style="margin-left: 10px"
label="Email"
type="email"
>
</v-text-field>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn
block
color="#892cdc"
>SEND</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</template>

<script>
export default {
data() {
return {
dialog:false
}
},
name: "contact"
};
</script>

<style>
.contact_wrapper {
background: #f1e6da;
height: 100vh;
margin: 10px;
padding: 10px;
}
</style>

Header.vue
<template>
<header class="default-font">
<v-app-bar app color="white" height="55px" scroll-off-screen>
<v-toolbar-title>
FOOD PLANET
</v-toolbar-title>
<v-spacer></v-spacer>
<router-link :to="{ name: 'homeLink' }" tag="li"><v-btn text>HOME</v-
btn></router-link>
<router-link :to="{ name: 'menuLink' }" tag="li"><v-btn text>MENU</v-
btn></router-link>
<router-link :to="{ name: 'contactLink' }" tag="li"><v-btn
text>CONTACT</v-btn></router-link>
<router-link :to="{ name: 'aboutLink' }" tag="li"><v-btn
text>ABOUT</v-btn></router-link>
</v-app-bar>
</header>
</template>

<script>
export default {
name: "appHeader"
};
</script>

<style scoped>
h1 {
font-size: 1.3em;
}

.header_logo {
background-size: 80px 80px;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 10px;
}

.header_logo img {
max-width: 25vw;
}
ul {
padding: 10px 0;
}

li {
display: inline-block;
}

@media screen and (min-width: 900px) {


.header_logo img {
max-width: 20vw;
}
}
</style>

Home.vue
<template>
<v-container class="default-font" fluid>
<v-carousel v-model="model" hide-delimiter-background :show-arrows="false">
<v-carousel-item v-for="(slide) in slides" :key="slide.title">

<v-img :src="require('../assets/images/pizza1.jpg')">
<v-container
fill-height
fluid
pa-0 ma-0

>
<v-layout fill-height align-end>
<v-flex xs12>
<v-card color="red" class="pa-2" >
<span class="headline white--text" v-text="slide.title">
</span>
</v-card>
</v-flex>
</v-layout>
</v-container>
</v-img>

</v-carousel-item>
</v-carousel>

<div class="info_block_wrapper ">


<div v-for="(card,i) in cards" :key="i" style="margin-top: 50px">
<ordering-guide :card="card"></ordering-guide>
</div>

</div>
</v-container>
</template>

<script>
import OrderingGuide from "@/components/OrderingGuide";

export default {
data:() => ({
model: 0,
colors: [
'primary',
'secondary',
'yellow darken-2',
'red',
'orange',
],
slides: [
{
title: "TITLE 1",
url: '../assets/images/pizza1.jpg'
},
{
title: "TITLE 2",
url: '../assets/images/pizza2.jpg'
},
{
title: "TITLE 3",
url: '../assets/images/pizza3.jpg'
}
],
cards: [
{
title: 'HOW TO ORDER',
subTitle: 'FOLLOW OUR ORDER GUIDE',
text: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit.
Facilis officia rerum explicabo quia natus non temporibus debitis quod
aliquid necessitatibus.',
url: 'https://cdn.pixabay.com/photo/2016/12/26/17/28/food-
1932466__340.jpg'
},{
title: 'FAST DELIVERY',
subTitle: 'WE HAVE THE FASTEST POSSIBLE DELIVERY',
text: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit.
Facilis officia rerum explicabo quia natus non temporibus debitis quod
aliquid necessitatibus .',
url: 'https://cdn.pixabay.com/photo/2016/12/26/17/28/food-
1932466__340.jpg'
},{
title: 'QUALITY FOOD',
subTitle: 'WE PROVIDE HYGIENE AND GOOD QUALITY FOOD',
text: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit.
Facilis officia rerum explicabo quia natus non temporibus debitis quod
aliquid necessitatibus .',
url: 'https://cdn.pixabay.com/photo/2016/12/26/17/28/food-
1932466__340.jpg'
},{
title: 'TRY TODAY',
subTitle: 'GOT HUNGRY? ORDER TODAY',
text: 'Lorem ipsum dolor sit, amet consectetur adipisicing elit.
Facilis officia rerum explicabo quia natus non temporibus debitis quod
aliquid necessitatibus .',
url: 'https://cdn.pixabay.com/photo/2016/12/26/17/28/food-
1932466__340.jpg'
},

],
}),
name: "home",
methods: {
goToMenu() {
this.$router.push({ name: "menuLink" });
}
},
components: {
orderingGuide: OrderingGuide
}
};
</script>

Login.vue
<template>
<!-- <div style="margin-top: 100px" class="login_wrapper">-->
<!-- <p>Please login to continue:</p>-->

<!-- <form>-->
<!-- <div>-->
<!-- <label>Email address</label>-->
<!-- <input type="email" id="email" placeholder="Enter email" v-
model="email" />-->
<!-- </div>-->
<!-- <div>-->
<!-- <label>Password</label>-->
<!-- <input type="password" id="password" placeholder="Password" v-
model="password" />-->
<!-- </div>-->
<!-- <button type="button" class="btn_green"
@click.prevent="signIn">Sign in</button>-->
<!-- </form>-->
<!-- </div>-->
<v-container style="margin-top: 50px; " class="fill-height" fluid>
<v-row style="align-content: center; justify-content: center;">
<v-col cols="12" sm="8" md="4">
<v-card class="elevation-12">
<v-toolbar color="#892cdc" dark flat>
<v-toolbar-title>LOGIN</v-toolbar-title>
</v-toolbar>
</v-card>
<v-card-text>
<v-form>
<v-text-field
label="Email or Username"
name="Email"
v-model="email"
required
type="email"
>
</v-text-field>
</v-form>
</v-card-text>
<v-card-text>
<form>
<v-text-field
label="Password"
name="Password"
v-model="password"
required
type="password"
>
</v-text-field>
</form>
</v-card-text>
<v-card-actions>
<v-spacer>
</v-spacer>
<v-btn
@click.prevent="signIn"
block
color="#892cdc"
><span style="color: #dddddd">SIGN IN</span></v-btn>
</v-card-actions>
</v-col>
</v-row>
</v-container>
</template>

<script>
import { store } from "../store/store.js";
import Firebase from 'firebase'

export default {
name: "login",
data() {
return {
email: "",
password: ""
};
},
methods: {
signIn() {
const user = {
email: this.email,
password: this.password
};
store.dispatch("signIn", user);
},
signOut() {
Firebase.auth().signOut().then(function() {
alert('logged out');
}).catch(function(error) {
alert(error);
})
}
},
computed: {
currentUser() {
return this.$store.getters.currentUser
}
}
};
</script>

<style scoped>
</style>

Menu.vue
<template>
<div class="menu_wrapper">

<v-container class="default-font" style="margin-top: 55px">


<h2 class="text-h2">MENU</h2>
<v-card
class="mx-auto"
elevation="2"
style="margin-bottom: 10px"
v-for="item in getMenuItems"
:key="item.id"
>
<v-col>
<v-row>
<v-list-item>
<v-col>
<v-row>
<v-list-item-icon style="margin-top: -5px">
<v-card class="rounded-lg mx-auto" style="padding:
2px"><v-img src="https://wallpaperaccess.com/full/767033.jpg"

width="90"

height="90"
>
</v-img></v-card>
</v-list-item-icon>
<v-list-item-content>
<v-col>
<v-list-item-title class="text-wrap"
style="margin-top: -28px"
>
{{ item.name }}
</v-list-item-title>
<v-list-item-content>
<v-col>
<v-list-item-subtitle class="text-wrap"
style="margin-top: -25px;
margin-left: -12px; font-size: 14px"
>
{{item.description}}
</v-list-item-subtitle>
<v-row>
<div v-for="(option, index) in
item.options" :key="option[index]">
<v-btn outlined x-small style="margin-right: 5px"
@click="addToBasket(item, option)">ADD VARIANT {{option.size}}</v-btn>
</div>
</v-row>
</v-col></v-list-item-content>
</v-col></v-list-item-content>
</v-row>
</v-col>
</v-list-item>
</v-row>
</v-col>
</v-card>
<h2 class="text-h2">BASKET</h2>
<div v-if="basket.length > 0">
<v-card
class="mx-auto"
elevation="2"
style="margin-bottom: 10px;margin-top: 20px"
v-for="(item, index) in basket"
:key="index"
>
<v-col>
<v-row>
<v-list-item>
<v-col>
<v-row>
<v-list-item-content>
<v-col>
<v-list-item-title class="text-wrap" style="margin-
top: -25px">
ITEM NAME : {{item.name}}
</v-list-item-title>
<v-list-item-subtitle>ITEM VARIANT: {{item.size}}</v-
list-item-subtitle>
<div class="text-center float-left"
style="margin-left: -8px; margin-top: 10px"
>
<v-btn
class="mx-2"
height="25px"
width="25px"
outlined
fab
@click="decreaseQuantity(item)"
>
-
</v-btn>
<v-list-item-action-text>
<span>
Qty: {{item.quantity}}
</span>
</v-list-item-action-text>
<v-btn
class="mx-2"
height="25px"
width="25px"
outlined
fab
@click="increaseQuantity(item)"
>
+
</v-btn>
</div>
<v-list-item-subtitle style="float:
right">{{item.price * item.quantity | currency}}</v-list-item-subtitle>
</v-col>
</v-list-item-content>
</v-row>
</v-col>
</v-list-item>
</v-row>
</v-col>
</v-card>
<v-btn v-if="basket.length > 0" @click="addNewOrder" flat block
color="#892cdc"><span style="color: #dddddd">PLACE ORDER</span></v-btn></div>
<v-row
style="align-content: center; justify-content: center; margin-top: 10%"
fill-height
v-else
>
<v-card
justify="center"
align="center"
class="rounded-lg"
>
<v-img src=" https://freepikpsd.com/wp-
content/uploads/2019/10/empty-cart-png-Transparent-Images.png
"
height="200"
width="200"
>
<v-layout
slot="placeholder"
fill-height
align-center
justify-center
ma-0
>
<v-progress-circular
indeterminate
color="grey lighten-5"
></v-progress-circular>
</v-layout>
</v-img>
<p style="font-size: 110%"><strong>{{ basketText }}</strong></p>
</v-card>
</v-row>
</v-container>
</div>
</template>

<script>
import { mapGetters } from "vuex";
import { store } from "../store/store.js";

export default {
name: "appMenu",
data() {
return {
basket: [],
basketText: "Hungry???Go for some food now!"
};
},
computed: {
...mapGetters(["getMenuItems"]),
total() {
let totalCost = 0;
this.basket.map(item => {
totalCost += item.quantity * item.price;
});
return totalCost;
}
},
methods: {
async addToBasket(item, option) {
const pizzaExists = await this.basket.find(
pizza => pizza.name === item.name && pizza.size === option.size
);
if (pizzaExists) {
pizzaExists.quantity++;
return;
}
this.basket.push({
name: item.name,
price: option.price,
size: option.size,
quantity: 1
});
},
removeFromBasket(item) {
this.basket.splice(this.basket.indexOf(item), 1);
},
increaseQuantity(item) {
item.quantity++;
},
decreaseQuantity(item) {
item.quantity--;
if (item.quantity === 0) {
this.removeFromBasket(item);
}
},
addNewOrder() {
const order = {
createdAt: new Date(),
pizzas: { ...this.basket }
};
store.dispatch("addNewOrder", order);
this.basket = [];
this.basketText = "Thank you, your order has been placed!";
}
}
};
</script>

<style scoped>
h3 {
text-align: center;
}

.menu_wrapper {
display: flex;
flex-direction: column;
}

.menu,
.basket {
background: #f1e6da;
border-radius: 3px;
height: 100vh;
margin: 10px;
padding: 10px;
}

@media screen and (min-width: 900px) {


.menu_wrapper {
flex-direction: row;
justify-content: space-between;
}
.menu {
width: 65vw;
}
.basket {
width: 35vw;
}
}
</style>

NewPizza.vue
<template>
<form>
<h3>Add new pizza:</h3>
<div class="form_group">
<label>Name</label>
<input type="text" id="name" v-model="newPizza.name" />
</div>

<div class="form_group">
<label>Description</label>
<textarea type="text" rows="5" v-
model="newPizza.description"></textarea>
</div>

<p>
<strong>Option 1:</strong>
</p>
<div class="form_group">
<label>Size (")</label>
<input type="text" v-model="newPizza.options[0].size" />
</div>

<div class="form_group">
<label>Price</label>
<input type="text" v-model="newPizza.options[0].price" />
</div>

<p>
<strong>Option 2:</strong>
</p>
<div class="form_group">
<label>Size (")</label>
<input type="text" v-model="newPizza.options[1].size" />
</div>

<div class="form_group">
<label>Price</label>
<input type="text" v-model="newPizza.options[1].price" />
</div>

<button type="button" class="btn_green" @click="add">Add</button>


</form>
</template>

<script>
import { store } from "../store/store.js";

export default {
name: "addNewPizza",
data() {
return {
newPizza: {
name: "Eg. Margherita",
description:
"Eg. A delicious tomato based pizza topped with mozarella, ham and
pineapple.",
options: [
{
size: 9,
price: 6.95
},
{
size: 12,
price: 12.95
}
]
}
};
},
methods: {
add() {
store.dispatch("addMenuItem", this.newPizza);
}
}
};
</script>
<style>
.form_group {
margin: 10px 0;
}

label {
display: block;
margin: 0 0 10px 0;
}

input,
textarea {
width: 50%;
padding: 5px;
box-sizing: border-box;
background-color: rgb(254, 254, 252);
border: solid 1px #f79e38;
}

button {
background-color: rgb(63, 145, 63);
color: white;
padding: 5px 15px;
border-radius: 5px;
}
</style>

OrderingGuide.vue
<template>
<v-card
class="mx-auto"
width="300"
height="400"
style="margin: 10px"
elevation="5"

>
<v-img
class="white--text align-end"
:src="card.url"
>

</v-img>
<v-card-title>{{ card.title }}</v-card-title>
<v-card-subtitle style="margin-top: -20px">{{card.subTitle}}</v-card-
subtitle>
<v-card-text>{{card.text}}</v-card-text>
</v-card>
</template>

<script>
export default {
name: "orderingGuide",
props:['card']
};
</script>

Menu.js
import { firestoreAction } from "vuexfire";
import { dbMenuRef } from "../../firebase";

const state = {
menuItems: [],
};

const getters = {
getMenuItems: (state) => state.menuItems,
};

const mutations = {};

const actions = {
setMenuRef: firestoreAction((context) => {
return context.bindFirestoreRef("menuItems", dbMenuRef);
}),
addMenuItem: async ({ commit }, pizza) => {
// dont need to also commit a mutation since we dont need to update our
own store
// vuexfire handles syncing our store with firebase for us
try {
await dbMenuRef.add(pizza);
} catch (error) {
alert(`Error creating new pizza, ${error}`);
}
},
removeMenuItem: async ({ commit }, id) => {
try {
const item = await dbMenuRef.doc(id);
item.delete();
} catch (error) {
alert(`Error removing menu item, ${error}`);
}
},
};

export default {
state,
mutations,
getters,
actions,
};

Orders.js
import { firestoreAction } from "vuexfire";
import { dbOrdersRef } from "../../firebase";

const state = {
orders: [],
};

// REMOVE AFTER ADDING ACTION BELOW


const mutations = {
// addOrder: (state, order) => state.orders.push(order),
};

const getters = {
getOrders: (state) => state.orders,
numberOfOrders: (state) => state.orders.length,
};

const actions = {
setOrdersRef: firestoreAction((context) => {
return context.bindFirestoreRef("orders",
dbOrdersRef.orderBy("createdAt"));
}),
addNewOrder: async (context, order) => {
try {
await dbOrdersRef.add(order);
} catch (error) {
alert(
`Sorry, there was a problem placing your order, please try again...`
);
}
},
removeOrder: async ({ commit }, id) => {
try {
const order = await dbOrdersRef.doc(id);
order.delete();
} catch (error) {
alert(`Sorry, there was a problem removing the order, ${error}`);
}
},
};

export default {
state,
mutations,
getters,
actions,
};

Users.js
import { firebaseAuth } from "../../firebase";

const state = {
currentUser: null,
};

const mutations = {
userStatus: (state, user) => {
user === null
? (state.currentUser = null)
: (state.currentUser = user.email);
},
};
const getters = {
currentUser: (state) => state.currentUser,
};

const actions = {
signIn: async ({ commit }, user) => {
try {
const userData = await firebaseAuth.signInWithEmailAndPassword(
user.email,
user.password
);
commit("userStatus", userData.user);
} catch (error) {
const errorCode = error.code;
const errorMessage = error.message;
if (errorCode === "auth/wrong-password") {
alert("Wrong password.");
} else {
alert(errorMessage);
}
}
},
signOut: async ({ commit }) => {
try {
await firebaseAuth.signOut();
// set userStatus mutation to be null
commit("userStatus", null);
alert("logged out.");
} catch (error) {
alert(`error signing out, ${error}`);
}
},
};

export default {
state,
mutations,
getters,
actions,
};

Actions.js
import {firebaseAuth} from "@/firebase";

export const signIn = async ({ commit }, user) => {


try {
await firebaseAuth.signInWithEmailAndPassword(
this.email,
this.password
)
} catch (error){
const errorCode = error.code
const errorMessage = error.message
if (errorCode === 'auth/wrong-password'){
alert("Wrong password")
} else {
alert(errorMessage)
}
}
}

Getters.js
export const getmenuItems = state => state.menuItems
export const numberOfOrders = state => state.orders.length
export const currentUser = state => state.currentUser

Store.js
import Vue from 'vue'
import Vuex from 'vuex'
import menu from './modules/menu'
import orders from './modules/orders'
import users from './modules/users'
import { vuexfireMutations } from 'vuexfire'

Vue.use(Vuex)

export const store = new Vuex.Store({


mutations: vuexfireMutations,
modules: {
menu,
orders,
users,
},

})

App.vue
<template>
<div id="app" class="default-font">
<app-header></app-header>
<router-view></router-view>

</div>
</template>

<script>
import Header from "./components/Header.vue";
import { dbMenuRef, dbOrdersRef } from "./firebase";
export default {
name: "app",
components: {
// should not use reserved header element name
appHeader: Header,

},
created() {
this.$store.dispatch("setMenuRef", dbMenuRef);
this.$store.dispatch("setOrdersRef", dbOrdersRef);
},
data() {
return {

}
}
};
</script>

<style>
body {
font-family: "Crimson Text", serif;
margin: 0;
font-size: 1.5em;
}
a {
text-decoration: none;
color: inherit;
}
ul {
display: flex;
justify-content: center;
margin: 0;
padding: 10px 0;
background: #f1e6da;
}
li {
list-style: none;
}
span {
margin: 0 5px;
}
input,
textarea {
font-size: 1.1rem;
}
button {
border: none;
font-size: 1.1rem;
}
.btn_green {
background: rgb(101, 168, 101);
}
.btn_red {
background: rgb(241, 70, 70);
}
.info_block_wrapper {
display: flex;
flex-direction: column;
}
.info_block {
background: #f1e6da;
margin: 10px 0;
padding: 10px;
}
.info_block > h3 {
text-align: center;
}
.info_block_content {
display: flex;
align-items: center;
}
.info_block img {
width: 30%;
}
@media screen and (min-width: 900px) {
.info_block {
width: 100%;
}
.info_block_wrapper {
flex-direction: row;
justify-content: space-around;
/* align-items: center; */
align-content: space-between;
}
.info_block:nth-child(2) {
margin: 10px;
}
.info_block:nth-child(2) img {
/* middle info block has image second in source order on small screens
this will move the image back on top of text for large screens */
order: -1;
}
.info_block_content {
flex-direction: column;
align-items: center;
}
.info_block img {
max-width: 100%;
}
}
@font-face {
font-family: Itim;
src: url("https://fonts.googleapis.com/css2?family=Itim&display=swap");
}
.default-font {
font-family: Itim;
}

</style>

Firebase.js
import firebase from "firebase/app";
import 'firebase/firestore'
import 'firebase/auth'

const config = {
apiKey: "AIzaSyBuKyBRkEoAQ0usKeHFVWEhu8lMmgUFmN0",
authDomain: "fodd-app.firebaseapp.com",
databaseURL: "https://fodd-app.firebaseio.com",
projectId: "fodd-app",
storageBucket: "fodd-app.appspot.com",
messagingSenderId: "755715556376",
appId: "1:755715556376:web:7ce469a93a7e2828ee2f6d",
measurementId: "G-9V5T0BG9CR"
}

firebase.initializeApp(config)
const db = firebase.firestore()
export const firebaseAuth = firebase.auth()
export const dbMenuRef = db.collection('menu')
export const dbOrdersRef = db.collection('orders')

Main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import { store } from './store/store.js'
import Accounting from 'accounting-js'
import { routes } from './routes';
import vuetify from './plugins/vuetify';
import './style.css'

Vue.config.productionTip = false
Vue.use(VueRouter)

export const router = new VueRouter({


routes,
mode: 'history',
// eslint-disable-next-line no-unused-vars
scrollBehavior(to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}
})

Vue.filter('currency', val => {


return Accounting.formatMoney(val)
})

new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount('#app')

Routes.js
import Home from './components/Home.vue'
//import Menu from './components/Menu.vue'
const Menu = () => import('./components/Menu.vue')

//import About from './components/About.vue'


const About = () => import('./components/About.vue')
import Contact from './components/Contact.vue'
import OrderingGuide from './components/OrderingGuide.vue'

//import Admin from './components/Admin.vue'


const Admin = () => import('./components/Admin.vue')

export const routes = [


{
path: '/',
name: "homeLink",
components:
{
default: Home,
"ordering-guide": OrderingGuide,
}
},
{ path: '/menu', name: 'menuLink', component: Menu },
{ path: '/contact', name: 'contactLink', component: Contact },
{
path: '/admin',
name: 'adminLink',
component: Admin,
beforeEnter: (to, from, next) => {
alert('This area is for authorised users only, please login to
continue');
next();
}
},
{
path: '/about', name: 'aboutLink', component: About, children: [
{ path: 'history', name: 'historyLink', component: History },
{ path: 'ordering-guide', name: 'orderingGuideLink', component:
OrderingGuide }
]
},
// catch all needed when using history mode, redirect to home page
{ path: '*', redirect: '/' }
];
SCREENSHOTS

You might also like