<file_sep>const express = require('express');
const cors = require('cors')
const bodyParser = require('body-parser');
const path = require('path');
const http = require('http');
const mongoose = require('mongoose');

const app = express();
app.use(cors())

var config = require('./config');
mongoose.connect(config.database);

require('./server/models/db.js')

// API file for interacting with MongoDB
const api = require('./server/routes/api');

// On Connection
mongoose.connection.on('connected', () => {
  console.log('Connected to database '+config.database);
});

// On Error
mongoose.connection.on('error', (err) => {
  console.log('Database error: '+err);
});

// const users = require('./routes/users');


// Parsers
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false}));
//
// Angular DIST output folder
app.use(express.static(path.join(__dirname, 'dist')));

// API location
app.use('/api', api);

// Send all other requests to the Angular app
app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, 'dist/index.html'));
});

//Set Port
const port = process.env.PORT || '3000';
app.set('port', port);

const server = http.createServer(app);

server.listen(port, () => console.log(`Running on localhost:${port}`));
<file_sep>import { Component, ViewChild, OnInit } from '@angular/core';
import { Location } from '@angular/common';
import { Router } from '@angular/router'

import { OrderService } from '../services/order.service'
import { OrderFormComponent } from '../order-form/order-form.component'

@Component({
  selector: 'app-place-order',
  templateUrl: './place-order.component.html',
  styleUrls: ['./place-order.component.css']
})
export class PlaceOrderComponent implements OnInit {

  @ViewChild(OrderFormComponent) private orderFormComponent: OrderFormComponent;

  items: any[] = []

  constructor(
    private orderService: OrderService,
    private location: Location,
    private router: Router
  ) {
    this.items = this.orderService.items
  }

  ngOnInit() {
  }

  decQty(item: any) {
    this.items[this.items.indexOf(item)].qty--
    if (this.items[this.items.indexOf(item)].qty == 0) {
      this.items.splice(this.items.indexOf(item), 1)
    }
    // (+item.qty)--
  }

  incQty(item: any) {
    this.items[this.items.indexOf(item)].qty++
    //(+item.qty)++
  }

  getItemCount() {
    if (this.items) { // for testing
      return this.items.length
    } else {
      return 2
    }
  }

  getTotalPrice() {
    if (this.items) { // for testing
      var total = 0;
      for (let item of this.items) {
        total = total + item.price * item.qty
      }
      return total
    } else {
      return 11.25
    }
  }

  goBack() {
    this.location.back()
  }

  placeOrder() {
    var valid = this.orderFormComponent.validateInfo()
    console.log(valid)
    if (valid) {
      var order: any = {}
      console.log(valid)
      order.deliveryOrPickup = valid.deliveryOrPickup
      console.log(order.deliveryOrPickup)
      if (valid.deliveryOrPickup == 'delivery') {
        order.address = {}
        order.firstName = valid.firstName
        order.lastName = valid.lastName
        order.address.address = valid.address
        order.address.city = valid.city
        order.address.zip = valid.zip
      } else {
        order.firstName = valid.firstName
        order.lastName = valid.lastName
      }
      var items = []
      var total = 0
      for (let item of this.items) {
        var itemWithoutQty = Object.assign({}, item);
        var quantity = itemWithoutQty.qty
        delete itemWithoutQty.qty
        for (var i = 0; i < quantity; i++) {
          items.push(itemWithoutQty)
          total += itemWithoutQty.price
        }
      }
      order.items = items
      order.total = total
      console.log(order)
      this.orderService.placeOrder(order).subscribe((data) => {
        if (data.success) {
          this.orderService.items = []
          this.router.navigate([''])
        }
      })
      console.log('submitting form...')
    } else {
      console.log('invalid form')
    }
  }

}
<file_sep>const mongoose = require('mongoose')
const Category = mongoose.model('Category')
const Location = mongoose.model('Location')
const Item = mongoose.model('Item')

// make sure name and size are unique
module.exports.addItem = function(req, res) {
  var name = req.body.name
  var price = parseFloat(req.body.price)
  var calories = parseInt(req.body.calories)
  var size = req.body.size
  var categoryName = req.body.category
  var locationName = req.body.location

  Category.findOne({"name": categoryName}).exec((err, category) => {
    if (err) {
      res.status(500).json(err)
    } else {
      item = {
        category: category,
        name: name,
        price: price,
        calories: calories,
        size: size
        // location: locationName
      }
      if (locationName == 'All') {
        Location.update({}, {
          $push : { 'items' : item }
        }, {
          multi : true
        }, (err, locations) => {
          if (err) {
            res.status(500).json(err)
          } else {
            res.status(201).json({success: true, item: item})
          }
        });
      } else {
        Location.findOneAndUpdate(
          {'city': locationName},
          {$push : {'items': item}},
          {'new': true}, (err, location) => {
            if (err) {
              res.status(500).json(err)
            } else {
              console.log({success: true, item: item})
              res.status(201).json({success: true, item: item})
            }
          }
        )
      }
    }
  })
}

