diff --git a/Gemfile b/Gemfile
index 67e7c1d22a..155b940676 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,8 +3,6 @@ source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.5'
-# Use sqlite3 as the database for Active Record
-gem 'sqlite3'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
@@ -28,6 +26,8 @@ gem 'bcrypt', '~> 3.1.7'
gem 'bootstrap-sass', '~> 3.3.6'
+gem 'font-awesome-rails'
+
# Use Unicorn as the app server
# gem 'unicorn'
@@ -42,6 +42,10 @@ group :development, :test do
gem 'hirb'
end
+group :production do
+ gem 'pg'
+end
+
group :development do
# Access an IRB console on exception pages or by using <%= console %> in views
gem 'web-console', '~> 2.0'
@@ -49,8 +53,9 @@ group :development do
gem 'binding_of_caller'
gem 'pry-rails'
gem 'rails-erd'
+ # Use sqlite3 as the database for Active Record
+ gem 'sqlite3'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
end
-
diff --git a/Gemfile.lock b/Gemfile.lock
index 6ef6bcd274..d0ca7b6dec 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -67,6 +67,8 @@ GEM
docile (1.1.5)
erubis (2.7.0)
execjs (2.6.0)
+ font-awesome-rails (4.5.0.0)
+ railties (>= 3.2, < 5.0)
globalid (0.3.6)
activesupport (>= 4.1.0)
hirb (0.7.3)
@@ -90,6 +92,7 @@ GEM
multi_json (1.11.2)
nokogiri (1.6.7)
mini_portile2 (~> 2.0.0.rc2)
+ pg (0.18.4)
pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
@@ -199,9 +202,11 @@ DEPENDENCIES
bootstrap-sass (~> 3.3.6)
byebug
coffee-rails (~> 4.1.0)
+ font-awesome-rails
hirb
jbuilder (~> 2.0)
jquery-rails
+ pg
pry-rails
rails (= 4.2.5)
rails-erd
diff --git a/Procfile b/Procfile
new file mode 100644
index 0000000000..c3936b2237
--- /dev/null
+++ b/Procfile
@@ -0,0 +1 @@
+web: bundle exec rackup -p $PORT
diff --git a/README.md b/README.md
index cd2baba9d7..d344afba1a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
+TEST
Team Quartzy Trelly Board
https://trello.com/b/sBn5vXJY/quartzy
+Heroku Link:
+http://quartzy.herokuapp.com/
+
# bEtsy
[b]Etsy will be an online store where a wide variety of products can be listed and sold by any user. In this project we will focus on reinforcing the major components of Rails, Model Validation, as well as introducing some more complex logic such as user authentication.
diff --git a/README.rdoc b/README.rdoc
index dd4e97e22e..c8b858f49a 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,28 +1 @@
-== README
-
-This README would normally document whatever steps are necessary to get the
-application up and running.
-
-Things you may want to cover:
-
-* Ruby version
-
-* System dependencies
-
-* Configuration
-
-* Database creation
-
-* Database initialization
-
-* How to run the test suite
-
-* Services (job queues, cache servers, search engines, etc.)
-
-* Deployment instructions
-
-* ...
-
-
-Please feel free to use a different markup language if you do not plan to run
-rake doc:app.
+Find us on heroku: http://quartzy.herokuapp.com/
diff --git a/app/assets/images/quartzy.png b/app/assets/images/quartzy.png
new file mode 100644
index 0000000000..10f82f239b
Binary files /dev/null and b/app/assets/images/quartzy.png differ
diff --git a/app/assets/javascripts/categories.coffee b/app/assets/javascripts/categories.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/categories.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/categoryproducts.coffee b/app/assets/javascripts/categoryproducts.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/categoryproducts.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/orderitems.coffee b/app/assets/javascripts/orderitems.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/orderitems.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/orders.coffee b/app/assets/javascripts/orders.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/orders.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/products.coffee b/app/assets/javascripts/products.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/products.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/reviews.coffee b/app/assets/javascripts/reviews.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/reviews.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/sessions.coffee b/app/assets/javascripts/sessions.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/sessions.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/users.coffee b/app/assets/javascripts/users.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/users.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/welcome.coffee b/app/assets/javascripts/welcome.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/app/assets/javascripts/welcome.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index baccee21b4..205a5e36a7 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -1,2 +1,276 @@
@import "bootstrap-sprockets";
@import "bootstrap";
+@import "font-awesome";
+
+$teal: #159E98;
+$other-highlight: #218578;
+$teal-grey: #32A69B;
+$dark-teal: #238479;
+$rust: #C18763;
+$yellow: #D6D8A6;
+
+$light-contrast: $teal-grey;
+$flash: #75E8E1;
+$highlight: $teal;
+$neutral: $yellow;
+
+/*$body: 'IM Fell DW Pica';*/
+$body: 'Tinos';
+$header: 'Tinos';
+
+.each-merchant {
+ padding-bottom: 70px;
+}
+
+.merchant-name {
+ padding-bottom: 20px;
+ text-align: center;
+ max-width: 380px;
+}
+
+.centerBlock {
+ display: table;
+ margin: 0 auto;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ }
+
+.product-names {
+ text-align: center;
+}
+
+.emptycart {
+ text-align: center;
+}
+
+.flash {
+ background-color: $flash;
+ text-align: center;
+ font-size: 16px;
+ padding: 5px;
+ margin: 5px;
+}
+
+.front-pg {
+ text-align: center;
+ font-family: $body, serif;
+ font-size: 64px;
+}
+
+.front-pg-link {
+ color: black;
+}
+
+.header-box {
+ padding: 20px;
+ background-color: $light-contrast;
+ margin-bottom: 10px;
+ margin-top: 10px;
+}
+
+.button-box {
+ padding-bottom: 25px;
+}
+
+.col-xs-12 {
+ text-align: center;
+}
+
+body {
+ font-family: $body, serif;
+}
+
+.front-pg2 {
+ font-family: $header, serif;
+ text-align: center;
+ font-size: 20px;
+}
+
+.jumbotron {
+ background-image: image-url("quartzy.png");
+ background-size: cover;
+
+}
+
+.home_img {
+ display: block;
+ margin-left: auto;
+ margin-right: auto
+}
+
+.cntr {
+ text-align: center;
+}
+
+h1 {
+ font-family: $header, serif;
+ text-align: center;
+ margin-bottom: 20px;
+}
+
+h2 {
+ font-family: $header, serif;
+}
+
+h3 {
+ font-family: $header, serif;
+}
+
+h4 {
+ font-family: $header, serif;
+}
+
+h5 {
+ font-family: $header, serif;
+}
+
+.cntr-header {
+ text-align: center;
+}
+
+.navbar {
+ font-family: $header;
+ border:1px solid #ccc;
+ border-width:1px 0;
+ margin:0;
+ padding:15px;
+ text-align:center;
+ word-spacing: 10px;
+ text-transform: uppercase;
+}
+
+.mark_shipped {
+
+}
+
+.cat-navbar {
+
+}
+
+
+.category-box{
+ margin: auto;
+ padding: 7px;
+ border: 1px solid $highlight;
+}
+
+.product-page-img {
+ max-width: 250px;
+ max-height: 200px;
+ min-height: 200px;
+}
+
+.cart-img {
+ max-width: 150px;
+ height: auto;
+ max-height: 200px;
+}
+
+a {
+ color: $highlight;
+ text-decoration: none;
+}
+
+a:hover {
+ color: $other-highlight;
+ text-decoration: none;
+}
+
+
+.table-master {
+ font-family: $body;
+ font-size: 17px;
+
+}
+
+.form-control {
+ width: 50%;
+ font-family: $body;
+ font-size: 17px;
+}
+
+.form-group {
+ margin-left: 50px;
+}
+
+.col-sm-4 {
+ margin: auto;
+ margin-bottom: 10px;
+}
+
+.col-sm-6 {
+ margin-top: 20px;
+ text-align: center;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.spacer {
+ margin: 10px;
+}
+
+.rating {
+ list-style-image: none;
+}
+
+.fa {
+ color: #FFD700;
+}
+
+.btn {
+ font-size: 14px;
+ font-family: $header;
+ border: none;
+}
+
+.center {
+ margin: auto;
+ display: block;
+}
+
+.btn-success {
+ background-color: $dark-teal;
+}
+
+.btn-success:hover {
+ background-color: $flash;
+}
+
+.btn-success:active {
+ background-color: $flash;
+}
+
+.btn-danger {
+ background-color: $rust;
+ border: none;
+}
+
+.btn-default {
+ background-color: $other-highlight;
+ border: none;
+}
+
+.btn-default:hover {
+ background-color: $rust;
+}
+
+.btn-default:active {
+ background-color: $rust;
+}
+
+.btn-info {
+ background-color: $teal;
+}
+
+.rating-btn {
+ margin-top: 7px;
+}
+
+.category_list {
+ padding: 0;
+ list-style-type: none;
+}
+
+.dash-list {
+ list-style-type: none;
+}
diff --git a/app/assets/stylesheets/categories.scss b/app/assets/stylesheets/categories.scss
new file mode 100644
index 0000000000..ef1657f8c9
--- /dev/null
+++ b/app/assets/stylesheets/categories.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the categories controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/categoryproducts.scss b/app/assets/stylesheets/categoryproducts.scss
new file mode 100644
index 0000000000..a89a1cd344
--- /dev/null
+++ b/app/assets/stylesheets/categoryproducts.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the categoryproducts controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/orderitems.scss b/app/assets/stylesheets/orderitems.scss
new file mode 100644
index 0000000000..3ae284dd37
--- /dev/null
+++ b/app/assets/stylesheets/orderitems.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the orderitems controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/orders.scss b/app/assets/stylesheets/orders.scss
new file mode 100644
index 0000000000..3b0428a94e
--- /dev/null
+++ b/app/assets/stylesheets/orders.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the orders controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/products.scss b/app/assets/stylesheets/products.scss
new file mode 100644
index 0000000000..89e2e8db07
--- /dev/null
+++ b/app/assets/stylesheets/products.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the products controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/reviews.scss b/app/assets/stylesheets/reviews.scss
new file mode 100644
index 0000000000..6ea2454d26
--- /dev/null
+++ b/app/assets/stylesheets/reviews.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the reviews controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss
new file mode 100644
index 0000000000..7bef9cf826
--- /dev/null
+++ b/app/assets/stylesheets/sessions.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the sessions controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss
new file mode 100644
index 0000000000..1efc835ccd
--- /dev/null
+++ b/app/assets/stylesheets/users.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the users controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/welcome.scss b/app/assets/stylesheets/welcome.scss
new file mode 100644
index 0000000000..77ce11a740
--- /dev/null
+++ b/app/assets/stylesheets/welcome.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the welcome controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d83690e1b9..6ecd527aa9 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -2,4 +2,59 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
+ before_action :current_user
+ before_action :current_order
+ before_action :items_in_cart
+
+
+ def current_order
+ if session[:order_id] && !!Order.exists?(session[:order_id])
+ @current_order ||= Order.find(session[:order_id])
+ end
+ end
+
+ def current_user
+ if session[:user_id] && !!User.exists?(session[:user_id])
+ @current_user ||= User.find(session[:user_id])
+ end
+ end
+
+ def items_in_cart
+ if current_order
+ sum = []
+ Order.find(session[:order_id]).orderitems.each do |oi|
+ sum.push(oi.quantity)
+ end
+ @cart_num = sum.inject(0) {|r, e| r + e }
+ else
+ @cart_num = 0
+ end
+ end
+
+ def require_login
+ if current_user.nil?
+ flash[:error] = "You must be logged in to view this section"
+ redirect_to new_session_path
+ end
+ end
+
+ def navbar_categories
+ @navbar_cat = Category.where(user_id: nil)
+ end
+
+ # checks that the user/:id, the :id is equal to current user
+ def only_current_user
+ if !@current_user || @current_user.id != params[:id].to_i
+ flash[:error] = "You are not authorized to view that section"
+ redirect_to root_path
+ end
+ end
+
+ def current_user_owns_product
+ if current_user.id != Product.find(params[:id]).user_id
+ flash[:error] = "You are not authorized to view this section"
+ redirect_to :back
+ end
+ end
+
end
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
new file mode 100644
index 0000000000..41c67528af
--- /dev/null
+++ b/app/controllers/categories_controller.rb
@@ -0,0 +1,36 @@
+class CategoriesController < ApplicationController
+before_action :require_login, only: [:new, :create, :edit, :update]
+before_action :navbar_categories, only: [:index, :show]
+
+ def index
+ @cat = Category.all
+ end
+ def show
+ @category = Category.find(params[:id])
+ end
+ def new
+ @category = Category.new
+ @action = "create"
+ end
+ def create
+ @category = Category.new(category_params)
+ @category.user_id = session[:user_id]
+ if @category.save
+ redirect_to user_products_dash_path(@category.user_id)
+ else
+ render :new
+ end
+ end
+ def edit
+
+ end
+ def update
+
+ end
+
+ private
+
+ def category_params
+ params.require(:category).permit(:name, :description)
+ end
+end
diff --git a/app/controllers/orderitems_controller.rb b/app/controllers/orderitems_controller.rb
new file mode 100644
index 0000000000..d6e2b3ec97
--- /dev/null
+++ b/app/controllers/orderitems_controller.rb
@@ -0,0 +1,48 @@
+class OrderitemsController < ApplicationController
+
+ #this method hooks up to button in cart to delete an orderitem, OR you might end up redirected here if you update at orderitem quantity to zero.
+ #if you are deleting the last item in the cart, the whole order gets deleted
+ def destroy
+ @orderitem = Orderitem.find(params[:id])
+ @order = @orderitem.order
+ total_items = @order.orderitems
+ if total_items.count == 1
+ @order = Order.find(session[:order_id])
+ @order.destroy!
+ session[:order_id] = nil
+ redirect_to root_path
+ else
+ @orderitem.destroy
+ redirect_to cart_path
+ end
+ end
+
+
+ #happens in cart
+ #the only thing update can do is change the quantity
+ #a form will need to be sent for a particular orderitem on the cart page
+ #let them type in an integer, and if it's zero delete it
+ def update
+ @orderitem = Orderitem.find(params[:id])
+ if orderitem_params[:orderitem][:quantity] == 0
+ redirect_to action: :destroy
+ else
+ @orderitem.update(quantity: orderitem_params[:orderitem][:quantity])
+ redirect_to cart_path
+ end
+ end
+
+ def ship
+ @orderitem = Orderitem.find(params[:id])
+ @orderitem.item_shipped
+ @orderitem.order.mark_shipped
+ redirect_to :back
+ end
+
+ private
+
+ def orderitem_params
+ params.permit(orderitem:[:quantity, :status])
+ end
+
+end
diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb
new file mode 100644
index 0000000000..3eb79af07b
--- /dev/null
+++ b/app/controllers/orders_controller.rb
@@ -0,0 +1,93 @@
+class OrdersController < ApplicationController
+ #look up callbacks
+ #also, in order to make filter work, buttons to do stuff to orders need to send an id into params. (see guest_authorize method, which uses id)
+ #this is to help make it so only the person who started an order can do stuff to it.
+ before_action :guest_authorize, :only =>[:clear_cart, :cancel_as_guest, :pay]
+ before_action :navbar_categories, only: [:cart]
+
+
+ #the order from the perspective of the merchant
+ def show
+ @order = Order.find(params[:id])
+ end
+
+ #the order from the perspective of the guest
+ #stuff needed to view the cart page
+ def cart
+ if !session[:order_id]
+ @cart_status = "empty"
+ end
+ end
+
+ #before you pay, clearing cart destroys order and orderitems.
+ #after you've paid, you instead cancel the order, and the order sticks around in the database
+ def clear_cart
+ @order = Order.find(session[:order_id])
+ @order.destroy!
+ session[:order_id] = nil
+ redirect_to root_path
+ end
+
+ def checkout
+ @order = Order.find(session[:order_id])
+ end
+
+ def confirm
+ @order = Order.find(session[:order_id])
+ end
+
+ def cancel_as_guest
+ @order = Order.find(session[:order_id])
+ @order.update_attribute(:status, "cancelled")
+ session[:order_id] = nil
+ @cart_status = "empty"
+ redirect_to root_path
+ end
+
+ def finalize
+ @order = Order.find(session[:order_id])
+ @order.decrement_products_stock
+ session[:order_id] = nil
+ @cart_status = "empty"
+ flash[:notice] = "Thank you for your order!"
+ redirect_to root_path
+ end
+
+ def pay
+ @order = Order.find(session[:order_id])
+ @order.update(order_params[:order])
+ @order.placed_at = Time.now
+ @order.status = "paid"
+ @order.save!
+ redirect_to order_confirm_path(@order.id)
+ end
+
+
+ #edit and update... are for the guest to do on the cart page?
+ #needs more thought
+ #we have lots of specific ways we are updating, as it stands right now
+ def edit
+ @order = Order.find(params[:id])
+ end
+
+ def update
+ id = params[:id]
+ order = Order.find(id)
+ order.update_attributes(order_params[:order])
+ redirect_to order_path(params[:id])
+ end
+
+ private
+
+ def order_params
+ params.permit(order:[:status, :cc_name, :email_address, :mailing_address, :cc_number, :cc_exp, :cc_cvv, :zip, :placed_at])
+ end
+
+ #views will need to make sure they send in an id to use here
+ def guest_authorize
+ unless session[:order_id]
+ redirect_to root_path
+ end
+ end
+
+end
diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb
new file mode 100644
index 0000000000..5980897899
--- /dev/null
+++ b/app/controllers/products_controller.rb
@@ -0,0 +1,122 @@
+
+
+class ProductsController < ApplicationController
+ include ApplicationHelper
+ before_action :navbar_categories, only: [:index]
+ before_action :current_user_owns_product, only: [:update, :delete, :retire]
+ before_action :find_product, only: [:retire, :buy, :show, :destroy]
+
+
+
+ def buy
+ #if there is not yet an order_id in session, make it now
+ #this is totally independent of being logged in.
+ #then if the id is nil, make an order and put it's id in the session hash
+ #if there already is an order in the session, add the product to it.
+ @current_order = current_order
+ if !@current_order
+ session[:order_id] = []
+ order = Order.pending(@product)
+ session[:order_id] = order.id
+ flash[:notice] = "Added #{@product.name} to cart."
+ else
+ #logic for whether or not one is in cart already
+ if @current_order.orderitems.where(product_id: @product.id) != []
+ #product is already in order
+ @orderitem = @current_order.orderitems.where(product_id: @product.id).first
+ @orderitem.update_attribute(:quantity, @orderitem.quantity + 1 )
+ flash[:notice] = "Added another #{@orderitem.product.name} to cart."
+ else
+ #product is not already in order
+ @orderitem = Orderitem.create!(quantity: 1, order_id: @current_order.id, product_id: @product.id)
+ flash[:notice] = "Added #{@orderitem.product.name} to cart."
+ end
+ end
+ #in any case, want to stay on same page after clicking button
+ redirect_to request.referrer
+ end
+
+
+ def index
+ @products = Product.all
+ end
+
+ def new
+ @product = Product.new
+ # @categories = @product.categories
+ @action = "create"
+ @all_categories = Category.all
+ end
+
+ def create
+ @product = Product.new(product_params)
+ @product.price = (params[:product][:price].to_f * 100).to_i
+ if @product.save
+ redirect_to product_path(@product)
+ else
+ flash[:error] = "Why don't you double check those product specs, eh?"
+ render :new
+ end
+ end
+
+ def show
+ @title = "#{@product.name} Info"
+ @stars = @product.avg_rating
+ @reviews = @product.reviews
+ @review = Review.new
+ end
+
+ def review
+ @product = Product.find(params[:product_id])
+ @review = Review.create(review_params)
+ redirect_to product_path(@product)
+ end
+
+ def edit
+ id = params[:id]
+ @product = Product.find(id)
+ @categories = @product.categories
+ @all_categories = Category.all
+ @action = "update"
+ @title = "Edit #{@product.name}"
+ end
+
+ def update
+ @product = Product.update(params[:id], product_params)
+ # @product.price = (params[:product][:price].to_f * 100).to_i
+ @product.category_ids = params[:product][:category_ids]
+ if @product.save
+ redirect_to product_path(@product)
+ else
+ render :edit
+ end
+ end
+
+ def retire
+ id = params[:id]
+ @product = Product.find(id)
+ @product.retire_toggle
+ @product.save
+ redirect_to request.referrer
+ end
+
+ def destroy
+ @product.destroy
+ flash[:notice] = "You've deleted #{@product.name}"
+ redirect_to request.referrer
+ end
+
+ private
+
+ def find_product
+ @product = Product.find(params[:id])
+ end
+
+ def product_params
+ params.require(:product).permit(:user_id, :retired, :category_ids, :name, :price, :description, :photo_url, :stock)
+ end
+
+ def review_params
+ params.require(:review).permit(:rating, :body).merge(product_id: params[:product_id])
+ end
+end
diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb
new file mode 100644
index 0000000000..b3d77cc1c3
--- /dev/null
+++ b/app/controllers/reviews_controller.rb
@@ -0,0 +1,2 @@
+class ReviewsController < ApplicationController
+end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
new file mode 100644
index 0000000000..0966e6540f
--- /dev/null
+++ b/app/controllers/sessions_controller.rb
@@ -0,0 +1,42 @@
+class SessionsController < ApplicationController
+ def new
+ end
+
+ def create
+ data = params[:session_data]
+ @user = User.find_by_email_address(data[:email_address])
+
+ if !@user.nil?
+ if @user.authenticate(data[:password])
+ session[:user_id] = @user.id
+ flash[:notice] = "Welcome, #{@user.username}!"
+ redirect_to root_path
+ else
+ flash.now[:error] = "Your email was not found or password did not match. Please try again."
+ render :new
+ end
+ else
+ flash.now[:error] = "Your email was not found or password did not match. Please try again or sign up to create a new user."
+ render :new
+ end
+ end
+
+ def destroy
+ session[:user_id] = nil if session[:user_id]
+ current_order
+ if !!@current_order
+ @current_order = Order.find(session[:order_id])
+ @current_order.destroy! #destroys whatever order is in the session so long as it was only pending, which is done in an order validation
+ session[:order_id] = nil
+ end
+ flash[:notice] = "You have been logged out."
+ redirect_to root_path
+ end
+
+ private
+
+ def session_params
+ params.require(:user).permit(:username, :email_address, :password, :password_confirmation)
+ end
+
+end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
new file mode 100644
index 0000000000..de56fdc06c
--- /dev/null
+++ b/app/controllers/users_controller.rb
@@ -0,0 +1,52 @@
+class UsersController < ApplicationController
+before_action :navbar_categories, only: [:index]
+before_action :find_user, only: [:show, :dash, :product_dash, :order_dash]
+before_action :only_current_user, only: [:dash, :order_dash]
+
+ def index
+ @users = User.all
+ @cat = Category.all
+ end
+
+ def new
+ @user = User.new
+ end
+
+ def create
+ @user = User.new(strong_params)
+ if @user.save
+ session[:user_id] = @user.id
+ flash[:notice] = "Welcome to Quartzy! You are now logged in."
+ redirect_to root_path
+ else
+ render :new
+ end
+ end
+
+ def show
+ end
+
+ def dash
+ end
+
+ def product_dash
+ end
+
+ def order_dash
+ @user = @current_user
+ session[:rev_status] = params[:rev_status] if !params[:rev_status].nil?
+ session[:order_status] = params[:order_status] if !params[:order_status].nil?
+ session[:order_status] = 'all' if session[:order_status].nil?
+ end
+
+ private
+
+ def strong_params
+ params.require(:user).permit(:username, :email_address, :password, :password_confirmation)
+ end
+
+ def find_user
+ @user = User.find(params[:id])
+ end
+
+end
diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb
new file mode 100644
index 0000000000..40c6fa319e
--- /dev/null
+++ b/app/controllers/welcome_controller.rb
@@ -0,0 +1,24 @@
+class WelcomeController < ApplicationController
+ before_action :navbar_categories, only: [:index]
+
+ def index
+ x = rand(1..User.all.length)
+ until User.find(x).products.length >= 2
+ x = rand(1..User.all.length)
+ end
+ y = rand(1..User.all.length)
+ until y != x && User.find(y).products.length >= 2
+ y = rand(1..User.all.length)
+ end
+ @top_users = [User.find(x), User.find(y)]
+ m = rand(1..Category.all.length)
+ until Category.find(m).products.length >= 2
+ m = rand(1..Category.all.length)
+ end
+ n = rand(1..Category.all.length)
+ until n != m && Category.find(n).products.length >= 2
+ n = rand(1..Category.all.length)
+ end
+ @top_categories = [Category.find(m), Category.find(n)]
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index de6be7945c..95fdc53368 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,2 +1,9 @@
+require "action_view/helpers/number_helper"
+
module ApplicationHelper
+ include ActionView::Helpers::NumberHelper
+ def nice_price(price)
+ price = price / 100.0
+ number_to_currency(price)
+ end
end
diff --git a/app/helpers/categories_helper.rb b/app/helpers/categories_helper.rb
new file mode 100644
index 0000000000..e06f31554c
--- /dev/null
+++ b/app/helpers/categories_helper.rb
@@ -0,0 +1,2 @@
+module CategoriesHelper
+end
diff --git a/app/helpers/categoryproducts_helper.rb b/app/helpers/categoryproducts_helper.rb
new file mode 100644
index 0000000000..c7e72f5504
--- /dev/null
+++ b/app/helpers/categoryproducts_helper.rb
@@ -0,0 +1,2 @@
+module CategoryproductsHelper
+end
diff --git a/app/helpers/orderitems_helper.rb b/app/helpers/orderitems_helper.rb
new file mode 100644
index 0000000000..c74e407d1c
--- /dev/null
+++ b/app/helpers/orderitems_helper.rb
@@ -0,0 +1,2 @@
+module OrderitemsHelper
+end
diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb
new file mode 100644
index 0000000000..443227fd48
--- /dev/null
+++ b/app/helpers/orders_helper.rb
@@ -0,0 +1,2 @@
+module OrdersHelper
+end
diff --git a/app/helpers/products_helper.rb b/app/helpers/products_helper.rb
new file mode 100644
index 0000000000..ab5c42b325
--- /dev/null
+++ b/app/helpers/products_helper.rb
@@ -0,0 +1,2 @@
+module ProductsHelper
+end
diff --git a/app/helpers/reviews_helper.rb b/app/helpers/reviews_helper.rb
new file mode 100644
index 0000000000..682b7b1abc
--- /dev/null
+++ b/app/helpers/reviews_helper.rb
@@ -0,0 +1,2 @@
+module ReviewsHelper
+end
diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb
new file mode 100644
index 0000000000..309f8b2eb3
--- /dev/null
+++ b/app/helpers/sessions_helper.rb
@@ -0,0 +1,2 @@
+module SessionsHelper
+end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
new file mode 100644
index 0000000000..2310a240d7
--- /dev/null
+++ b/app/helpers/users_helper.rb
@@ -0,0 +1,2 @@
+module UsersHelper
+end
diff --git a/app/helpers/welcome_helper.rb b/app/helpers/welcome_helper.rb
new file mode 100644
index 0000000000..eeead45fc9
--- /dev/null
+++ b/app/helpers/welcome_helper.rb
@@ -0,0 +1,2 @@
+module WelcomeHelper
+end
diff --git a/app/models/categories_product.rb b/app/models/categories_product.rb
new file mode 100644
index 0000000000..6f63ba6762
--- /dev/null
+++ b/app/models/categories_product.rb
@@ -0,0 +1,4 @@
+class CategoriesProduct < ActiveRecord::Base
+ belongs_to :category
+ belongs_to :product
+end
diff --git a/app/models/category.rb b/app/models/category.rb
new file mode 100644
index 0000000000..d5e497e0e3
--- /dev/null
+++ b/app/models/category.rb
@@ -0,0 +1,14 @@
+class Category < ActiveRecord::Base
+ has_many :categories_products, :dependent => :destroy
+ has_many :products, through: :categories_products
+ belongs_to :user
+
+ validates :name, presence: true
+ validates :name, uniqueness: true
+
+ def top(x)
+ products = self.products.where(retired: false).sort_by{|pro| pro.avg_rating}
+ return products[0..x-1]
+ end
+
+end
diff --git a/app/models/order.rb b/app/models/order.rb
new file mode 100644
index 0000000000..2c91fd1ee5
--- /dev/null
+++ b/app/models/order.rb
@@ -0,0 +1,77 @@
+class Order < ActiveRecord::Base
+ belongs_to :user
+ has_many :orderitems, :dependent => :destroy
+ has_many :products, through: :orderitems
+
+ validate :has_orderitem, on: :create
+ validate :customer_destroys_only_pending, on: :destroy
+ validates :status, presence: true
+ validates :status, inclusion: { in: %w(pending) }, on: :create
+ validates :status, inclusion: { in: %w(paid) }, on: :pay
+ validates :status, inclusion: { in: %w(complete) }, on: :ship
+ validates :status, inclusion: { in: %w(cancelled) }, on: :cancel
+ validates :cc_name, presence: true, on: :pay
+ validates :email_address, presence: true, on: :pay
+ validates :mailing_address, presence: true, on: :pay
+ validates :cc_number, presence: true, on: :pay
+ validates :zip, presence: true, on: :pay
+ validates :cc_exp, presence: true, on: :pay
+ validates :cc_cvv, presence: true, on: :pay
+
+ def self.pending(first_product)
+ Order.transaction do
+ order = Order.new(status: 'pending')
+ order.orderitems << Orderitem.create!(quantity: 1, order_id: order.id, product_id: first_product.id)
+ order.save!
+ return order
+ end
+ end
+
+ def decrement_products_stock
+ Order.transaction do
+ self.orderitems.each do |orderitem|
+ product = orderitem.product
+ product.stock -= orderitem.quantity
+ product.save!
+ end
+ end
+ end
+
+ def total
+ sales = []
+ self.orderitems.each do |orderitem|
+ sales.push(orderitem.quantity * orderitem.product.price)
+ end
+ return sales.inject(0) {|r, e| r + e }
+ end
+
+ def total_by_user(user_id)
+ sales = []
+ self.products.each do |product|
+ sales.push(product.price) if product.user_id == user_id
+ end
+ return sales.inject(0) {|r, e| r + e }
+ end
+
+ def mark_shipped
+ c = 0
+ self.orderitems.each do |orderitem|
+ c += 1 if orderitem.status == 'shipped'
+ end
+ self.status = 'completed' if c == self.orderitems.length
+ self.save
+ return self
+ end
+ #returns the last four numbers of the cc
+ def last_four
+ return self.cc_number[-4..-1]
+ end
+
+ def customer_destroys_only_pending
+ !session[:user_id] && self.status = "pending"
+ end
+
+ def has_orderitem
+ !!self.orderitems
+ end
+end
diff --git a/app/models/orderitem.rb b/app/models/orderitem.rb
new file mode 100644
index 0000000000..fb0e237a91
--- /dev/null
+++ b/app/models/orderitem.rb
@@ -0,0 +1,14 @@
+class Orderitem < ActiveRecord::Base
+ belongs_to :order
+ belongs_to :product
+ validates :quantity, presence: true
+
+
+
+ # decerements inventory of product
+ def item_shipped
+ self.status = 'shipped'
+ self.save
+ end
+
+end
diff --git a/app/models/product.rb b/app/models/product.rb
new file mode 100644
index 0000000000..15f9d94516
--- /dev/null
+++ b/app/models/product.rb
@@ -0,0 +1,59 @@
+class Product < ActiveRecord::Base
+ belongs_to :user
+ has_many :reviews, :dependent => :destroy
+ has_many :orderitems
+ has_many :orders, through: :orderitems
+ has_many :categories_products #, :dependent => :destroy
+ has_many :categories, through: :categories_products
+
+ validates :name, presence: true
+ validates :name, uniqueness: true
+ validates :price, presence: true
+ validates_numericality_of :price, :greater_than => 0
+ validates_numericality_of :stock, :greater_than_or_equal_to => 0
+ # validates :retired, inclusion: { in: %w(false) }, on: :create
+ validate :belongs_to_user?
+
+ # Takes an array of products and returns the x number of products in order of which sold the most often
+ def self.top_selling(product_array, x)
+ sales_hash = {}
+ product_array.each do |product|
+ revenue = product.orderitems.length * product.price
+ sales_hash[product] = revenue
+ end
+ top = sales_hash.sort_by{|k, v| v}
+ top_array = top[0..x-1].flatten.reject!{|item| item.class == Fixnum}
+ return top_array
+ end
+
+ # returns the average rating for all ratings for a given product
+ def avg_rating
+ total = 0
+ self.reviews.each do |r|
+ total += r.rating
+ end
+ if self.reviews.length > 0
+ avg = total / self.reviews.length
+ else
+ avg = 0
+ end
+ return avg
+ end
+
+ def belongs_to_user?
+ !!self.user
+ end
+
+ def out_of_stock?
+ self.stock == 0
+ end
+
+ def retire_toggle
+ if self.retired
+ self.retired = false
+ elsif !self.retired
+ self.retired = true
+ end
+ end
+
+end
diff --git a/app/models/review.rb b/app/models/review.rb
new file mode 100644
index 0000000000..6eb6e4c822
--- /dev/null
+++ b/app/models/review.rb
@@ -0,0 +1,7 @@
+class Review < ActiveRecord::Base
+ belongs_to :product
+
+ validates :rating, presence: true
+ validates :rating, numericality: { only_integer: true }
+ validates :rating, :inclusion => 1..5
+end
diff --git a/app/models/user.rb b/app/models/user.rb
new file mode 100644
index 0000000000..bcd4e7a465
--- /dev/null
+++ b/app/models/user.rb
@@ -0,0 +1,87 @@
+class User < ActiveRecord::Base
+ has_many :products, :dependent => :destroy
+ has_many :categories
+
+ validates :username, presence: true
+ validates :username, uniqueness: true
+
+ validates :email_address, presence: true
+ validates :email_address, uniqueness: true
+
+ validates_confirmation_of :password, :message => "Passwords should match"
+ has_secure_password
+
+ # returns the top x number of products the user sells based on their average rating
+ def top(x)
+ products = self.products.where(retired: false).sort_by{|pro| pro.avg_rating}
+ return products[0..x-1]
+ end
+
+ # returns all the orders that have any products in them belonging to the user
+ def orders
+ orders = []
+ self.products.each do |product|
+ product.orders.each do |order|
+ orders.push(order) if !orders.include?(order)
+ end
+ end
+ return orders
+ end
+
+ def orderitems
+ orderitems = []
+ self.products.each do |product|
+ product.orderitems.each do |oi|
+ orderitems.push(oi)
+ end
+ end
+ return orderitems
+ end
+
+ # returns orderitems for the user that are in a given order
+ def orderitems_by_order(order)
+ orderitems = []
+ self.products.each do |product|
+ product.orderitems.each do |oi|
+ orderitems.push(oi) if oi.order == order
+ end
+ end
+ return orderitems
+ end
+
+ # returns all the orders a user is associated with, based on the order status
+ def orders_by_status(status)
+ orders = []
+ self.products.each do |product|
+ product.orders.each do |order|
+ orders.push(order) if order.status == "#{status}"
+ end
+ end
+ return orders
+ end
+
+ # returns total revenue for a user
+ def revenue
+ rev = []
+ self.products.each do |product|
+ product.orderitems.each do |orderitem|
+ rev.push(orderitem.product.price * orderitem.quantity)
+ end
+ end
+ total = rev.inject(0) {|r, e| r + e}
+ return total
+ end
+
+ # returns revenue for user by order status
+ def rev_by_status(status)
+ rev = []
+ self.products.each do |product|
+ product.orderitems.each do |orderitem|
+ rev.push(orderitem.product.price * orderitem.quantity) if orderitem.order.status == "#{status}"
+ end
+ end
+ total = rev.inject{|r, e| r + e}
+ return total
+ end
+
+end
diff --git a/app/views/application/_cat_navbar.html.erb b/app/views/application/_cat_navbar.html.erb
new file mode 100644
index 0000000000..5425badf37
--- /dev/null
+++ b/app/views/application/_cat_navbar.html.erb
@@ -0,0 +1,5 @@
+
+ <% @navbar_cat.each do |cat| %>
+ <%= link_to "#{cat.name}", category_path(cat) %> |
+ <% end %>
+
diff --git a/app/views/application/_navbar.html.erb b/app/views/application/_navbar.html.erb
new file mode 100644
index 0000000000..2fbee45049
--- /dev/null
+++ b/app/views/application/_navbar.html.erb
@@ -0,0 +1,14 @@
+
+ <% if @user.rev_by_status(session[:rev_status]).nil? %>
+ You don't have any <%= session[:rev_status]%> orders.
+ <% else %>
+ Revenue for <%= session[:rev_status]%> orders is: <%= nice_price(@user.rev_by_status(session[:rev_status])) %>
+ <% end %>
+
+ <% elsif session[:rev_status].nil? %>
+
+ select a status for which you want to see total revenue
+