module.exports.getItems = function(req, res) {
  Location.find().exec((err, locations) => {

    if (err) {
      res.status(500).json(err)
    } else {
      var items = []
      for (var location of locations) {
        for (var item of location.items) {
          if (items.findIndex(i => (i.name == item.name && i.size == item.size)) != -1) {
            console.log('item = ' + item.name)
            items[items.findIndex(i => (i.name == item.name && i.size == item.size))].location = 'all'
          } else {
            item.location = location.city
            items.push(item)
          }
        }
      }
      console.log(items)
      // console.log(items)
      // var reducedItems = items.reduce(function (allItems, item) {
      //   console.log('item=', item)
      //   console.log(allItems.findIndex(i => i.name == item.name)
      //   if (allItems.findIndex(i => i.name == item.name) != -1) {
      //     allItems[allItems.findIndex(i => i.name == item.name)].location = 'all'
      //   } else {
      //     allItems.push(item)
      //   }
      //   return allItems;
      // }, []);
      // console.log(reducedItems)

      res.status(200).json({success: true})
      // var dups = [];
      // var arr = items.filter(function(el) {
      //   // If it is not a duplicate, return true
      //   if (dups.indexOf(el.ID) == -1) {
      //     dups.push(el.ID);
      //     return true;
      //   }
      //
      //   return false;
      //
      // });

      // res.status(200).json(locations)
    }
  })
}

module.exports.deleteItem = function(req, res) {
  const deleteLocation = req.body.location;
  const itemId = req.body._id

  if ("all" || deleteLocation == "All") {
    Location.find().exec((err, locations) => {
      if (err) {
        res.status(500).json(err)
      } else {
        for (var location of locations) {
          location.items.splice(location.items.findIndex(i => (i._id == itemId)), 1 )
          location.save()
        }
        res.status(201).json({success: true })
        // locations.save(function(err, deleted) {
        //   if (err) {
        //     res.status(500).json(err)
        //   } else {
        //     res.status(201).json({success: true })
        //   }
        // })
      }
    })
  } else {
    Location.find({"city": deleteLocation}).exec((err, location) => {
      if (err) {
        res.status(500).json(err)
      } else {
        console.log("location = " + location)
        console.log('items')
        console.log(location[0].items)
        location[0].items.splice(location[0].items.findIndex(i => (i._id == itemId)), 1 )
        location[0].save(function(err, deleted) {
          if (err) {
            res.status(500).json(err)
          } else {
            res.status(201).json({success: true })
          }
        })
      }
    })
  }
}

module.exports.editItem = function(req, res) {
  const editLocation = req.body.location;
  const itemId = req.body._id;

  if (editLocation == "All") {
    Location.find().exec((err, locations) => {
      if (err) {
        res.status(500).json(err)
      } else {
        delete req.body.location
        for (var location of locations) {
          location.items.splice(location.items.findIndex(i => (i._id == itemId)), 1, req.body)
          location.save()
        }
        res.status(201).json({success: true })
      }
    })
  } else {
    Location.find({"city": editLocation}).exec((err, location) => {
      if (err) {
        res.status(500).json(err)
      } else {
        delete req.body.location
        location[0].items.splice(location[0].items.findIndex(i => (i._id == itemId)), 1, req.body)
        location[0].save(function(err, deleted) {
          if (err) {
            res.status(500).json(err)
          } else {
            res.status(201).json({success: true })
          }
        })
      }
    })
  }
}
<file_sep>import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http'
import 'rxjs/add/operator/map'

@Injectable()
export class AdminService {

  constructor(private http: Http) { }

  addCategory(name: String) {
    // let headers = new Headers();
    // this.loadToken()
    // headers.append('Authorization', 'JWT ' + this.authToken)
    // headers.append('Content-Type', 'application/json')
    return this.http.post('http://localhost:3000/api/category', { name: name }/*, { headers: headers }*/)
      .map(res => res.json())
  }

  deleteCategory(categoryName: String) {
    // let headers = new Headers();
    // this.loadToken()
    // headers.append('Authorization', 'JWT ' + this.authToken)
    // headers.append('Content-Type', 'application/json')
    return this.http.delete('http://localhost:3000/api/category', new RequestOptions({ body: { "categoryName": categoryName } }))
      .map(res => res.json())
  }

  editCategory(oldAndNewCategories: Object) {
    // let headers = new Headers();
    // this.loadToken()
    // headers.append('Authorization', 'JWT ' + this.authToken)
    // headers.append('Content-Type', 'application/json')
    return this.http.put('http://localhost:3000/api/category', oldAndNewCategories)
      .map(res => res.json())
  }

  addItem(item: Object) {
    // let headers = new Headers();
    // this.loadToken()
    // headers.append('Authorization', 'JWT ' + this.authToken)
    // headers.append('Content-Type', 'application/json')
    return this.http.post('http://localhost:3000/api/items', item/*, { headers: headers }*/)
      .map(res => res.json())
  }

  deleteItem(item: Object) {
    // let headers = new Headers();
    // this.loadToken()
    // headers.append('Authorization', 'JWT ' + this.authToken)
    // headers.append('Content-Type', 'application/json')
    return this.http.delete('http://localhost:3000/api/items', new RequestOptions({ body: item })).map(res => res.json())
  }

  editItem(item: Object) {
    // let headers = new Headers();
    // this.loadToken()
    // headers.append('Authorization', 'JWT ' + this.authToken)
    // headers.append('Content-Type', 'application/json')
    return this.http.put('http://localhost:3000/api/items', item).map(res => res.json())
  }
}
<file_sep>var mongoose = require('mongoose')
var Location = mongoose.model('Location')
var config = require('../../config')

// name: type: String,required: true
// address: {type: String,required: true},
// coordinates: {type: [Number],required: true},
// hours: {type: [OpeningTimeSchema],required: true},
// wifi: {type: Boolean},
// menu: {type: MenuSchema}

// days: {type: String,required: true},
// opening: String,
// closing: String,
// closed: {type: Boolean,required: true}

module.exports.addLocation = function(req, res) {
  const street = req.body.street
  const city = req.body.city
  const state = req.body.state
  const zip = req.body.zip
  const slug = city.replace(/\./, "").replace(/\s/g, '-').toLowerCase()

  const lat = req.body.lat
  const lng = req.body.lng
  const coordinates = [parseFloat(lat), parseFloat(lng)]
  const hours =
  [
    {
      days: 'Monday-Friday',
      opening: "10:00",
      closing: "10:00",
      closed: false
    },
    {
      days: 'Saturday',
      opening: "10:00",
      closing: "12:00",
      closed: false
    },
    {
      days: 'Sunday',
      closed: true
    }
  ]
  const wifi = (req.body.wifi == 'true')
  Location.create({
    street: street,
    city: city,
    state: state,
    zip: zip,
    slug: slug,
    coordinates: coordinates,
    hours: hours,
    wifi: wifi
  }, (err, location) => {
    if (err) {
      res.status(400).json(err)
    } else {
      res.status(201).json(location)
    }
  })
}

module.exports.getLocation = function(req, res) {
  var city = req.params.location
  // var city = req.params.location.replace(/-/, " ").toLowerCase()
  // city = city.indexOf('st') != -1 ? city.replace(/st/, 'st.') : city
  // console.log(city)
  // res.status(200).json({success: true})
  Location.find({"slug": city}).exec((err, location) => {
    var categoryNameHolder = []
    var categoryArr = []
    var pizzaSizes = []
    var specialPizzas = []
    var toppings = []

    location[0].items.forEach(function(item) {
      if (item.name == "Custom Pizza") {
        pizzaSizes.push(item)
      }
      else if (item.category.name == "Pizza") {
        specialPizzas.push(item)
      }
      else if (item.category.name == "Topping") {
        toppings.push(item)
      } else {
        categoryNameHolder[item.category.name] = categoryNameHolder[item.category.name] || {}
        var obj = categoryNameHolder[item.category.name]
        if (Object.keys(obj).length == 0)
          categoryArr.push(obj)
        obj.categoryName = item.category.name
        obj.items = obj.items || []
        obj.items.push(item)
      }
    })

    var pizza = {
      categoryName: "Pizza",
      pizzaSizes: pizzaSizes,
      specialPizzas: specialPizzas,
      toppings: toppings
    }
    // var locationAndSortedItems = {
    //   location: location[0],
    //   sortedItems: groupedItems
    // }

    var locationSortedItemsAndPizza = {
      location: location[0],
      sortedItems: categoryArr,
      pizza: pizza
    }

    if (err) {
      res.status(400).json(err)
    } else {
      res.status(200).json(locationSortedItemsAndPizza)
    }
  })
}
<file_sep>const mongoose = require('mongoose');

const AddressSchema = mongoose.Schema({
  address: {
    type: String
  },
  city: {
    type: String
  },
  zip: {
    type: Number
  }
});

module.exports = mongoose.model('Address', AddressSchema);
<file_sep>const mongoose = require('mongoose');

const OpeningTimeSchema = mongoose.Schema({
  days: {
    type: String,
    required: true
  },
  opening: String,
  closing: String,
  closed: {
    type: Boolean,
    required: true
  }
});

module.exports = mongoose.model('OpeningTime', OpeningTimeSchema);
<file_sep>import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'
import { Subscription }   from 'rxjs/Subscription';

import { OrderService } from '../services/order.service'

@Component({
  selector: 'app-order-summary',
  templateUrl: './order-summary.component.html',
  styleUrls: ['./order-summary.component.css']
})
export class OrderSummaryComponent implements OnInit {

  items: any[] = []
  subscription: Subscription;
  orderCount: number = 0

  constructor(
    private orderService: OrderService,
    private router: Router
  ) {
    this.subscription = orderService.itemAdded$.subscribe(
      item => {
        this.orderCount++
        var ordered = false;
        for (let orderItem of this.items) {
          if (orderItem._id == item._id) {
            if (orderItem.name == 'Custom Pizza') {
              if (orderItem.toppings.length == item.toppings.length) {
                var toppingsDontMatch = false
                orderItem.toppings.sort(function(a, b) {
                  var nameA = a.name.toUpperCase(); // ignore upper and lowercase
                  var nameB = b.name.toUpperCase(); // ignore upper and lowercase
                  if (nameA < nameB) {
                    return -1;
                  }
                  if (nameA > nameB) {
                    return 1;
                  }
                  return 0;
                })
                item.toppings.sort(function(a, b) {
                  var nameA = a.name.toUpperCase(); // ignore upper and lowercase
                  var nameB = b.name.toUpperCase(); // ignore upper and lowercase
                  if (nameA < nameB) {
                    return -1;
                  }
                  if (nameA > nameB) {
                    return 1;
                  }
                  return 0;
                })
                for (var i = 0; i < orderItem.toppings.length; i++) {
                  if (item.toppings[i]._id == orderItem.toppings[i]._id) {
                    continue
                  } else {
                    console.log('print unequal')
                    toppingsDontMatch = true
                  }
                }
                if (!toppingsDontMatch) {
                  orderItem.qty++
                  ordered = true
                }
              }
            } else {
              orderItem.qty++
              ordered = true
            }
          }
        }
        if (!ordered) {
          item.qty = 1;
          this.items.push(item)
        }
      }
    )
    this.items = this.orderService.items
  }

  ngOnInit() {
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }

  removeItem(item: any) {
    this.items = this.items.filter((orderItem) => {
      if (item.name == "Custom Pizza" && orderItem._id == item._id) {
        if (item.toppings.length == orderItem.toppings.length) {
          for (var i = 0; i < item.toppings.length; i++) {
            if (item.toppings[i] != orderItem.toppings[i])
              return orderItem;
          }
        } else {
          return orderItem
        }
      } else {
        if (orderItem._id != item._id)
          return orderItem
        else
          this.orderCount = this.orderCount - orderItem.qty
      }
    })
    this.orderService.removeItem(this.orderCount)
  }

  getTotal() {
    return this.items
      .reduce((sum, value) => sum + (value.price * value.qty), 0);
  }

  placeOrder() {
    this.orderService.placeOrder(this.items)
    this.router.navigate(['place-order'])
  }

}
<file_sep>import { Component, OnInit, EventEmitter, Output } from '@angular/core';

@Component({
  selector: 'app-navbar',
  templateUrl: './navbar.component.html',
  styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
  @Output() showCat = new EventEmitter<boolean>()
  @Output() showIt = new EventEmitter<boolean>()
  @Output() logout = new EventEmitter<boolean>();

  constructor() { }
  showCategory = false
  showItem = false
  ngOnInit() {
  }

  showAddCategory() {
    this.showCategory = !this.showCategory
    this.showCat.emit(this.showCategory)
  }

  showAddItem() {
    this.showItem = !this.showItem
    this.showIt.emit(this.showItem)
  }

  logoutAdmin() {
    this.logout.emit(true)
  }
}
<file_sep>var mongoose = require('mongoose')
var User = mongoose.model('User')
var bcrypt = require('bcrypt-nodejs')
var jwt = require('jsonwebtoken')
var config = require('../../config')

module.exports.authenticate = function(req, res, next) {
  var headerExists = req.headers.authorization
  if (headerExists) {
    var token = req.headers.authorization.split(' ')[1]
    jwt.verify(token, config.secret, function(error, decoded) {
      if (error) {
        console.log(error)
        res.status(401).json('Unauthorized')
      } else {
        req.user = decoded.username
        next()
      }
    })
  } else {
    res.status(403).json('No token provided')
  }
}

module.exports.login = function(req, res) {
  var username = req.body.username
  var password = <PASSWORD>
  User.findOne({
    username: username
  }).exec(function(err, user) {
    if (err) {
      console.log(err)
      res.status(400).json(err)
    } else {
      if (bcrypt.compareSync(password, user.password)) {
        var token = jwt.sign({ username: user.username }, config.secret)
        res.status(200).json({success: true, token: token, user: user.username})
      } else {
        res.status(401).json('Unauthorized')
      }
    }
  })
}

// module.exports.addUser = function(req, res) {
//   var username = req.body.username;
//   var password = <PASSWORD>;
//
//   User.create({
//     username: username,
//     password: <PASSWORD>.hashSync(password, bcrypt.genSaltSync(10))
//   }, function(err, user) {
//     if (err) {
//       console.log(err)
//       res.status(400).json(err)
//     } else {
//       console.log('user created', user)
//       // var token = jwt.sign({ username: user.username }, '<PASSWORD>', { expiresIn: 3600 })
//       res.status(201).json({success: true})
//     }
//   })
// }
<file_sep>import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-order-form',
  templateUrl: './order-form.component.html',
  styleUrls: ['./order-form.component.css']
})
export class OrderFormComponent implements OnInit {

  delivery: boolean = true

  submitted: boolean = false

  orderForm: FormGroup

  // orderInfo = {
  //   deliveryOrPickup: this.delivery ? "delivery" : "Pickup",
  //   firstName: "",
  //   lastName: "",
  //   address: "",
  //   city: "",
  //   zip: ""
  // }

  // orderForm = new FormGroup({
  //   firstName: new FormControl(),
  //   lastName: new FormControl(),
  //   address: new FormControl(),
  //   city: new FormControl(),
  //   zip: new FormControl(),
  // });

  constructor(private fb: FormBuilder) {
    this.createForm()
  }

  createForm() {
    this.orderForm = this.fb.group({
      deliveryOrPickup: "delivery",
      firstName: ["", Validators.compose([
        Validators.minLength(2),
        Validators.required
      ])],
      lastName: ["", Validators.compose([
        Validators.minLength(2),
        Validators.required
      ])],
      address: ["", Validators.compose([
        Validators.minLength(4),
        Validators.required
      ])],
      city: ["", Validators.compose([
        Validators.minLength(4),
        Validators.required
      ])],
      zip: ["", Validators.compose([
        Validators.pattern(/^\d{5}$/),
        Validators.required
      ])]
    });
  }

  ngOnInit() {
    this.orderForm.get('deliveryOrPickup').valueChanges.subscribe((deliveryOrPickup: string) => {
      // console.log(deliveryOrPickup)
      if (deliveryOrPickup == "delivery") {
        this.orderForm.get('address').setValidators([Validators.required, Validators.minLength(4)]);
        this.orderForm.get('city').setValidators([Validators.required, Validators.minLength(4)]);
        this.orderForm.get('zip').setValidators([Validators.required, Validators.pattern(/^\d{5}$/)]);
      }
      if (deliveryOrPickup == "pickup") {
        // this.orderForm.get('address').setValue("")
        this.orderForm.get('address').setValidators(null);
        this.orderForm.get('address').updateValueAndValidity();
        // this.orderForm.get('city').setValue("")
        this.orderForm.get('city').setValidators(null);
        this.orderForm.get('city').updateValueAndValidity();
        // this.orderForm.get('zip').setValue("")
        this.orderForm.get('zip').setValidators(null);
        this.orderForm.get('zip').updateValueAndValidity();
      }
    })
  }

  get firstName() { return this.orderForm.get('firstName'); }
  get lastName() { return this.orderForm.get('lastName'); }
  get address() { return this.orderForm.get('address'); }
  get city() { return this.orderForm.get('city'); }
  get zip() { return this.orderForm.get('zip'); }

  validateInfo() {
    if (this.orderForm.status == 'VALID') {
      // Submit
      return this.orderForm.value
    } else {
      this.submitted = true
      return false
    }
  }

  deliverySelected() {
    this.delivery = true;
    this.orderForm.get('deliveryOrPickup').setValue("delivery")
  }

  pickUpSelected() {
    this.delivery = false;
    this.orderForm.get('deliveryOrPickup').setValue("pickup")
  }


}
<file_sep>const mongoose = require('mongoose');

const Item = require('./item.js')
const ItemSchema = mongoose.model('Item').schema
const AddressSchema = mongoose.model('Address').schema

const OrderSchema = mongoose.Schema({
  items: {
    type: [ItemSchema],
    required: true
  },
  timePlaced: {
    type: Date,
    "default": Date.now
  },
  paid: {
    type: Boolean,
    "default": false
  },
  deliveryOrPickup: {
    type: String,
    required: true
  },
  firstName: {
    type: String,
    required: true
  },
  lastName: {
    type: String,
    required: true
  },
  total: {
    type: Number,
    required: true
  },
  address: AddressSchema
});

module.exports = mongoose.model('Order', OrderSchema);
<file_sep>import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router';
import { AgmCoreModule } from '@agm/core';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { LocationsComponent } from './locations/locations.component';
import { LoginComponent } from './login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component'
import { NavbarComponent } from './navbar/navbar.component';
import { MenuComponent } from './menu/menu.component'

import { AdminService } from './services/admin.service';
import { AuthService } from './services/auth.service';
import { MenuService } from './services/menu.service';
import { OrderService } from './services/order.service';
import { AuthGuard } from './guards/auth.guard';
import { DialogComponent } from './dialog/dialog.component';
import { UserMenuComponent } from './user-menu/user-menu.component';
import { OrderSummaryComponent } from './order-summary/order-summary.component';
import { PlaceOrderComponent } from './place-order/place-order.component';
import { OrderFormComponent } from './order-form/order-form.component';

const routes: Routes = [
  { path: '', component: HomeComponent, pathMatch: 'full' },
  { path: 'about', component: AboutComponent },
  { path: 'locations', component: LocationsComponent },
  { path: 'order-form', component: OrderFormComponent },
  { path: 'menu/:location', component: UserMenuComponent },
  { path: 'place-order', component: PlaceOrderComponent },
  { path: 'admin/login', component: LoginComponent },
  { path: 'admin/dashboard', component: DashboardComponent/*, canActivate: [AuthGuard]*/ }
];

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    AboutComponent,
    LocationsComponent,
    LoginComponent,
    DashboardComponent,
    NavbarComponent,
    MenuComponent,
    DialogComponent,
    UserMenuComponent,
    OrderSummaryComponent,
    PlaceOrderComponent,
    OrderFormComponent,
  ],
  imports: [
    BrowserModule,
    HttpModule,
    FormsModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
    RouterModule.forRoot(routes),
    AgmCoreModule.forRoot({
      apiKey: '<KEY>'
    })
  ],
  providers: [
    AdminService,
    AuthService,
    MenuService,
    OrderService,
    AuthGuard
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
<file_sep>import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http'
import 'rxjs/add/operator/map'
import { Subject }    from 'rxjs/Subject';

@Injectable()
export class OrderService {

  items: Object[] = []

  constructor(private http: Http) { }

  // Observable string sources
  private itemAddedSource = new Subject<any>();
  private itemRemovedSource = new Subject<number>();

  // Observable string streams
  itemAdded$ = this.itemAddedSource.asObservable();
  itemRemoved$ = this.itemRemovedSource.asObservable();
  // missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // Service message commands
  addItem(item: any) {
    this.itemAddedSource.next(item);
  }

  removeItem(newQty: number) {
    this.itemRemovedSource.next(newQty)
  }

  placeOrder(order: any) {
    return this.http.post('http://localhost:3000/api/order', order)
      .map(res => res.json())
  }

  retrieveOrder() {
    return this.items
  }

  //

  // confirmMission(astronaut: string) {
  //   this.missionConfirmedSource.next(astronaut);
  // }

}
<file_sep>import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { trigger, state, style, transition, animate } from '@angular/animations';
import 'rxjs/add/operator/switchMap';
import { Subscription }   from 'rxjs/Subscription';

import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';

import { MenuService } from '../services/menu.service'
import { OrderService } from '../services/order.service';

@Component({
  selector: 'app-user-menu',
  templateUrl: './user-menu.component.html',
  styleUrls: ['./user-menu.component.css'],
  animations: [
    trigger('slideInOut', [
      state('in', style({
        transform: 'translate3d(0, 0, 0)'
      })),
      state('out', style({
        transform: 'translate3d(100%, 0, 0)'
      })),
      transition('in => out', animate('400ms ease-in-out')),
      transition('out => in', animate('400ms ease-in-out'))
    ]),
  ]
  // providers: [OrderService]
})
export class UserMenuComponent implements OnInit {

  location: Object
  sortedItems: Object[]
  pizza: any

  menuState: String = 'out';
  orderQty: number = 0;

  subscription: Subscription;

  showDialog: boolean = false;

  customPizzaForm: FormGroup
  toppings: any

  constructor(
    private route: ActivatedRoute,
    private menuService: MenuService,
    private orderService: OrderService,
    private fb: FormBuilder
  ) {
    this.route.params
      .switchMap((params: Params) => this.menuService.getLocationInfo(params['location']))
      .subscribe(data => {
        console.log(data)
        this.location = data.location
        this.sortedItems = data.sortedItems
        this.pizza = data.pizza
        this.toppings = data.pizza.toppings
        console.log(this.toppings)
        // console.log(this.location)
        // console.log('sortedItems')
        // console.log(this.sortedItems)
        // console.log(this.pizza.items[this.pizza.items.findIndex(i => i.size == "Medium")])
        this.customPizzaForm
          .patchValue({
            pizzaSize: this.pizza.pizzaSizes[this.pizza.pizzaSizes.findIndex(i => i.size == "Medium")]
          })
      });
    this.subscription = orderService.itemRemoved$.subscribe(
      newQty => {
        this.orderQty = newQty
      });
    this.orderQty = this.orderService.items.length
  }

  addTopping() {
    const control = <FormArray>this.customPizzaForm.controls['toppings'];
    const addrCtrl = this.initTopping();


    control.push(addrCtrl);
  }

  removeTopping(i: number) {
    const control = <FormArray>this.customPizzaForm.controls['toppings'];
    control.removeAt(i);
  }

  initTopping() {
    return this.fb.group({
      topping: {}
    });
  }

  ngOnInit() {

    // call from ngOnInit
    this.customPizzaForm = this.fb.group({
      pizzaSize: [{}, Validators.required],
      // pizzaSize: this.fb.group(new Item())
      toppings: this.fb.array([])
    });

    this.addTopping()
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }

  toggleOrder() {
    this.menuState = this.menuState === 'out' ? 'in' : 'out';
  }

  addItemToOrder(product: Object) {
    this.orderQty++;
    this.orderService.addItem(product)
  }

  addPizzaToOrder() {
    var pizza = Object.assign({}, this.customPizzaForm.value.pizzaSize);
    var toppings = []
    var size = pizza.size
    var toppingsArr = this.customPizzaForm.value.toppings.slice(0)
    for (let topping of toppingsArr) {
      if (Object.keys(topping.topping).length !== 0) {
        var newTopping = Object.assign({}, topping.topping)
        switch (size) {
          case ("Small"):
            newTopping.calories = parseInt((newTopping.calories * 0.8).toFixed(0))
            newTopping.price = parseFloat((newTopping.price * 0.8).toFixed(1))
            pizza.calories += parseInt(newTopping.calories.toFixed(0))
            pizza.price = parseFloat((pizza.price + newTopping.price).toFixed(1))
            break;
          case ("Large"):
            newTopping.calories = parseInt((newTopping.calories * 1.2).toFixed(0))
            newTopping.price = parseFloat((newTopping.price * 1.2).toFixed(1))
            pizza.calories += parseInt((newTopping.calories).toFixed(0))
            pizza.price = parseFloat((pizza.price + newTopping.price).toFixed(1))
            break;
          default:
            pizza.calories += newTopping.calories
            pizza.price = parseFloat((pizza.price + newTopping.price).toFixed(1))
        }
        toppings.push(newTopping)
      }
    }
    // console.log('pizzaPrice')
    // console.log(pizza.price)
    // console.log("toppings")
    // console.log(toppings)
    pizza.toppings = toppings
    // console.log(pizza)
    this.orderService.addItem(pizza)

    this.orderQty++
    this.showDialog = !this.showDialog

    this.customPizzaForm = this.fb.group({
      pizzaSize: [{}, Validators.required],
      // pizzaSize: this.fb.group(new Item())
      toppings: this.fb.array([])
    });
    this.addTopping()

    this.customPizzaForm
      .patchValue({
        pizzaSize: this.pizza.pizzaSizes[this.pizza.pizzaSizes.findIndex(i => i.size == "Medium")]
      })
  }

  toppingPrice(topping: any) {
    switch (this.customPizzaForm.value.pizzaSize.size) {
      case ("Small"):
        return topping.price * 0.8
      case ("Large"):
        return topping.price * 1.2
      default:
        return topping.price
    }
  }

  createPizza() {
    this.showDialog = !this.showDialog
  }

}
<file_sep>var mongoose = require('mongoose')
var Category = mongoose.model('Category')
var Location = mongoose.model('Location')

module.exports.getAdminInfo = function(req, res) {
  Category.find().exec((err, categories) => {
    if (err) {
      res.status(500).json(err)
    } else {
      Location.find().exec((err, locations) => {
        if (err) {
          res.status(500).json(err)
        } else {
          var items = []
          var locationNames = []
          for (var location of locations) {
            locationNames.push(location.city)
            for (var item of location.items) {
              if (items.findIndex(i => (i.name == item.name && i.size == item.size)) != -1) {
                items[items.findIndex(i => (i.name == item.name && i.size == item.size))].location = 'All'
              } else {
                item.location = location.city
                items.push(item)
              }
            }
          }
          // var reducedItems = items.sort(function(a, b) {
          //   var catA = a.category.name.toUpperCase()
          //   var catB = b.category.name.toUpperCase()
          //   if (catA < catB) {
          //     return -1;
          //   }
          //   if (catA > catB) {
          //     return 1;
          //   }
          //   return 0;
          // }).reduce(function(acc, item) {
          //   if (acc.length) {
          //     if (acc[acc.length - 1][0].name.category = item.category) {
          //       acc[acc.length-1].push(item)
          //     } else {
          //       acc.push([item])
          //     }
          //   } else {
          //     acc.push([item])
          //   }
          // }, [])
          var categoryNameHolder = []
          var categoryArr = []
          items.forEach(function(item) {
            categoryNameHolder[item.category.name] = categoryNameHolder[item.category.name] || {}
            var obj = categoryNameHolder[item.category.name]
            if (Object.keys(obj).length == 0)
              categoryArr.push(obj)
            obj.categoryName = item.category.name
            obj.items = obj.items || []
            obj.items.push(item)
          })
          // var groupedItems = items.reduce(function(arr, item) {
          //   arr.findIndex(i => (i.category.name == i.category.name))
          //   obj[item.category.name] = obj[item.category.name] || []
          //   obj[item.category.name].push(item)
          //   return obj;
          // }, [])
          // var result = Object.keys(groupedItems).map(function(key) {
          //   return groupedItems[key];
          // });
          locationNames.push('All')
          var categoriesAndGroupedItems = {
            locations: locationNames,
            categories: categories,
            items: categoryArr
          }
          // console.log(categoriesAndLocations)
          res.status(200).json(categoriesAndGroupedItems)
        }
      })
      //res.status(200).json(categories)
    }
  })
}
<file_sep>const mongoose = require('mongoose');

const Category = require('./category.js')
const CategorySchema = mongoose.model('Category').schema

const ItemSchema = mongoose.Schema({
  category: {
    type: CategorySchema,
    required: true
  },
  name: {
    type: String,
    required: true
  },
  size: {
    type: String
  },
  calories: {
    type: Number,
    required: true
  },
  price: {
    type: Number,
    required: true
  },
  location: String,
  toppings: [this]
});

module.exports = mongoose.model('Item', ItemSchema);
<file_sep>import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-locations',
  templateUrl: './locations.component.html',
  styleUrls: ['./locations.component.css']
})
export class LocationsComponent implements OnInit {
  coords: any;
  lat: number;
  lng: number;

  constructor() {
    this.coords = [
      [44.9695247, -93.1064527],
      [44.9744402, -93.2732667]
    ];
    this.lat = (this.coords[0][0] + this.coords[1][0]) / 2;
    this.lng = (this.coords[0][1] + this.coords[1][1]) / 2;
  }

  ngOnInit() {
  }

}
<file_sep>const mongoose = require('mongoose');

const OpeningTime = require('./openingTime.js')
const OpeningTimeSchema = mongoose.model('OpeningTime').schema

const Item = require('./item.js')
const ItemSchema = mongoose.model('Item').schema

const LocationSchema = mongoose.Schema({
  street: {
    type: String,
    required: true
  },
  city: {
    type: String,
    required: true
  },
  state: {
    type: String,
    required: true
  },
  zip: {
    type: String,
    required: true
  },
  slug: {
    type: String,
    required: true
  },
  coordinates: {
    type: [Number],
    required: true
  },
  hours: {
    type: [OpeningTimeSchema],
    // required: true
  },
  wifi: {
    type: Boolean,
    required: true
  },
  items: {
    type: [ItemSchema]
  }
});

// Note: Virtuals aren't able to be queried
// LocationSchema.virtual('slug').get(function() {
//     return this.city.replace(/\./, "").replace('\s', '-').toLowerCase();
// });

module.exports = mongoose.model('Location', LocationSchema);
<file_sep>import { Component, OnInit, Pipe, PipeTransform } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'
import {trigger, transition, style, animate, state} from '@angular/animations'
import { NgForm } from '@angular/forms'

import { AdminService } from '../services/admin.service'
import { AuthService } from '../services/auth.service'

@Pipe({ name: 'keys', pure: false })
export class KeysPipe implements PipeTransform {
  transform(value: any, args: any[] = null): any {
    return Object.keys(value)//.map(key => value[key]);
  }
}

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css'],
  animations: [
    trigger(
      'myAnimation',
      [
        transition(
          ':enter', [
            style({ transform: 'translateY(-100%)', opacity: 0 }),
            animate('500ms', style({ transform: 'translateY(0)', 'opacity': 1 }))
          ]
        ),
        transition(
          ':leave', [
            style({ transform: 'translateY(0)', 'opacity': 1 }),
            animate('500ms', style({ transform: 'translateY(-100%)', 'opacity': 0 }))
          ]
        )
      ]
    )
  ]
})
export class DashboardComponent implements OnInit {

  categories: Object[]
  items: any//Object[]
  locationNames: Object[]

  showCategory: boolean = false;
  showItem: boolean = false;
  catName: String;

  addCatSubmitted: boolean = false;
  addItemSubmitted: boolean = false;

  item = {
    name: "",
    category: "",
    price: "",
    location: "",
    calories: "",
    size: ""
  }

  constructor(
    private adminService: AdminService,
    private authService: AuthService,
    private router: Router
  ) { }

  ngOnInit() {
    this.authService.getInfo().subscribe(data => {
      this.categories = data.categories
      this.items = data.items
      this.locationNames = data.locations
      // console.log(this.items)
    }, err => {
      console.log(err)
      return false
    })
  }

  logout(value: boolean) {
    // this.authService.logout()
    this.router.navigate(['admin/login'])
  }

  showCat(show: boolean) {
    // if (this.showItem) {
    //   this.showItem = !this.showItem
    //   setTimeout(function() {
    //     this.showCategory = !this.showCategory
    //   }, 1000)
    // } else {
    //   this.showCategory = !this.showCategory
    // }
    this.showCategory = !this.showCategory
  }

  showIt(show: boolean) {
    // if (this.showCategory) {
    //   console.log('print')
    //   this.showCategory = !this.showCategory
    //   setTimeout(function() {
    //     this.showItem = !this.showItem
    //   }, 500)
    // } else {
    //   this.showItem = !this.showItem
    // }
    this.showItem = !this.showItem
  }

  addCategory(form: NgForm) {
    if (form.valid) {
      this.addCatSubmitted = false
      this.adminService.addCategory(this.catName).subscribe((data) => {
        this.categories.push(data);
        this.showCategory = !this.showCategory
      })
    } else {
      this.addCatSubmitted = true
    }
  }

  addItem(form: NgForm) {
    if (form.valid) {
      console.log(this.item)
      this.addItemSubmitted = false
      this.adminService.addItem(this.item).subscribe((data) => {
        if (data.success) {
          var addedToExistingCategory: boolean = false;
          this.items.forEach((definedItem) => {
            if (definedItem.categoryName == data.item.category.name) {
              data.item.location = this.item.location
              definedItem.items.push(data.item)
              addedToExistingCategory = true
            }
          })
          if (!addedToExistingCategory) {
            var obj = {}
            obj["categoryName"] = data.item.category.name
            obj["items"] = [data.item]
            this.items.push(obj)
          }
          this.item = {
            name: "",
            category: "",
            price: "",
            location: "",
            calories: "",
            size: ""
          }
          console.log(this.items)
        }
      })
    } else {
      this.addItemSubmitted = true
    }
  }
}
<file_sep>import { Component, OnInit, OnChanges, Input, SimpleChanges } from '@angular/core';
import { NgForm } from '@angular/forms'

import { AdminService } from '../services/admin.service'

@Component({
  selector: 'app-menu',
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit, OnChanges {
  @Input() items: any;
  @Input() categories: any;
  @Input() locations: any;

  selectedItem: any;
  selectedCategory: String;

  showDialog: boolean = false;
  showCategoryDialog: boolean = false;

  editProduct: any;
  editSubmitted: boolean = false;

  editCat: String;
  newCatName: String;
  editCatSubmitted: boolean = false;

  constructor(private adminService: AdminService) { }

  ngOnInit() {
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('changes')
    console.log(changes['items'])
    // if (changes['items']) {
    //   this.items = changes['items'];
    // }
    console.log('items')
    console.log(this.items)
    console.log('editProduct')
    console.log(this.editProduct)
  }

  // Delete category from dashboard
  deleteCategory() {
    this.adminService.deleteCategory(this.selectedCategory).subscribe((data) => {
      if (data.success) {
        // console.log(this.items.findIndex(i => (i.categoryName == this.selectedItem.category)))
        this.items.splice(this.items.findIndex(i => (i.categoryName == this.selectedCategory)), 1)
        this.selectedCategory = ""
      }
    })
    this.showCategoryDialog = !this.showCategoryDialog
  }

  editCategory(catName: String) {
    if (this.editCat == catName) {
      this.editCat = ""
      this.newCatName = ""
    } else {
      this.editCat = catName
      this.newCatName = catName
    }
  }

  editCategorySubmit(form: NgForm) {
    var obj = {}
    obj["oldCategoryName"] = this.editCat
    obj["newCategoryName"] = this.newCatName
    if (form.valid) {
      this.adminService.editCategory(obj).subscribe((data) => {
        if (data.success) {
          this.items[this.items.findIndex(i => (i.categoryName == this.editCat))]["categoryName"] = this.newCatName
          this.editCat = ""
          this.newCatName = ""
        }
      })
    } else {
      this.editCatSubmitted = true;
    }
  }

  deleteItem() {
    this.adminService.deleteItem(this.selectedItem).subscribe((data) => {
      if (data.success) {
        // console.log(this.items.findIndex(i => (i.categoryName == this.selectedItem.category)))
        var item = this.items[this.items.findIndex(i => (i.categoryName == this.selectedItem.category.name))]
        item.items.splice(item.items.findIndex(i => (i._id == this.selectedItem._id)), 1)
        this.selectedItem = {}
      }
    })
    this.showDialog = !this.showDialog
  }

  setSelected(product: Object) {
    this.selectedItem = product
    this.showDialog = !this.showDialog
  }

  setSelectedCategory(category: String) {
    this.selectedCategory = category
    this.showCategoryDialog = !this.showCategoryDialog
  }

  edit(product: any) {
    if (this.editProduct && this.editProduct._id == product._id) {
      this.editProduct = {}
    } else {
      this.editProduct = Object.assign({}, product)
      console.log(this.editProduct)
    }
  }

  editItem(form: NgForm) {
    if (form.valid) {
      this.adminService.editItem(this.editProduct).subscribe((data) => {
        if (data.success) {
          // console.log(this.items.findIndex(i => (i.categoryName == this.selectedItem.category)))
          var item = this.items[this.items.findIndex(i => (i.categoryName == this.editProduct.category.name))]
          item.items.splice(item.items.findIndex(i => (i._id == this.editProduct._id)), 1, this.editProduct)
          this.editProduct = {}
        }
      })
    } else {
      this.editSubmitted = true;
    }
  }
}
